@botbotgo/agent-harness 0.0.282 → 0.0.284

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +2 -2
  2. package/README.zh.md +2 -2
  3. package/dist/config/agents/direct.yaml +0 -3
  4. package/dist/config/agents/orchestra.yaml +0 -3
  5. package/dist/config/catalogs/stores.yaml +2 -2
  6. package/dist/config/catalogs/vector-stores.yaml +1 -1
  7. package/dist/config/knowledge/knowledge-runtime.yaml +3 -3
  8. package/dist/config/runtime/runtime-memory.yaml +3 -3
  9. package/dist/config/runtime/workspace.yaml +16 -9
  10. package/dist/contracts/workspace.d.ts +6 -0
  11. package/dist/init-project.js +11 -9
  12. package/dist/package-version.d.ts +1 -1
  13. package/dist/package-version.js +1 -1
  14. package/dist/persistence/file-store.d.ts +1 -0
  15. package/dist/persistence/file-store.js +29 -25
  16. package/dist/persistence/sqlite-store.js +6 -3
  17. package/dist/runtime/harness/run/resources.js +4 -2
  18. package/dist/runtime/harness/system/mem0-ingestion-sync.js +1 -1
  19. package/dist/runtime/harness/system/runtime-memory-manager.js +1 -1
  20. package/dist/runtime/harness/system/runtime-memory-policy.js +1 -1
  21. package/dist/runtime/harness/system/runtime-memory-sync.js +2 -2
  22. package/dist/runtime/harness/system/thread-memory-sync.js +1 -1
  23. package/dist/runtime/harness.js +12 -11
  24. package/dist/runtime/maintenance/checkpoint-maintenance.js +2 -1
  25. package/dist/runtime/maintenance/runtime-record-maintenance.js +2 -1
  26. package/dist/runtime/support/llamaindex.d.ts +1 -1
  27. package/dist/runtime/support/llamaindex.js +2 -2
  28. package/dist/runtime/support/runtime-factories.js +6 -3
  29. package/dist/runtime/support/runtime-layout.d.ts +7 -0
  30. package/dist/runtime/support/runtime-layout.js +22 -0
  31. package/dist/runtime/support/vector-stores.js +4 -3
  32. package/dist/workspace/agent-binding-compiler.js +8 -6
  33. package/dist/workspace/compile.js +6 -1
  34. package/dist/workspace/object-loader.js +3 -0
  35. package/dist/workspace/support/workspace-ref-utils.d.ts +6 -0
  36. package/dist/workspace/support/workspace-ref-utils.js +25 -0
  37. package/package.json +1 -1
package/README.md CHANGED
@@ -524,7 +524,7 @@ Use `normalizeUserChatInput(...)` when a product already has chat-style user mes
524
524
  import { memorize, recall } from "@botbotgo/agent-harness";
525
525
 
526
526
  await memorize(runtime, {
527
- threadId: "thread-123",
527
+ sessionId: "session-123",
528
528
  records: [
529
529
  {
530
530
  content: "The release checklist requires a smoke test before publish.",
@@ -972,7 +972,7 @@ spec:
972
972
  maxFileSizeMb: 10
973
973
  sessionStorage:
974
974
  enabled: true
975
- rootDir: "{runRoot}/threads/{threadId}/filesystem"
975
+ rootDir: "{runRoot}/sessions/{sessionId}/filesystem"
976
976
  middleware: []
977
977
  systemPrompt: Answer simple requests directly.
978
978
  ```
package/README.zh.md CHANGED
@@ -495,7 +495,7 @@ const result = await request(runtime, {
495
495
  import { memorize, recall } from "@botbotgo/agent-harness";
496
496
 
497
497
  await memorize(runtime, {
498
- threadId: "thread-123",
498
+ sessionId: "session-123",
499
499
  records: [
500
500
  {
501
501
  content: "The release checklist requires a smoke test before publish.",
@@ -929,7 +929,7 @@ spec:
929
929
  maxFileSizeMb: 10
930
930
  sessionStorage:
931
931
  enabled: true
932
- rootDir: "{runRoot}/threads/{threadId}/filesystem"
932
+ rootDir: "{runRoot}/sessions/{sessionId}/filesystem"
933
933
  middleware: []
934
934
  systemPrompt: Answer simple requests directly.
935
935
  ```
@@ -10,9 +10,6 @@ metadata:
10
10
  description: Manual low-latency host for direct answers and lightweight inventory lookup. Do not use it as the default executor for tool-heavy or delegation-heavy tasks.
11
11
  spec:
12
12
  runtime:
13
- # agent-harness feature: host-level run data placement override.
14
- # This matches the workspace default and keeps the default config shape explicit.
15
- runRoot: ./.agent
16
13
  # agent-harness feature: workspace-level durable long-term memory defaults for this host profile.
17
14
  runtimeMemory: default
18
15
  # =====================
@@ -10,9 +10,6 @@ metadata:
10
10
  description: Default execution host. Answer directly when possible, use local tools and skills first, and delegate only when a subagent is a better fit. Not a reflex delegation-only orchestrator.
11
11
  spec:
12
12
  runtime:
13
- # agent-harness feature: host-level run data placement override.
14
- # This matches the workspace default and keeps the default config shape explicit.
15
- runRoot: ./.agent
16
13
  # agent-harness feature: workspace-level durable long-term memory defaults for this host profile.
17
14
  runtimeMemory: default
18
15
  # =====================
@@ -8,11 +8,11 @@ spec:
8
8
  name: default
9
9
  description: Default sqlite-backed store preset for runtime-managed agent state and durable memory.
10
10
  storeKind: SqliteStore
11
- path: store.sqlite
11
+ path: knowledge/records.sqlite
12
12
 
13
13
  # agent-harness feature: reusable checkpointer preset for resumable execution state.
14
14
  - kind: Checkpointer
15
15
  name: default
16
16
  description: Default file-backed checkpointer preset for durable local graph state without native sqlite bindings.
17
17
  checkpointerKind: FileCheckpointer
18
- path: checkpoints.json
18
+ path: runtime/checkpoints.json
@@ -13,7 +13,7 @@ spec:
13
13
  storeKind: LibSQLVectorStore
14
14
  # LangChain aligned feature: libSQL connection URL.
15
15
  # Local SQLite files use the `file:` prefix.
16
- url: file:.agent/vector-store.db
16
+ url: file:knowledge/vectors.sqlite
17
17
  # LangChain aligned feature: target table and embedding column.
18
18
  table: rag_chunks
19
19
  column: embedding
@@ -22,7 +22,7 @@ spec:
22
22
  ## agent-harness feature: stable namespace roots shared with runtime memory policy.
23
23
  users: memories/users/{userId}
24
24
  projects: memories/projects/{projectId}
25
- threads: memories/threads/{threadId}
25
+ sessions: memories/sessions/{sessionId}
26
26
  agents: memories/agents/{agentId}
27
27
  workspaces: memories/workspaces/{workspaceId}
28
28
  retrieval:
@@ -47,7 +47,7 @@ spec:
47
47
  enabled: true
48
48
  scopes:
49
49
  - thread
50
- stateStorePath: runtime-memory-formation-state.json
50
+ stateStorePath: knowledge/formation-state.json
51
51
  maxMessagesPerRun: 40
52
52
  writeOnApprovalResolution: true
53
53
  writeOnRunCompletion: true
@@ -56,5 +56,5 @@ spec:
56
56
  enabled: false
57
57
  apiKeyEnv: MEM0_API_KEY
58
58
  appId: agent-harness
59
- stateStorePath: mem0-sync-state.json
59
+ stateStorePath: knowledge/mem0-sync-state.json
60
60
  maxMessagesPerRun: 200
@@ -30,7 +30,7 @@ spec:
30
30
  namespaces:
31
31
  users: memories/users/{userId}
32
32
  projects: memories/projects/{projectId}
33
- threads: memories/threads/{threadId}
33
+ sessions: memories/sessions/{sessionId}
34
34
  agents: memories/agents/{agentId}
35
35
  workspaces: memories/workspaces/{workspaceId}
36
36
 
@@ -78,7 +78,7 @@ spec:
78
78
  enabled: true
79
79
  scopes:
80
80
  - thread
81
- stateStorePath: runtime-memory-formation-state.json
81
+ stateStorePath: knowledge/formation-state.json
82
82
  maxMessagesPerRun: 40
83
83
  writeOnApprovalResolution: true
84
84
  writeOnRunCompletion: true
@@ -92,5 +92,5 @@ spec:
92
92
  enabled: false
93
93
  apiKeyEnv: MEM0_API_KEY
94
94
  appId: agent-harness
95
- stateStorePath: mem0-sync-state.json
95
+ stateStorePath: knowledge/mem0-sync-state.json
96
96
  maxMessagesPerRun: 200
@@ -12,15 +12,22 @@ spec:
12
12
  # ======================
13
13
  # agent-harness Features
14
14
  # ======================
15
- # agent-harness feature: filesystem root where the harness stores local run artifacts by default.
16
- # This affects file-backed persistence such as:
17
- # - session / thread / run indexes
18
- # - approval and delegation records
19
- # - default file-backed store paths
20
- # - default file-backed checkpointer paths
21
- # Agents inherit this value unless they override `spec.runRoot` locally.
22
- # Value options: relative workspace path like `./.agent`, or an absolute filesystem path.
23
- runRoot: ./.agent
15
+ # agent-harness feature: read-only application folder that contains the shared workspace assembly.
16
+ # This is where the runtime loads config catalogs and packaged resources such as:
17
+ # - config/
18
+ # - resources/
19
+ # Downstream applications can point multiple runtime profiles at the same application folder.
20
+ applicationRoot: .
21
+
22
+ # agent-harness feature: writable data folder for one runtime profile.
23
+ # Runtime-owned persistence stays under this root, split by function:
24
+ # - runtime/ for requests, approvals, checkpoints, and artifacts
25
+ # - knowledge/ for durable knowledge and vector indexes
26
+ # Different runtime profiles can use different data folders while sharing the same application folder.
27
+ dataRoot: ./.agent
28
+
29
+ # agent-harness feature: stable runtime profile identifier for this data folder.
30
+ profile: default
24
31
 
25
32
  # agent-harness feature: runtime-level task queue and maximum number of concurrent runs.
26
33
  # Additional runs wait in the harness queue until a slot becomes available.
@@ -7,6 +7,9 @@ export type ParsedAgentObject = {
7
7
  description: string;
8
8
  modelRef: string;
9
9
  runRoot?: string;
10
+ applicationRoot?: string;
11
+ dataRoot?: string;
12
+ runtimeProfile?: string;
10
13
  toolRefs: string[];
11
14
  toolBindings?: ParsedAgentToolBinding[];
12
15
  inlineTools?: ParsedToolObject[];
@@ -260,6 +263,9 @@ export type CompiledAgentBinding = {
260
263
  */
261
264
  deepAgentParams?: LegacyDeepAgentParams;
262
265
  harnessRuntime: {
266
+ applicationRoot?: string;
267
+ dataRoot?: string;
268
+ runtimeProfile?: string;
263
269
  runRoot: string;
264
270
  workspaceRoot?: string;
265
271
  capabilities?: RuntimeCapabilities;
@@ -93,7 +93,9 @@ kind: Runtime
93
93
  metadata:
94
94
  name: default
95
95
  spec:
96
- runRoot: ./.agent
96
+ applicationRoot: .
97
+ dataRoot: ./.agent
98
+ profile: default
97
99
  `;
98
100
  }
99
101
  function renderAgentContext(options) {
@@ -143,11 +145,11 @@ spec:
143
145
  enabled: true
144
146
  store:
145
147
  kind: SqliteStore
146
- path: runtime-memory.sqlite
148
+ path: knowledge/records.sqlite
147
149
  namespaces:
148
150
  users: memories/users/{userId}
149
151
  projects: memories/projects/{projectId}
150
- threads: memories/threads/{threadId}
152
+ sessions: memories/sessions/{sessionId}
151
153
  agents: memories/agents/{agentId}
152
154
  workspaces: memories/workspaces/{workspaceId}
153
155
  retrieval:
@@ -179,7 +181,7 @@ spec:
179
181
  enabled: true
180
182
  scopes:
181
183
  - thread
182
- stateStorePath: runtime-memory-formation-state.json
184
+ stateStorePath: knowledge/formation-state.json
183
185
  maxMessagesPerRun: 40
184
186
  writeOnApprovalResolution: true
185
187
  writeOnRunCompletion: true
@@ -189,7 +191,7 @@ spec:
189
191
  enabled: false
190
192
  apiKeyEnv: MEM0_API_KEY
191
193
  appId: ${projectSlug}
192
- stateStorePath: mem0-sync-state.json
194
+ stateStorePath: knowledge/mem0-sync-state.json
193
195
  maxMessagesPerRun: 200
194
196
  `;
195
197
  }
@@ -202,11 +204,11 @@ spec:
202
204
  enabled: true
203
205
  store:
204
206
  kind: SqliteStore
205
- path: runtime-memory.sqlite
207
+ path: knowledge/records.sqlite
206
208
  namespaces:
207
209
  users: memories/users/{userId}
208
210
  projects: memories/projects/{projectId}
209
- threads: memories/threads/{threadId}
211
+ sessions: memories/sessions/{sessionId}
210
212
  agents: memories/agents/{agentId}
211
213
  workspaces: memories/workspaces/{workspaceId}
212
214
  retrieval:
@@ -223,7 +225,7 @@ spec:
223
225
  enabled: true
224
226
  scopes:
225
227
  - thread
226
- stateStorePath: runtime-memory-formation-state.json
228
+ stateStorePath: knowledge/formation-state.json
227
229
  maxMessagesPerRun: 40
228
230
  writeOnApprovalResolution: true
229
231
  writeOnRunCompletion: true
@@ -231,7 +233,7 @@ spec:
231
233
  enabled: false
232
234
  apiKeyEnv: MEM0_API_KEY
233
235
  appId: ${projectSlug}
234
- stateStorePath: mem0-sync-state.json
236
+ stateStorePath: knowledge/mem0-sync-state.json
235
237
  maxMessagesPerRun: 200
236
238
  `;
237
239
  }
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.281";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.283";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.281";
1
+ export const AGENT_HARNESS_VERSION = "0.0.283";
@@ -10,6 +10,7 @@ type RunIndexRecord = {
10
10
  export declare class FilePersistence implements RuntimePersistence {
11
11
  private readonly runRoot;
12
12
  constructor(runRoot: string);
13
+ private artifactsRoot;
13
14
  private threadIndexPath;
14
15
  private runIndexPath;
15
16
  private approvalIndexPath;
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { appendFile, readFile, readdir, rm, writeFile } from "node:fs/promises";
3
3
  import { ensureDir, fileExists, readJson, writeJson } from "../utils/fs.js";
4
+ import { resolveRuntimeArtifactsRoot } from "../runtime/support/runtime-layout.js";
4
5
  function nowIso() {
5
6
  return new Date(Date.now()).toISOString();
6
7
  }
@@ -9,39 +10,42 @@ export class FilePersistence {
9
10
  constructor(runRoot) {
10
11
  this.runRoot = runRoot;
11
12
  }
13
+ artifactsRoot() {
14
+ return resolveRuntimeArtifactsRoot(this.runRoot);
15
+ }
12
16
  threadIndexPath(threadId) {
13
- return path.join(this.runRoot, "indexes", "threads", `${threadId}.json`);
17
+ return path.join(this.artifactsRoot(), "indexes", "sessions", `${threadId}.json`);
14
18
  }
15
19
  runIndexPath(runId) {
16
- return path.join(this.runRoot, "indexes", "runs", `${runId}.json`);
20
+ return path.join(this.artifactsRoot(), "indexes", "requests", `${runId}.json`);
17
21
  }
18
22
  approvalIndexPath(approvalId) {
19
- return path.join(this.runRoot, "indexes", "approvals", `${approvalId}.json`);
23
+ return path.join(this.artifactsRoot(), "indexes", "approvals", `${approvalId}.json`);
20
24
  }
21
25
  runQueuePath(runId) {
22
- return path.join(this.runRoot, "indexes", "queue", `${runId}.json`);
26
+ return path.join(this.artifactsRoot(), "indexes", "requests-queue", `${runId}.json`);
23
27
  }
24
28
  runControlPath(runId) {
25
- return path.join(this.runRoot, "indexes", "run-control", `${runId}.json`);
29
+ return path.join(this.artifactsRoot(), "indexes", "requests-control", `${runId}.json`);
26
30
  }
27
31
  traceItemsPath(threadId, runId) {
28
32
  return path.join(this.runDir(threadId, runId), "trace-items.ndjson");
29
33
  }
30
34
  async initialize() {
31
35
  await Promise.all([
32
- "indexes/threads",
33
- "indexes/runs",
36
+ "indexes/sessions",
37
+ "indexes/requests",
34
38
  "indexes/approvals",
35
- "indexes/queue",
36
- "indexes/run-control",
37
- "threads",
38
- ].map((segment) => ensureDir(path.join(this.runRoot, segment))));
39
+ "indexes/requests-queue",
40
+ "indexes/requests-control",
41
+ "sessions",
42
+ ].map((segment) => ensureDir(path.join(this.artifactsRoot(), segment))));
39
43
  }
40
44
  threadDir(threadId) {
41
- return path.join(this.runRoot, "threads", threadId);
45
+ return path.join(this.artifactsRoot(), "sessions", threadId);
42
46
  }
43
47
  runDir(threadId, runId) {
44
- return path.join(this.threadDir(threadId), "runs", runId);
48
+ return path.join(this.threadDir(threadId), "requests", runId);
45
49
  }
46
50
  async createThread(input) {
47
51
  const meta = {
@@ -58,7 +62,7 @@ export class FilePersistence {
58
62
  threadId: input.threadId,
59
63
  items: [],
60
64
  });
61
- await writeJson(path.join(this.runRoot, "indexes", "threads", `${input.threadId}.json`), {
65
+ await writeJson(this.threadIndexPath(input.threadId), {
62
66
  threadId: input.threadId,
63
67
  status: input.status,
64
68
  latestRunId: input.runId,
@@ -118,7 +122,7 @@ export class FilePersistence {
118
122
  hasError: false,
119
123
  lastError: null,
120
124
  }),
121
- writeJson(path.join(this.runRoot, "indexes", "runs", `${input.runId}.json`), {
125
+ writeJson(this.runIndexPath(input.runId), {
122
126
  runId: input.runId,
123
127
  threadId: input.threadId,
124
128
  state: "running",
@@ -175,7 +179,7 @@ export class FilePersistence {
175
179
  kind: "langgraph-checkpoint",
176
180
  });
177
181
  }
178
- await writeJson(path.join(this.runRoot, "indexes", "runs", `${runId}.json`), {
182
+ await writeJson(this.runIndexPath(runId), {
179
183
  runId,
180
184
  threadId,
181
185
  state,
@@ -188,7 +192,7 @@ export class FilePersistence {
188
192
  threadMeta.updatedAt = now;
189
193
  threadMeta.latestRunId = runId;
190
194
  await writeJson(threadMetaPath, threadMeta);
191
- await writeJson(path.join(this.runRoot, "indexes", "threads", `${threadId}.json`), {
195
+ await writeJson(this.threadIndexPath(threadId), {
192
196
  threadId,
193
197
  status: state,
194
198
  latestRunId: runId,
@@ -208,7 +212,7 @@ export class FilePersistence {
208
212
  ]);
209
213
  }
210
214
  async listSessions(filter = {}) {
211
- const threadIndexDir = path.join(this.runRoot, "indexes", "threads");
215
+ const threadIndexDir = path.join(this.artifactsRoot(), "indexes", "sessions");
212
216
  if (!(await fileExists(threadIndexDir))) {
213
217
  return [];
214
218
  }
@@ -253,7 +257,7 @@ export class FilePersistence {
253
257
  return summaries.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
254
258
  }
255
259
  async listRunIndexes() {
256
- const runIndexDir = path.join(this.runRoot, "indexes", "runs");
260
+ const runIndexDir = path.join(this.artifactsRoot(), "indexes", "requests");
257
261
  if (!(await fileExists(runIndexDir))) {
258
262
  return [];
259
263
  }
@@ -314,7 +318,7 @@ export class FilePersistence {
314
318
  return this.readRunSummary(index.threadId, index.runId);
315
319
  }
316
320
  async getSession(threadId) {
317
- const filePath = path.join(this.runRoot, "indexes", "threads", `${threadId}.json`);
321
+ const filePath = this.threadIndexPath(threadId);
318
322
  if (!(await fileExists(filePath))) {
319
323
  return null;
320
324
  }
@@ -360,7 +364,7 @@ export class FilePersistence {
360
364
  return Promise.all(entries.map((entry) => readJson(path.join(eventsDir, entry))));
361
365
  }
362
366
  async listApprovals(filter = {}) {
363
- const approvalsDir = path.join(this.runRoot, "indexes", "approvals");
367
+ const approvalsDir = path.join(this.artifactsRoot(), "indexes", "approvals");
364
368
  if (!(await fileExists(approvalsDir))) {
365
369
  return [];
366
370
  }
@@ -380,7 +384,7 @@ export class FilePersistence {
380
384
  });
381
385
  }
382
386
  async getApproval(approvalId) {
383
- const approvalPath = path.join(this.runRoot, "indexes", "approvals", `${approvalId}.json`);
387
+ const approvalPath = this.approvalIndexPath(approvalId);
384
388
  if (!(await fileExists(approvalPath))) {
385
389
  return null;
386
390
  }
@@ -496,7 +500,7 @@ export class FilePersistence {
496
500
  };
497
501
  await Promise.all([
498
502
  writeJson(path.join(this.runDir(sessionId, requestId), "approvals", `${record.approvalId}.json`), normalized),
499
- writeJson(path.join(this.runRoot, "indexes", "approvals", `${record.approvalId}.json`), normalized),
503
+ writeJson(this.approvalIndexPath(record.approvalId), normalized),
500
504
  ]);
501
505
  }
502
506
  async resolveApproval(threadId, runId, approvalId, status) {
@@ -509,7 +513,7 @@ export class FilePersistence {
509
513
  };
510
514
  await Promise.all([
511
515
  writeJson(approvalPath, updated),
512
- writeJson(path.join(this.runRoot, "indexes", "approvals", `${approvalId}.json`), updated),
516
+ writeJson(this.approvalIndexPath(approvalId), updated),
513
517
  ]);
514
518
  return updated;
515
519
  }
@@ -668,7 +672,7 @@ export class FilePersistence {
668
672
  });
669
673
  }
670
674
  async listExpiredClaimedRuns(cutoffIso) {
671
- const queueDir = path.join(this.runRoot, "indexes", "queue");
675
+ const queueDir = path.join(this.artifactsRoot(), "indexes", "requests-queue");
672
676
  if (!(await fileExists(queueDir))) {
673
677
  return [];
674
678
  }
@@ -2,6 +2,7 @@ import path from "node:path";
2
2
  import { mkdir, rm } from "node:fs/promises";
3
3
  import { createClient } from "@libsql/client";
4
4
  import { fileExists, readJson, writeJson } from "../utils/fs.js";
5
+ import { resolveRuntimeRoot, resolveRuntimeSqlitePath } from "../runtime/support/runtime-layout.js";
5
6
  import { SqliteRunContextStore } from "./sqlite-run-context-store.js";
6
7
  import { SqliteRunQueueStore } from "./sqlite-run-queue-store.js";
7
8
  const RUNTIME_SQLITE_SCHEMA_VERSION = 6;
@@ -97,7 +98,9 @@ export class SqlitePersistence {
97
98
  initialization = null;
98
99
  constructor(runRoot, dbFile = "runtime.sqlite") {
99
100
  this.runRoot = runRoot;
100
- this.dbPath = path.join(runRoot, dbFile);
101
+ this.dbPath = dbFile === "runtime.sqlite"
102
+ ? resolveRuntimeSqlitePath(runRoot)
103
+ : path.join(resolveRuntimeRoot(runRoot), dbFile);
101
104
  this.runContextStore = new SqliteRunContextStore({
102
105
  execute: (sql, args) => this.execute(sql, args),
103
106
  selectOne: (sql, args) => this.selectOne(sql, args),
@@ -111,7 +114,7 @@ export class SqlitePersistence {
111
114
  }
112
115
  async getClient() {
113
116
  if (!this.client) {
114
- await mkdir(this.runRoot, { recursive: true });
117
+ await mkdir(path.dirname(this.dbPath), { recursive: true });
115
118
  this.client = createClient({ url: toSqliteUrl(this.dbPath) });
116
119
  }
117
120
  return this.client;
@@ -148,7 +151,7 @@ export class SqlitePersistence {
148
151
  return;
149
152
  }
150
153
  this.initialization = (async () => {
151
- await mkdir(this.runRoot, { recursive: true });
154
+ await mkdir(resolveRuntimeRoot(this.runRoot), { recursive: true });
152
155
  await this.getClient();
153
156
  await this.rawExecute("PRAGMA journal_mode=WAL");
154
157
  await this.rawExecute("PRAGMA foreign_keys=ON");
@@ -1,6 +1,7 @@
1
1
  import { createCheckpointerForConfig, createStoreForConfig } from "../../support/runtime-factories.js";
2
2
  import { resolveCompiledEmbeddingModel, resolveCompiledEmbeddingModelRef } from "../../support/embedding-models.js";
3
3
  import { resolveCompiledVectorStore, resolveCompiledVectorStoreRef } from "../../support/vector-stores.js";
4
+ import { resolveRuntimeCheckpointerPath } from "../../support/runtime-layout.js";
4
5
  export function resolveStoreFromConfig(stores, storeConfig, runRoot) {
5
6
  const cacheKey = storeConfig ? `${runRoot}:${JSON.stringify(storeConfig)}` : undefined;
6
7
  if (!storeConfig || !cacheKey) {
@@ -42,12 +43,13 @@ export async function resolveVectorStore(workspace, vectorStores, vectorStoreRef
42
43
  return resolved;
43
44
  }
44
45
  export function resolveCheckpointer(checkpointers, binding) {
45
- const key = `${binding.harnessRuntime.runRoot}:${JSON.stringify(binding.harnessRuntime.checkpointer ?? { kind: "FileCheckpointer", path: "checkpoints.json" })}`;
46
+ const key = `${binding.harnessRuntime.runRoot}:${JSON.stringify(binding.harnessRuntime.checkpointer ?? { kind: "FileCheckpointer", path: resolveRuntimeCheckpointerPath(binding.harnessRuntime.runRoot, "checkpoints.json") })}`;
46
47
  const existing = checkpointers.get(key);
47
48
  if (existing) {
48
49
  return existing;
49
50
  }
50
- const resolvedConfig = binding.harnessRuntime.checkpointer ?? { kind: "FileCheckpointer", path: "checkpoints.json" };
51
+ const resolvedConfig = binding.harnessRuntime.checkpointer ??
52
+ { kind: "FileCheckpointer", path: resolveRuntimeCheckpointerPath(binding.harnessRuntime.runRoot, "checkpoints.json") };
51
53
  if (typeof resolvedConfig === "boolean") {
52
54
  checkpointers.set(key, resolvedConfig);
53
55
  return resolvedConfig;
@@ -62,7 +62,7 @@ export function readMem0RuntimeConfig(runtimeMemory, workspaceRoot) {
62
62
  projectId: asNonEmptyString(mem0.projectId),
63
63
  appId: asNonEmptyString(mem0.appId) ?? workspaceBaseName,
64
64
  userIdPrefix: asNonEmptyString(mem0.userIdPrefix),
65
- stateStorePath: asNonEmptyString(mem0.stateStorePath) ?? "mem0-sync-state.json",
65
+ stateStorePath: asNonEmptyString(mem0.stateStorePath) ?? "knowledge/mem0-sync-state.json",
66
66
  maxMessagesPerRun: asPositiveInteger(mem0.maxMessagesPerRun) ?? 200,
67
67
  writeOnApprovalResolution: asBoolean(mem0.writeOnApprovalResolution) ?? asBoolean(ingestion?.writeOnApprovalResolution) ?? true,
68
68
  writeOnRunCompletion: asBoolean(mem0.writeOnRunCompletion) ?? asBoolean(ingestion?.writeOnRunCompletion) ?? true,
@@ -57,7 +57,7 @@ export function readRuntimeMemoryFormationConfig(runtimeMemory, workspaceRoot) {
57
57
  enabled: asBoolean(background?.enabled) ?? true,
58
58
  maxMessagesPerRun: asPositiveInteger(background?.maxMessagesPerRun) ?? asPositiveInteger(ingestion?.maxMessagesPerRun) ?? 40,
59
59
  scopes: normalizeScopeList(asMemoryScopes(background?.scopes) ?? ["thread"]),
60
- stateStorePath: asNonEmptyString(background?.stateStorePath) ?? `${workspaceBaseName}-memory-formation-state.json`,
60
+ stateStorePath: asNonEmptyString(background?.stateStorePath) ?? `knowledge/${workspaceBaseName}-memory-formation-state.json`,
61
61
  writeOnApprovalResolution: asBoolean(background?.writeOnApprovalResolution) ?? asBoolean(ingestion?.writeOnApprovalResolution) ?? true,
62
62
  writeOnRunCompletion: asBoolean(background?.writeOnRunCompletion) ?? asBoolean(ingestion?.writeOnRunCompletion) ?? true,
63
63
  },
@@ -35,7 +35,7 @@ export function readRuntimeMemoryPolicyConfig(runtimeMemory, workspaceRoot) {
35
35
  maxPromptMemories: asPositiveInteger(retrieval?.maxPromptMemories) ?? 8,
36
36
  },
37
37
  namespaces: {
38
- thread: asString(namespaces?.threads) ?? "memories/threads/{threadId}",
38
+ thread: asString(namespaces?.sessions) ?? "memories/sessions/{sessionId}",
39
39
  workspace: asString(namespaces?.workspaces) ?? `memories/workspaces/${workspaceId}`,
40
40
  agent: asString(namespaces?.agents) ?? "memories/agents/{agentId}",
41
41
  user: asString(namespaces?.users) ?? "memories/users/{userId}",
@@ -192,10 +192,10 @@ export class RuntimeMemorySync {
192
192
  await this.store.put(this.resolveThreadNamespace(threadId), "durable-summary.md", { content: `${digestMarkdown}\n` });
193
193
  }
194
194
  resolveThreadNamespace(threadId) {
195
- return this.options.resolveThreadNamespace ? this.options.resolveThreadNamespace(threadId) : ["memories", "threads", threadId];
195
+ return this.options.resolveThreadNamespace ? this.options.resolveThreadNamespace(threadId) : ["memories", "sessions", threadId];
196
196
  }
197
197
  resolveRunNamespace(threadId) {
198
- return this.options.resolveRunNamespace ? this.options.resolveRunNamespace(threadId) : ["memories", "runs", threadId];
198
+ return this.options.resolveRunNamespace ? this.options.resolveRunNamespace(threadId) : ["memories", "requests", threadId];
199
199
  }
200
200
  async close() {
201
201
  await Promise.allSettled(Array.from(this.pending));
@@ -47,7 +47,7 @@ function formatOpenApprovalsSection(approvals) {
47
47
  return lines;
48
48
  }
49
49
  function resolveThreadNamespace(threadId, resolver) {
50
- return resolver ? resolver(threadId) : ["memories", "threads", threadId];
50
+ return resolver ? resolver(threadId) : ["memories", "sessions", threadId];
51
51
  }
52
52
  const THREAD_MEMORY_EVENT_TYPES = new Set([
53
53
  "run.state.changed",
@@ -10,7 +10,7 @@ import { getConcurrencyConfig, getRecoveryConfig, getRoutingDefaultAgentId, getR
10
10
  import { createHarnessEvent, inferRoutingBindings, renderRuntimeFailure, } from "./support/harness-support.js";
11
11
  import { ThreadMemorySync } from "./harness/system/thread-memory-sync.js";
12
12
  import { RuntimeMemorySync, readRuntimeMemorySyncConfig } from "./harness/system/runtime-memory-sync.js";
13
- import { FileBackedStore } from "./harness/system/store.js";
13
+ import { SqliteBackedStore } from "./harness/system/store.js";
14
14
  import { HealthMonitor, readHealthMonitorConfig, } from "./harness/system/health-monitor.js";
15
15
  import { normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, } from "./harness/run/helpers.js";
16
16
  import { emitHarnessEvent, } from "./harness/events/events.js";
@@ -34,6 +34,7 @@ import { Mem0IngestionSync, Mem0SemanticRecall, readMem0RuntimeConfig, } from ".
34
34
  import { createRuntimeMemoryManager, RuntimeMemoryFormationSync, readRuntimeMemoryFormationConfig, } from "./harness/system/runtime-memory-manager.js";
35
35
  import { readRuntimeMemoryMaintenanceConfig, readRuntimeMemoryPolicyConfig, resolveMemoryNamespace, } from "./harness/system/runtime-memory-policy.js";
36
36
  import { resolveRuntimeAdapterOptions } from "./support/runtime-adapter-options.js";
37
+ import { resolveKnowledgeStorePath } from "./support/runtime-layout.js";
37
38
  import { initializeHarnessRuntime, reclaimExpiredClaimedRuns as reclaimHarnessExpiredClaimedRuns, recoverStartupRuns as recoverHarnessStartupRuns, isStaleRunningRun as isHarnessStaleRunningRun, } from "./harness/run/startup-runtime.js";
38
39
  import { traceStartupStage } from "./startup-tracing.js";
39
40
  import { normalizeProcessExecutablePath } from "./support/runtime-env.js";
@@ -256,11 +257,11 @@ export class AgentHarnessRuntime {
256
257
  normalizeProcessExecutablePath();
257
258
  this.runtimeEntryBindings = inferRoutingBindings(this.workspace).runtimeEntryBindings;
258
259
  this.defaultRuntimeEntryBinding = this.runtimeEntryBindings[0];
259
- this.defaultRunRootValue = this.defaultRuntimeEntryBinding?.harnessRuntime.runRoot ?? `${this.workspace.workspaceRoot}/run-data`;
260
+ this.defaultRunRootValue = this.defaultRuntimeEntryBinding?.harnessRuntime.runRoot ?? `${this.workspace.workspaceRoot}/.agent`;
260
261
  const runRoot = this.defaultRunRoot();
261
262
  this.persistence = new SqlitePersistence(runRoot);
262
263
  const defaultStoreConfig = this.defaultRuntimeEntryBinding?.harnessRuntime.store;
263
- this.defaultStore = resolveStoreFromConfig(this.stores, defaultStoreConfig, runRoot) ?? new FileBackedStore(`${runRoot}/store.json`);
264
+ this.defaultStore = resolveStoreFromConfig(this.stores, defaultStoreConfig, runRoot) ?? new SqliteBackedStore(resolveKnowledgeStorePath(runRoot));
264
265
  const runtimeMemoryStoreConfig = typeof this.defaultRuntimeEntryBinding?.harnessRuntime.runtimeMemory?.store === "object" &&
265
266
  this.defaultRuntimeEntryBinding?.harnessRuntime.runtimeMemory?.store
266
267
  ? this.defaultRuntimeEntryBinding.harnessRuntime.runtimeMemory.store
@@ -291,12 +292,12 @@ export class AgentHarnessRuntime {
291
292
  this.routingDefaultAgentId = getRoutingDefaultAgentId(workspace.refs);
292
293
  if (isThreadMemorySyncEnabled(workspace)) {
293
294
  this.threadMemorySync = new ThreadMemorySync(this.persistence, this.runtimeMemoryStore, {
294
- resolveThreadNamespace: (threadId) => {
295
+ resolveThreadNamespace: (sessionId) => {
295
296
  const binding = this.defaultRuntimeEntryBinding;
296
297
  if (!binding) {
297
- return ["memories", "threads", threadId];
298
+ return ["memories", "sessions", sessionId];
298
299
  }
299
- return this.resolveMemoryNamespace("thread", binding, { threadId });
300
+ return this.resolveMemoryNamespace("thread", binding, { sessionId });
300
301
  },
301
302
  });
302
303
  this.unregisterThreadMemorySync = this.eventBus.registerProjection(this.threadMemorySync);
@@ -308,12 +309,12 @@ export class AgentHarnessRuntime {
308
309
  const runtimeMemorySyncConfig = readRuntimeMemorySyncConfig(this.defaultRuntimeEntryBinding?.harnessRuntime.runtimeMemory);
309
310
  if (runtimeMemorySyncConfig) {
310
311
  this.runtimeMemorySync = new RuntimeMemorySync(this.persistence, this.runtimeMemoryStore, runtimeMemorySyncConfig, {
311
- resolveThreadNamespace: (threadId) => {
312
+ resolveThreadNamespace: (sessionId) => {
312
313
  const binding = this.defaultRuntimeEntryBinding;
313
314
  if (!binding) {
314
- return ["memories", "threads", threadId];
315
+ return ["memories", "sessions", sessionId];
315
316
  }
316
- return this.resolveMemoryNamespace("thread", binding, { threadId });
317
+ return this.resolveMemoryNamespace("thread", binding, { sessionId });
317
318
  },
318
319
  });
319
320
  this.unregisterRuntimeMemorySync = this.eventBus.registerProjection(this.runtimeMemorySync);
@@ -363,7 +364,7 @@ export class AgentHarnessRuntime {
363
364
  return ["memories", `${scope}s`, identifier ?? "default"];
364
365
  }
365
366
  return this.resolveMemoryNamespace(scope, binding, {
366
- threadId: context.sessionId,
367
+ sessionId: context.sessionId,
367
368
  agentId: context.agentId,
368
369
  userId: context.userId,
369
370
  projectId: context.projectId,
@@ -937,7 +938,7 @@ export class AgentHarnessRuntime {
937
938
  const workspaceId = this.getWorkspaceId(binding);
938
939
  const template = this.runtimeMemoryPolicy?.namespaces[scope] ?? `memories/${scope}s/{${scope}Id}`;
939
940
  return resolveMemoryNamespace(template, {
940
- threadId: options.threadId,
941
+ sessionId: options.sessionId,
941
942
  agentId: options.agentId ?? binding.agent.id,
942
943
  workspaceId,
943
944
  userId: options.userId ?? "default",
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { fileExists } from "../../utils/fs.js";
3
+ import { resolveRuntimeCheckpointerPath } from "../support/runtime-layout.js";
3
4
  import { getRuntimeDefaults } from "../../workspace/support/workspace-ref-utils.js";
4
5
  function asObject(value) {
5
6
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
@@ -52,7 +53,7 @@ function resolveSqliteCheckpointPath(binding) {
52
53
  if (kind !== "SqliteSaver") {
53
54
  return null;
54
55
  }
55
- const configuredPath = typeof config.path === "string" ? String(config.path) : "checkpoints.sqlite";
56
+ const configuredPath = typeof config.path === "string" ? String(config.path) : resolveRuntimeCheckpointerPath(binding.harnessRuntime.runRoot, "checkpoints.sqlite");
56
57
  return path.isAbsolute(configuredPath) ? configuredPath : path.join(binding.harnessRuntime.runRoot, configuredPath);
57
58
  }
58
59
  export function discoverCheckpointMaintenanceTargets(workspace) {
@@ -2,6 +2,7 @@ import path from "node:path";
2
2
  import { rm } from "node:fs/promises";
3
3
  import { createClient } from "@libsql/client";
4
4
  import { fileExists } from "../../utils/fs.js";
5
+ import { resolveRuntimeSqlitePath } from "../support/runtime-layout.js";
5
6
  import { getRuntimeDefaults } from "../../workspace/support/workspace-ref-utils.js";
6
7
  function asObject(value) {
7
8
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
@@ -50,7 +51,7 @@ export function discoverRuntimeRecordMaintenanceTargets(workspace) {
50
51
  runRoots.add(binding.harnessRuntime.runRoot);
51
52
  }
52
53
  return Array.from(runRoots.values()).map((runRoot) => ({
53
- dbPath: path.join(runRoot, "runtime.sqlite"),
54
+ dbPath: resolveRuntimeSqlitePath(runRoot),
54
55
  }));
55
56
  }
56
57
  export async function maintainSqliteRuntimeRecords(dbPath, config, nowMs = Date.now()) {
@@ -21,4 +21,4 @@ export type VectorStoreRuntimeLike = {
21
21
  }) => Promise<void>;
22
22
  };
23
23
  export declare function createLlamaIndexEmbeddingModel(embeddingModel: CompiledEmbeddingModel): EmbeddingRuntimeLike;
24
- export declare function createLlamaIndexVectorStore(workspaceRoot: string, vectorStore: CompiledVectorStore, embeddings: EmbeddingRuntimeLike): Promise<VectorStoreRuntimeLike>;
24
+ export declare function createLlamaIndexVectorStore(dataRoot: string, vectorStore: CompiledVectorStore, embeddings: EmbeddingRuntimeLike): Promise<VectorStoreRuntimeLike>;
@@ -41,8 +41,8 @@ export function createLlamaIndexEmbeddingModel(embeddingModel) {
41
41
  },
42
42
  };
43
43
  }
44
- export async function createLlamaIndexVectorStore(workspaceRoot, vectorStore, embeddings) {
45
- const persistPath = resolveFilePath(vectorStore.url ?? ".agent/vector_store.json", workspaceRoot);
44
+ export async function createLlamaIndexVectorStore(dataRoot, vectorStore, embeddings) {
45
+ const persistPath = resolveFilePath(vectorStore.url ?? "knowledge/vectors.json", dataRoot);
46
46
  await mkdir(path.dirname(persistPath), { recursive: true });
47
47
  const emptyStore = () => SimpleVectorStore.fromDict({
48
48
  embeddingDict: {},
@@ -2,15 +2,16 @@ import path from "node:path";
2
2
  import { MemorySaver } from "@langchain/langgraph";
3
3
  import { FileCheckpointSaver } from "../maintenance/file-checkpoint-saver.js";
4
4
  import { createInMemoryStore, FileBackedStore, SqliteBackedStore } from "../harness/system/store.js";
5
+ import { resolveKnowledgeFileStorePath, resolveKnowledgeStorePath, resolveRuntimeCheckpointerPath, } from "./runtime-layout.js";
5
6
  export function createStoreForConfig(storeConfig, runRoot) {
6
7
  const kind = typeof storeConfig.kind === "string" ? storeConfig.kind : "FileStore";
7
8
  switch (kind) {
8
9
  case "FileStore": {
9
- const configuredPath = typeof storeConfig.path === "string" ? storeConfig.path : "store.json";
10
+ const configuredPath = typeof storeConfig.path === "string" ? storeConfig.path : resolveKnowledgeFileStorePath(runRoot, "records.json");
10
11
  return new FileBackedStore(path.isAbsolute(configuredPath) ? configuredPath : path.join(runRoot, configuredPath));
11
12
  }
12
13
  case "SqliteStore": {
13
- const configuredPath = typeof storeConfig.path === "string" ? storeConfig.path : "store.sqlite";
14
+ const configuredPath = typeof storeConfig.path === "string" ? storeConfig.path : resolveKnowledgeStorePath(runRoot, "records.sqlite");
14
15
  return new SqliteBackedStore(path.isAbsolute(configuredPath) ? configuredPath : path.join(runRoot, configuredPath));
15
16
  }
16
17
  case "InMemoryStore":
@@ -34,7 +35,9 @@ export function createCheckpointerForConfig(checkpointerConfig, runRoot) {
34
35
  case "SqliteSaver":
35
36
  throw new Error("Checkpointer kind SqliteSaver is not supported in this runtime right now");
36
37
  case "FileCheckpointer": {
37
- const configuredPath = typeof checkpointerConfig.path === "string" ? String(checkpointerConfig.path) : "checkpoints.json";
38
+ const configuredPath = typeof checkpointerConfig.path === "string"
39
+ ? String(checkpointerConfig.path)
40
+ : resolveRuntimeCheckpointerPath(runRoot, "checkpoints.json");
38
41
  return new FileCheckpointSaver(path.isAbsolute(configuredPath) ? configuredPath : path.join(runRoot, configuredPath));
39
42
  }
40
43
  default:
@@ -0,0 +1,7 @@
1
+ export declare function resolveRuntimeRoot(dataRoot: string): string;
2
+ export declare function resolveRuntimeArtifactsRoot(dataRoot: string): string;
3
+ export declare function resolveRuntimeSqlitePath(dataRoot: string): string;
4
+ export declare function resolveRuntimeCheckpointerPath(dataRoot: string, fileName?: string): string;
5
+ export declare function resolveKnowledgeRoot(dataRoot: string): string;
6
+ export declare function resolveKnowledgeStorePath(dataRoot: string, fileName?: string): string;
7
+ export declare function resolveKnowledgeFileStorePath(dataRoot: string, fileName?: string): string;
@@ -0,0 +1,22 @@
1
+ import path from "node:path";
2
+ export function resolveRuntimeRoot(dataRoot) {
3
+ return path.join(dataRoot, "runtime");
4
+ }
5
+ export function resolveRuntimeArtifactsRoot(dataRoot) {
6
+ return path.join(resolveRuntimeRoot(dataRoot), "artifacts");
7
+ }
8
+ export function resolveRuntimeSqlitePath(dataRoot) {
9
+ return path.join(resolveRuntimeRoot(dataRoot), "runtime.sqlite");
10
+ }
11
+ export function resolveRuntimeCheckpointerPath(dataRoot, fileName = "checkpoints.json") {
12
+ return path.join(resolveRuntimeRoot(dataRoot), fileName);
13
+ }
14
+ export function resolveKnowledgeRoot(dataRoot) {
15
+ return path.join(dataRoot, "knowledge");
16
+ }
17
+ export function resolveKnowledgeStorePath(dataRoot, fileName = "records.sqlite") {
18
+ return path.join(resolveKnowledgeRoot(dataRoot), fileName);
19
+ }
20
+ export function resolveKnowledgeFileStorePath(dataRoot, fileName = "records.json") {
21
+ return path.join(resolveKnowledgeRoot(dataRoot), fileName);
22
+ }
@@ -5,7 +5,7 @@ import { createClient } from "@libsql/client";
5
5
  import { LibSQLVectorStore } from "@langchain/community/vectorstores/libsql";
6
6
  import { QdrantClient } from "@qdrant/js-client-rest";
7
7
  import { compileVectorStore } from "../../workspace/resource-compilers.js";
8
- import { resolveRefId } from "../../workspace/support/workspace-ref-utils.js";
8
+ import { getRuntimeStorageRoots, resolveRefId } from "../../workspace/support/workspace-ref-utils.js";
9
9
  import { resolveCompiledEmbeddingModel, resolveCompiledEmbeddingModelRef } from "./embedding-models.js";
10
10
  import { createLlamaIndexVectorStore } from "./llamaindex.js";
11
11
  function resolveFileUrl(rawUrl, workspaceRoot) {
@@ -92,10 +92,11 @@ export async function resolveCompiledVectorStore(workspace, vectorStore, options
92
92
  if (options.vectorStoreResolver) {
93
93
  return options.vectorStoreResolver(vectorStore.id);
94
94
  }
95
+ const { dataRoot } = getRuntimeStorageRoots(workspace.refs, workspace.workspaceRoot);
95
96
  if (vectorStore.kind === "LlamaIndexSimpleVectorStore") {
96
97
  const embeddingModel = resolveCompiledEmbeddingModelRef(workspace, vectorStore.embeddingModelRef);
97
98
  const embeddings = await resolveCompiledEmbeddingModel(embeddingModel, options.embeddingModelResolver);
98
- return createLlamaIndexVectorStore(workspace.workspaceRoot, vectorStore, embeddings);
99
+ return createLlamaIndexVectorStore(dataRoot, vectorStore, embeddings);
99
100
  }
100
101
  if (vectorStore.kind === "QdrantVectorStore") {
101
102
  const embeddingModel = resolveCompiledEmbeddingModelRef(workspace, vectorStore.embeddingModelRef);
@@ -178,7 +179,7 @@ export async function resolveCompiledVectorStore(workspace, vectorStore, options
178
179
  const embeddingModel = resolveCompiledEmbeddingModelRef(workspace, vectorStore.embeddingModelRef);
179
180
  const embeddings = await resolveCompiledEmbeddingModel(embeddingModel, options.embeddingModelResolver);
180
181
  const db = createClient({
181
- url: await ensureFileUrlDirectory(vectorStore.url ?? "", workspace.workspaceRoot),
182
+ url: await ensureFileUrlDirectory(vectorStore.url ?? "", dataRoot),
182
183
  ...(vectorStore.authToken ? { authToken: vectorStore.authToken } : {}),
183
184
  });
184
185
  const table = vectorStore.table ?? "vectors";
@@ -5,7 +5,7 @@ import { compileModel, compileTool } from "./resource-compilers.js";
5
5
  import { inferAgentCapabilities } from "./support/agent-capabilities.js";
6
6
  import { getAgentExecutionConfigValue, getAgentExecutionObject, getAgentExecutionString } from "./support/agent-execution-config.js";
7
7
  import { discoverSkillPaths } from "./support/discovery.js";
8
- import { compileAgentMemories, getResilienceConfig, getRuntimeDefaults, getRuntimeMemoryDefaults, getWorkspaceObject, resolvePromptValue, resolveRefId } from "./support/workspace-ref-utils.js";
8
+ import { compileAgentMemories, getResilienceConfig, getRuntimeDefaults, getRuntimeMemoryDefaults, getRuntimeStorageRoots, getWorkspaceObject, resolvePromptValue, resolveRefId, } from "./support/workspace-ref-utils.js";
9
9
  const WORKSPACE_BOUNDARY_GUIDANCE = "Keep repository and file exploration bounded to the current workspace root unless the user explicitly asks for broader host or filesystem access. " +
10
10
  "Do not inspect absolute paths outside the workspace, system directories, or unrelated repos by default. " +
11
11
  "Prefer workspace-local tools, relative paths, and the current repository checkout when analyzing code.";
@@ -339,6 +339,7 @@ function resolveRuntimeMemoryConfig(agent, refs) {
339
339
  export function compileBinding(workspaceRoot, agent, agents, referencedSubagentIds, refs, models, tools) {
340
340
  const internalSubagent = referencedSubagentIds.has(agent.id);
341
341
  const runtimeDefaults = getRuntimeDefaults(refs);
342
+ const runtimeStorage = getRuntimeStorageRoots(refs, workspaceRoot);
342
343
  const resilience = getResilienceConfig(refs);
343
344
  const compiledAgentSkills = compileAgentSkills(workspaceRoot, agent);
344
345
  const compiledAgentMemory = compileAgentMemories(workspaceRoot, agent.memorySources);
@@ -361,15 +362,16 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
361
362
  ? mergeConfigObjects(runtimeFilesystemDefaults, getAgentExecutionObject(agent, "filesystem", { executionMode: "langchain-v1" }))
362
363
  : undefined;
363
364
  const runRoot = typeof agent.runRoot === "string" && agent.runRoot.trim().length > 0
364
- ? path.resolve(workspaceRoot, agent.runRoot)
365
- : typeof runtimeDefaults?.runRoot === "string" && runtimeDefaults.runRoot.trim().length > 0
366
- ? path.resolve(workspaceRoot, runtimeDefaults.runRoot)
367
- : path.join(workspaceRoot, "run-data");
365
+ ? (path.isAbsolute(agent.runRoot) ? agent.runRoot : path.resolve(runtimeStorage.dataRoot, agent.runRoot))
366
+ : runtimeStorage.dataRoot;
368
367
  const base = {
369
368
  agent,
370
369
  harnessRuntime: {
370
+ applicationRoot: runtimeStorage.applicationRoot,
371
+ dataRoot: runtimeStorage.dataRoot,
372
+ runtimeProfile: runtimeStorage.runtimeProfile,
371
373
  runRoot,
372
- workspaceRoot,
374
+ workspaceRoot: runtimeStorage.applicationRoot,
373
375
  capabilities: inferAgentCapabilities(agent),
374
376
  resilience,
375
377
  ...(runtimeGovernanceDefaults ? { governance: runtimeGovernanceDefaults } : {}),
@@ -10,7 +10,7 @@ import { validateAgent, validateTopology } from "./validate.js";
10
10
  import { compileBinding } from "./agent-binding-compiler.js";
11
11
  import { discoverSubagents, ensureDiscoverySources } from "./support/discovery.js";
12
12
  import { collectAgentDiscoverySourceRefs, collectToolSourceRefs } from "./support/source-collectors.js";
13
- import { getRoutingDefaultAgentId, getRuntimeResources, getToolModuleDiscoveryConfig, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
13
+ import { getRoutingDefaultAgentId, getRuntimeResources, getRuntimeStorageRoots, getToolModuleDiscoveryConfig, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
14
14
  import { hydrateAgentMcpTools, hydrateResourceAndExternalTools } from "./tool-hydration.js";
15
15
  import { traceStartupStage } from "../runtime/startup-tracing.js";
16
16
  import { shouldSkipScanDirectory } from "../utils/fs.js";
@@ -209,6 +209,11 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
209
209
  const loaded = await traceStartupStage("workspace.load.objects", () => loadWorkspaceObjects(workspaceRoot, options), {
210
210
  workspaceRoot,
211
211
  });
212
+ const runtimeStorage = getRuntimeStorageRoots(loaded.refs, workspaceRoot);
213
+ if (path.resolve(runtimeStorage.applicationRoot) !== path.resolve(workspaceRoot)) {
214
+ throw new Error(`Runtime applicationRoot ${runtimeStorage.applicationRoot} must resolve to the loaded workspace root ${workspaceRoot}. ` +
215
+ "Load the shared application folder as the workspace root and use Runtime.dataRoot for writable profile data.");
216
+ }
212
217
  loaded.agents = await traceStartupStage("workspace.discover.subagents", () => discoverSubagents(loaded.agents, workspaceRoot), {
213
218
  workspaceRoot,
214
219
  });
@@ -516,6 +516,9 @@ export function parseAgentItem(item, sourcePath) {
516
516
  description: String(normalizedItem.description ?? ""),
517
517
  modelRef: readExecutionValue(normalizedItem, "modelRef", readSingleRef) ?? "",
518
518
  runRoot: typeof runtime?.runRoot === "string" ? runtime.runRoot : undefined,
519
+ applicationRoot: typeof runtime?.applicationRoot === "string" ? runtime.applicationRoot : undefined,
520
+ dataRoot: typeof runtime?.dataRoot === "string" ? runtime.dataRoot : undefined,
521
+ runtimeProfile: typeof runtime?.profile === "string" ? runtime.profile : undefined,
519
522
  toolRefs: toolBindings.map((binding) => binding.ref),
520
523
  toolBindings,
521
524
  inlineTools: undefined,
@@ -37,6 +37,12 @@ export type ToolModuleDiscoveryConfig = {
37
37
  };
38
38
  export declare function getWorkspaceObject(refs: Map<string, WorkspaceObject | ParsedAgentObject>, ref: string | undefined): WorkspaceObject | undefined;
39
39
  export declare function getRuntimeDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
40
+ export type RuntimeStorageRoots = {
41
+ applicationRoot: string;
42
+ dataRoot: string;
43
+ runtimeProfile: string;
44
+ };
45
+ export declare function getRuntimeStorageRoots(refs: Map<string, WorkspaceObject | ParsedAgentObject>, workspaceRoot: string): RuntimeStorageRoots;
40
46
  export declare function getRuntimeResources(refs: Map<string, WorkspaceObject | ParsedAgentObject>): string[];
41
47
  export declare function getToolModuleDiscoveryConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): ToolModuleDiscoveryConfig;
42
48
  export declare function getRuntimeMemoryDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
@@ -26,6 +26,31 @@ export function getRuntimeDefaults(refs) {
26
26
  }
27
27
  return runtimes[0].value;
28
28
  }
29
+ function asNonEmptyString(value) {
30
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
31
+ }
32
+ export function getRuntimeStorageRoots(refs, workspaceRoot) {
33
+ const runtimeDefaults = getRuntimeDefaults(refs);
34
+ const configuredApplicationRoot = asNonEmptyString(runtimeDefaults?.applicationRoot);
35
+ const configuredDataRoot = asNonEmptyString(runtimeDefaults?.dataRoot);
36
+ const configuredProfile = asNonEmptyString(runtimeDefaults?.profile);
37
+ const applicationRoot = configuredApplicationRoot
38
+ ? (path.isAbsolute(configuredApplicationRoot)
39
+ ? configuredApplicationRoot
40
+ : path.resolve(workspaceRoot, configuredApplicationRoot))
41
+ : workspaceRoot;
42
+ const dataRoot = configuredDataRoot
43
+ ? (path.isAbsolute(configuredDataRoot)
44
+ ? configuredDataRoot
45
+ : path.resolve(applicationRoot, configuredDataRoot))
46
+ : path.join(applicationRoot, ".agent");
47
+ const runtimeProfile = configuredProfile ?? "default";
48
+ return {
49
+ applicationRoot,
50
+ dataRoot,
51
+ runtimeProfile,
52
+ };
53
+ }
29
54
  export function getRuntimeResources(refs) {
30
55
  const runtimeDefaults = getRuntimeDefaults(refs);
31
56
  if (!Array.isArray(runtimeDefaults?.resources)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.282",
3
+ "version": "0.0.284",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",