Workspace¶
Purpose¶
The workspace is the foundation of a HarnessAgent: persona, long-term memory, domain knowledge, subagent declarations, session history, and skill definitions all land here as a directory structure + Markdown — no longer scattered in code.
Before every reasoning turn, a few key workspace files are automatically injected into the system prompt. Memory and session output from the running agent is also written back here along well-defined paths.
Trigger Points¶
When |
Action |
|---|---|
|
|
Before every |
|
Compaction / end of call |
|
Directory Layout¶
workspace/ ← default: .agentscope/workspace
├── AGENTS.md ← persona / behavior guidelines (full text injected each turn)
├── MEMORY.md ← curated long-term memory (injected each turn, token-budgeted)
├── knowledge/
│ ├── KNOWLEDGE.md ← domain knowledge entry point
│ └── * ← other reference files, opened on demand via read_file
├── memory/
│ ├── YYYY-MM-DD.md ← daily fact log (append-only, written by MemoryFlushManager)
│ └── .consolidation_state ← MemoryConsolidator internal state
├── skills/<skill-name>/SKILL.md ← custom skills
├── subagents/<id>.md ← subagent declarations (filename = agent_id, auto-discovered)
└── agents/<agentId>/
├── workspace/ ← runtime root for isolated subagents (auto-created when no workspace.path)
└── sessions/
├── sessions.json ← session index (id / summary / updatedAt)
├── <sessionId>.jsonl ← LLM-visible compacted context
└── <sessionId>.log.jsonl ← full conversation log (append-only)
The three-layer model for subagents (declaration / definition / runtime) is detailed in Subagent.
Key Logic¶
Two-Layer Read / Write¶
WorkspaceManager is a stateless accessor; all reads and writes follow the same contract:
graph LR
Caller[Hook / Tool] -->|read| WM[WorkspaceManager]
WM -->|read first| FS[AbstractFilesystem<br/>multi-tenant namespace transparent]
FS -- hit non-empty --> WM
FS -- empty --> LD[Local disk<br/>workspace/...]
LD --> WM
Caller -->|write| WM
WM -->|appendUtf8 / uploadFiles| FS2[AbstractFilesystem]
WM -. filesystem absent .-> LD2[local disk fallback]
Key points:
Read path:
AbstractFilesystemfirst → local disk fallback, making multi-tenant scenarios transparent to callersWrite path: defaults to
AbstractFilesystem; falls back to local disk when not configuredList operations (
listKnowledgeFiles/listMemoryFilePaths/listSessionLogFiles) take the union of both layers and deduplicate, avoiding missing files
System Prompt Injection Content¶
WorkspaceContextHook (priority 900) assembles a fixed-structure text segment on PreReasoningEvent and merges it into the first SYSTEM message:
Section |
Source |
Token Budget |
|---|---|---|
|
Template-generated (date, OS, workspace path, |
Unlimited |
|
Built-in template |
Unlimited |
|
— |
— |
↳ |
|
Full text |
↳ |
|
Capped by |
↳ |
|
Full text + path listing |
↳ |
Each |
Full text |
maxContextTokens defaults to 8000 (estimated as chars/4). When MEMORY.md estimated size exceeds the “remaining budget”, it is character-truncated and appended with ... (memory truncated — use memory_search for older entries) ..., prompting the agent to fall back to memory_search.
Key APIs¶
WorkspaceManager wm = new WorkspaceManager(workspace, abstractFilesystem);
wm.readAgentsMd(); // two-layer read
wm.readMemoryMd();
wm.readKnowledgeMd(); // note: reads knowledge/KNOWLEDGE.md
wm.readManagedWorkspaceFileUtf8(rel); // any workspace-relative path, with path traversal check
wm.listKnowledgeFiles(); // union of both layers
wm.listMemoryFilePaths();
wm.listSessionLogFiles();
wm.appendUtf8WorkspaceRelative(rel, content); // goes through AbstractFilesystem
wm.updateSessionIndex(agentId, sessionId, summary); // maintains sessions.json
Configuration¶
HarnessAgent agent = HarnessAgent.builder()
.name("MyAgent")
.model(model)
.workspace(Paths.get(".agentscope/workspace")) // uses default if not provided
.additionalContextFile("SOUL.md") // any workspace-relative path
.additionalContextFile("PREFERENCES.md")
.maxContextTokens(8000) // controls injection cap for MEMORY
.build();
If AGENTS.md is missing, the agent still works but loses the persona section. It is recommended to at least write a minimal skeleton (see the quickstart in overview.md).