zeitlich 0.2.28 → 0.2.29

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 (93) hide show
  1. package/README.md +121 -13
  2. package/dist/{activities-3xj_fEJK.d.ts → activities-1xrWRrGJ.d.cts} +2 -3
  3. package/dist/{activities-BzYq6jf7.d.cts → activities-DOViDCTE.d.ts} +2 -3
  4. package/dist/adapters/thread/anthropic/index.d.cts +5 -6
  5. package/dist/adapters/thread/anthropic/index.d.ts +5 -6
  6. package/dist/adapters/thread/anthropic/workflow.d.cts +4 -5
  7. package/dist/adapters/thread/anthropic/workflow.d.ts +4 -5
  8. package/dist/adapters/thread/google-genai/index.d.cts +5 -6
  9. package/dist/adapters/thread/google-genai/index.d.ts +5 -6
  10. package/dist/adapters/thread/google-genai/workflow.d.cts +4 -5
  11. package/dist/adapters/thread/google-genai/workflow.d.ts +4 -5
  12. package/dist/adapters/thread/langchain/index.d.cts +5 -6
  13. package/dist/adapters/thread/langchain/index.d.ts +5 -6
  14. package/dist/adapters/thread/langchain/workflow.d.cts +4 -5
  15. package/dist/adapters/thread/langchain/workflow.d.ts +4 -5
  16. package/dist/index.cjs +499 -8
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +66 -15
  19. package/dist/index.d.ts +66 -15
  20. package/dist/index.js +495 -10
  21. package/dist/index.js.map +1 -1
  22. package/dist/{proxy-7e7v8ccg.d.ts → proxy-78nc985d.d.ts} +1 -1
  23. package/dist/{proxy-CsB8r0RR.d.cts → proxy-Bm2UTiO_.d.cts} +1 -1
  24. package/dist/{thread-manager-D8C5QvLi.d.ts → thread-manager-07BaYu_z.d.ts} +1 -1
  25. package/dist/{thread-manager-DdVFl1IY.d.cts → thread-manager-BRE5KkHB.d.cts} +1 -1
  26. package/dist/{thread-manager-DFJ3sKKU.d.cts → thread-manager-CatBkarc.d.cts} +1 -1
  27. package/dist/{thread-manager-B5qA4v7V.d.ts → thread-manager-CxbWo7q_.d.ts} +1 -1
  28. package/dist/types-BkVoEyiH.d.ts +1211 -0
  29. package/dist/{types-BZ75HpYd.d.ts → types-DAv_SLN8.d.ts} +1 -1
  30. package/dist/{types-HbjqzyJH.d.cts → types-Dpz2gXLk.d.cts} +1 -1
  31. package/dist/types-seDYom4M.d.cts +1211 -0
  32. package/dist/workflow-B4T3la0p.d.cts +750 -0
  33. package/dist/workflow-DCmaXLZ_.d.ts +750 -0
  34. package/dist/workflow.cjs +171 -5
  35. package/dist/workflow.cjs.map +1 -1
  36. package/dist/workflow.d.cts +5 -579
  37. package/dist/workflow.d.ts +5 -579
  38. package/dist/workflow.js +170 -7
  39. package/dist/workflow.js.map +1 -1
  40. package/package.json +3 -23
  41. package/src/index.ts +7 -0
  42. package/src/lib/observability/hooks.ts +117 -0
  43. package/src/lib/observability/index.ts +13 -0
  44. package/src/lib/observability/sinks.ts +88 -0
  45. package/src/lib/sandbox/manager.ts +3 -3
  46. package/src/lib/session/session-edge-cases.integration.test.ts +1 -0
  47. package/src/lib/session/session.integration.test.ts +1 -0
  48. package/src/lib/session/session.ts +63 -0
  49. package/src/lib/session/types.ts +21 -0
  50. package/src/lib/state/manager.integration.test.ts +1 -0
  51. package/src/lib/subagent/handler.ts +17 -0
  52. package/src/lib/subagent/subagent.integration.test.ts +1 -0
  53. package/src/lib/tool-router/router-edge-cases.integration.test.ts +2 -0
  54. package/src/lib/tool-router/router.integration.test.ts +2 -0
  55. package/src/lib/tool-router/router.ts +24 -2
  56. package/src/{adapters/sandbox/virtual → lib/virtual-fs}/filesystem.ts +4 -4
  57. package/src/lib/virtual-fs/index.ts +18 -0
  58. package/src/lib/virtual-fs/manager.ts +48 -0
  59. package/src/lib/virtual-fs/proxy.ts +45 -0
  60. package/src/{adapters/sandbox/virtual → lib/virtual-fs}/types.ts +43 -33
  61. package/src/{adapters/sandbox/virtual/virtual-sandbox.test.ts → lib/virtual-fs/virtual-fs.test.ts} +15 -130
  62. package/src/lib/virtual-fs/with-virtual-fs.ts +94 -0
  63. package/src/workflow.ts +25 -8
  64. package/tsup.config.ts +0 -2
  65. package/dist/adapters/sandbox/virtual/index.cjs +0 -487
  66. package/dist/adapters/sandbox/virtual/index.cjs.map +0 -1
  67. package/dist/adapters/sandbox/virtual/index.d.cts +0 -90
  68. package/dist/adapters/sandbox/virtual/index.d.ts +0 -90
  69. package/dist/adapters/sandbox/virtual/index.js +0 -479
  70. package/dist/adapters/sandbox/virtual/index.js.map +0 -1
  71. package/dist/adapters/sandbox/virtual/workflow.cjs +0 -33
  72. package/dist/adapters/sandbox/virtual/workflow.cjs.map +0 -1
  73. package/dist/adapters/sandbox/virtual/workflow.d.cts +0 -28
  74. package/dist/adapters/sandbox/virtual/workflow.d.ts +0 -28
  75. package/dist/adapters/sandbox/virtual/workflow.js +0 -31
  76. package/dist/adapters/sandbox/virtual/workflow.js.map +0 -1
  77. package/dist/queries-DVnukByF.d.cts +0 -44
  78. package/dist/queries-kjlvsUfz.d.ts +0 -44
  79. package/dist/types-BclYm5Ic.d.cts +0 -581
  80. package/dist/types-BclYm5Ic.d.ts +0 -581
  81. package/dist/types-BgsAwN3L.d.cts +0 -125
  82. package/dist/types-BtqbM1bO.d.ts +0 -490
  83. package/dist/types-BuCEZ4dF.d.cts +0 -490
  84. package/dist/types-yU5AINiP.d.ts +0 -125
  85. package/src/adapters/sandbox/virtual/index.ts +0 -92
  86. package/src/adapters/sandbox/virtual/provider.ts +0 -121
  87. package/src/adapters/sandbox/virtual/proxy.ts +0 -53
  88. package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +0 -97
  89. package/src/lib/.env +0 -1
  90. package/src/tools/bash/.env +0 -1
  91. /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/mutations.ts +0 -0
  92. /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/queries.ts +0 -0
  93. /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/tree.ts +0 -0
package/dist/index.cjs CHANGED
@@ -135,6 +135,11 @@ function createToolRouter(options) {
135
135
  return null;
136
136
  }
137
137
  const effectiveArgs = preResult.args;
138
+ workflow.log.debug("tool call dispatched", {
139
+ toolName: toolCall.name,
140
+ toolCallId: toolCall.id,
141
+ turn
142
+ });
138
143
  let result;
139
144
  let content;
140
145
  let resultAppended = false;
@@ -161,6 +166,13 @@ function createToolRouter(options) {
161
166
  content = JSON.stringify(result, null, 2);
162
167
  }
163
168
  } catch (error) {
169
+ workflow.log.warn("tool call failed", {
170
+ toolName: toolCall.name,
171
+ toolCallId: toolCall.id,
172
+ turn,
173
+ durationMs: Date.now() - startTime,
174
+ error: error instanceof Error ? error.message : String(error)
175
+ });
164
176
  const recovery = await runFailureHooks(
165
177
  toolCall,
166
178
  tool,
@@ -186,19 +198,26 @@ function createToolRouter(options) {
186
198
  [workflow.uuid4(), config]
187
199
  );
188
200
  }
201
+ const durationMs = Date.now() - startTime;
189
202
  const toolResult = {
190
203
  toolCallId: toolCall.id,
191
204
  name: toolCall.name,
192
205
  data: result,
193
206
  ...metadata && { metadata }
194
207
  };
208
+ workflow.log.debug("tool call completed", {
209
+ toolName: toolCall.name,
210
+ toolCallId: toolCall.id,
211
+ turn,
212
+ durationMs
213
+ });
195
214
  await runPostHooks(
196
215
  toolCall,
197
216
  tool,
198
217
  toolResult,
199
218
  effectiveArgs,
200
219
  turn,
201
- Date.now() - startTime
220
+ durationMs
202
221
  );
203
222
  return toolResult;
204
223
  }
@@ -441,6 +460,12 @@ function createSubagentHandler(subagents, options) {
441
460
  args: resolvedContext === void 0 ? [args.prompt, workflowInput] : [args.prompt, workflowInput, resolvedContext],
442
461
  taskQueue: config.taskQueue ?? parentTaskQueue
443
462
  };
463
+ workflow.log.info("subagent spawned", {
464
+ subagent: config.agentName,
465
+ childWorkflowId,
466
+ threadMode,
467
+ sandboxSource: sandboxCfg.source
468
+ });
444
469
  const childHandle = await workflow.startChild(config.workflow, childOpts);
445
470
  const effectiveShutdown = sandboxCfg.shutdown ?? "destroy";
446
471
  const shouldDeferDestroy = effectiveShutdown === "pause-until-parent-close" && (sandboxCfg.source === "own" || allowsContinuation && sandboxCfg.source !== "inherit");
@@ -457,11 +482,20 @@ function createSubagentHandler(subagents, options) {
457
482
  const childResult = childResults.get(childWorkflowId);
458
483
  childResults.delete(childWorkflowId);
459
484
  if (!childResult) {
485
+ workflow.log.warn("subagent returned no result", {
486
+ subagent: config.agentName,
487
+ childWorkflowId
488
+ });
460
489
  return {
461
490
  toolResponse: "Subagent workflow did not signal a result",
462
491
  data: null
463
492
  };
464
493
  }
494
+ workflow.log.info("subagent completed", {
495
+ subagent: config.agentName,
496
+ childWorkflowId,
497
+ ...childResult.usage && { usage: childResult.usage }
498
+ });
465
499
  const {
466
500
  toolResponse,
467
501
  data,
@@ -678,7 +712,8 @@ async function createSession({
678
712
  sandboxOps,
679
713
  thread: threadInit,
680
714
  sandbox: sandboxInit,
681
- sandboxShutdown = "destroy"
715
+ sandboxShutdown = "destroy",
716
+ virtualFs: virtualFsConfig
682
717
  }) {
683
718
  const threadMode = threadInit?.mode ?? "new";
684
719
  let threadId;
@@ -818,6 +853,17 @@ async function createSession({
818
853
  stateManager.mergeUpdate(result.stateUpdate);
819
854
  }
820
855
  }
856
+ if (virtualFsConfig) {
857
+ const result = await virtualFsConfig.ops.resolveFileTree(
858
+ virtualFsConfig.resolverContext
859
+ );
860
+ stateManager.mergeUpdate({
861
+ fileTree: result.fileTree,
862
+ resolverContext: virtualFsConfig.resolverContext,
863
+ workspaceBase: virtualFsConfig.workspaceBase ?? "/",
864
+ ...result.stateUpdate
865
+ });
866
+ }
821
867
  if (hooks.onSessionStart) {
822
868
  await hooks.onSessionStart({
823
869
  threadId,
@@ -825,6 +871,14 @@ async function createSession({
825
871
  metadata
826
872
  });
827
873
  }
874
+ workflow.log.info("session started", {
875
+ agentName,
876
+ threadId,
877
+ threadMode,
878
+ maxTurns,
879
+ ...sandboxId && { sandboxId }
880
+ });
881
+ const sessionStartMs = Date.now();
828
882
  const systemPrompt = stateManager.getSystemPrompt();
829
883
  if (threadMode === "fork" && sourceThreadId) {
830
884
  await forkThread(sourceThreadId, threadId, threadKey);
@@ -847,6 +901,7 @@ async function createSession({
847
901
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
848
902
  stateManager.incrementTurns();
849
903
  const currentTurn = stateManager.getTurns();
904
+ workflow.log.debug("turn started", { agentName, threadId, turn: currentTurn });
850
905
  stateManager.setTools(toolRouter.getToolDefinitions());
851
906
  const { message, rawToolCalls, usage } = await runAgent({
852
907
  threadId,
@@ -857,9 +912,24 @@ async function createSession({
857
912
  if (usage) {
858
913
  stateManager.updateUsage(usage);
859
914
  }
915
+ workflow.log.debug("model response received", {
916
+ agentName,
917
+ threadId,
918
+ turn: currentTurn,
919
+ toolCallCount: rawToolCalls.length,
920
+ ...usage && { usage }
921
+ });
860
922
  if (!toolRouter.hasTools() || rawToolCalls.length === 0) {
861
923
  stateManager.complete();
862
924
  exitReason = "completed";
925
+ workflow.log.info("session ended", {
926
+ agentName,
927
+ threadId,
928
+ exitReason,
929
+ turns: currentTurn,
930
+ durationMs: Date.now() - sessionStartMs,
931
+ usage: stateManager.getTotalUsage()
932
+ });
863
933
  return {
864
934
  threadId,
865
935
  finalMessage: message,
@@ -911,9 +981,21 @@ async function createSession({
911
981
  }
912
982
  if (stateManager.getTurns() >= maxTurns && stateManager.isRunning()) {
913
983
  exitReason = "max_turns";
984
+ workflow.log.warn("session hit max turns", {
985
+ agentName,
986
+ threadId,
987
+ maxTurns
988
+ });
914
989
  }
915
990
  } catch (error) {
916
991
  exitReason = "failed";
992
+ workflow.log.error("session failed", {
993
+ agentName,
994
+ threadId,
995
+ turns: stateManager.getTurns(),
996
+ durationMs: Date.now() - sessionStartMs,
997
+ error: error instanceof Error ? error.message : String(error)
998
+ });
917
999
  throw workflow.ApplicationFailure.fromError(error);
918
1000
  } finally {
919
1001
  await callSessionEnd(exitReason, stateManager.getTurns());
@@ -932,6 +1014,14 @@ async function createSession({
932
1014
  await destroySubagentSandboxes();
933
1015
  }
934
1016
  }
1017
+ workflow.log.info("session ended", {
1018
+ agentName,
1019
+ threadId,
1020
+ exitReason,
1021
+ turns: stateManager.getTurns(),
1022
+ durationMs: Date.now() - sessionStartMs,
1023
+ usage: stateManager.getTotalUsage()
1024
+ });
935
1025
  return {
936
1026
  threadId,
937
1027
  finalMessage: null,
@@ -1269,6 +1359,60 @@ function defineSubagentWorkflow(config, fn) {
1269
1359
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1270
1360
  });
1271
1361
  }
1362
+ function createObservabilityHooks(agentName) {
1363
+ const { zeitlichMetrics } = workflow.proxySinks();
1364
+ let sessionStartMs = Date.now();
1365
+ return {
1366
+ onSessionStart: (ctx) => {
1367
+ sessionStartMs = Date.now();
1368
+ zeitlichMetrics.sessionStarted({
1369
+ agentName,
1370
+ threadId: ctx.threadId,
1371
+ metadata: ctx.metadata
1372
+ });
1373
+ },
1374
+ onSessionEnd: (ctx) => {
1375
+ zeitlichMetrics.sessionEnded({
1376
+ agentName,
1377
+ threadId: ctx.threadId,
1378
+ exitReason: ctx.exitReason,
1379
+ turns: ctx.turns,
1380
+ usage: {},
1381
+ durationMs: Date.now() - sessionStartMs
1382
+ });
1383
+ },
1384
+ onPostToolUse: (ctx) => {
1385
+ zeitlichMetrics.toolExecuted({
1386
+ agentName,
1387
+ toolName: ctx.toolCall.name,
1388
+ durationMs: ctx.durationMs,
1389
+ success: true,
1390
+ threadId: ctx.threadId,
1391
+ turn: ctx.turn
1392
+ });
1393
+ },
1394
+ onPostToolUseFailure: (ctx) => {
1395
+ zeitlichMetrics.toolExecuted({
1396
+ agentName,
1397
+ toolName: ctx.toolCall.name,
1398
+ durationMs: 0,
1399
+ success: false,
1400
+ threadId: ctx.threadId,
1401
+ turn: ctx.turn
1402
+ });
1403
+ return {};
1404
+ }
1405
+ };
1406
+ }
1407
+ function composeHooks(...fns) {
1408
+ return async (...args) => {
1409
+ let lastResult;
1410
+ for (const fn of fns) {
1411
+ lastResult = await fn(...args);
1412
+ }
1413
+ return lastResult;
1414
+ };
1415
+ }
1272
1416
  var SandboxNotSupportedError = class extends common.ApplicationFailure {
1273
1417
  constructor(operation) {
1274
1418
  super(
@@ -1284,7 +1428,7 @@ var SandboxNotFoundError = class extends common.ApplicationFailure {
1284
1428
  }
1285
1429
  };
1286
1430
 
1287
- // src/adapters/sandbox/virtual/mutations.ts
1431
+ // src/lib/virtual-fs/mutations.ts
1288
1432
  function applyVirtualTreeMutations(stateManager, mutations) {
1289
1433
  let tree = [...stateManager.get("fileTree")];
1290
1434
  for (const m of mutations) {
@@ -1306,7 +1450,7 @@ function applyVirtualTreeMutations(stateManager, mutations) {
1306
1450
  return tree;
1307
1451
  }
1308
1452
 
1309
- // src/adapters/sandbox/virtual/tree.ts
1453
+ // src/lib/virtual-fs/tree.ts
1310
1454
  var buildTree = (entries) => {
1311
1455
  const root = { name: "/", children: /* @__PURE__ */ new Map(), isFile: false };
1312
1456
  for (const entry of entries) {
@@ -1354,7 +1498,7 @@ function formatVirtualFileTree(entries, opts = {}) {
1354
1498
  return "/" + printNode(root, "", sort);
1355
1499
  }
1356
1500
 
1357
- // src/adapters/sandbox/virtual/queries.ts
1501
+ // src/lib/virtual-fs/queries.ts
1358
1502
  function hasFileWithMimeType(stateManager, pattern) {
1359
1503
  const tree = stateManager.get("fileTree");
1360
1504
  const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(buildMatcher);
@@ -1395,6 +1539,25 @@ function buildGlobMatcher(pattern) {
1395
1539
  );
1396
1540
  return (v) => re.test(v);
1397
1541
  }
1542
+ function proxyVirtualFsOps(scope, options) {
1543
+ const resolvedScope = scope ?? workflow.workflowInfo().workflowType;
1544
+ const acts = workflow.proxyActivities(
1545
+ options ?? {
1546
+ startToCloseTimeout: "30s",
1547
+ retry: {
1548
+ maximumAttempts: 3,
1549
+ initialInterval: "2s",
1550
+ maximumInterval: "30s",
1551
+ backoffCoefficient: 2
1552
+ }
1553
+ }
1554
+ );
1555
+ const prefix = resolvedScope;
1556
+ const p = (key) => `${prefix}${key.charAt(0).toUpperCase()}${key.slice(1)}`;
1557
+ return {
1558
+ resolveFileTree: acts[p("resolveFileTree")]
1559
+ };
1560
+ }
1398
1561
 
1399
1562
  // src/lib/skills/parse.ts
1400
1563
  function parseSkillFile(raw) {
@@ -2071,9 +2234,9 @@ var SandboxManager = class {
2071
2234
  * manager.createActivities("CodingAgent");
2072
2235
  * // registers: inMemoryCodingAgentCreateSandbox, inMemoryCodingAgentDestroySandbox, …
2073
2236
  *
2074
- * const vmgr = new SandboxManager(new VirtualSandboxProvider(resolver));
2075
- * vmgr.createActivities("CodingAgent");
2076
- * // registers: virtualCodingAgentCreateSandbox, …
2237
+ * const dmgr = new SandboxManager(new DaytonaSandboxProvider(config));
2238
+ * dmgr.createActivities("CodingAgent");
2239
+ * // registers: daytonaCodingAgentCreateSandbox, …
2077
2240
  * ```
2078
2241
  */
2079
2242
  createActivities(scope) {
@@ -2170,6 +2333,328 @@ var NodeFsSandboxFileSystem = class {
2170
2333
  return path.posix.resolve(base, path$1);
2171
2334
  }
2172
2335
  };
2336
+ function normalisePath(p, workspaceBase = "/") {
2337
+ return path.posix.resolve(workspaceBase, p);
2338
+ }
2339
+ function parentDir(p) {
2340
+ const idx = p.lastIndexOf("/");
2341
+ return idx <= 0 ? "/" : p.slice(0, idx);
2342
+ }
2343
+ function inferDirectories(entries, workspaceBase) {
2344
+ const dirs = /* @__PURE__ */ new Set();
2345
+ dirs.add("/");
2346
+ for (const entry of entries) {
2347
+ let dir = parentDir(normalisePath(entry.path, workspaceBase));
2348
+ while (dir !== "/" && !dirs.has(dir)) {
2349
+ dirs.add(dir);
2350
+ dir = parentDir(dir);
2351
+ }
2352
+ dirs.add("/");
2353
+ }
2354
+ return dirs;
2355
+ }
2356
+ var VirtualFileSystem = class {
2357
+ constructor(tree, resolver, ctx, workspaceBase = "/") {
2358
+ this.resolver = resolver;
2359
+ this.ctx = ctx;
2360
+ this.workspaceBase = normalisePath(workspaceBase);
2361
+ this.entries = new Map(
2362
+ tree.map((e) => [normalisePath(e.path, this.workspaceBase), e])
2363
+ );
2364
+ this.directories = inferDirectories(tree, this.workspaceBase);
2365
+ }
2366
+ workspaceBase;
2367
+ entries;
2368
+ directories;
2369
+ mutations = [];
2370
+ /** Return all mutations accumulated during this invocation. */
2371
+ getMutations() {
2372
+ return this.mutations;
2373
+ }
2374
+ /** Look up a file entry by virtual path. */
2375
+ getEntry(path) {
2376
+ return this.entries.get(normalisePath(path, this.workspaceBase));
2377
+ }
2378
+ // --------------------------------------------------------------------------
2379
+ // Read operations — delegate to resolver lazily
2380
+ // --------------------------------------------------------------------------
2381
+ async readFile(path) {
2382
+ const norm = normalisePath(path, this.workspaceBase);
2383
+ const entry = this.entries.get(norm);
2384
+ if (!entry) throw new Error(`ENOENT: no such file: ${path}`);
2385
+ return this.resolver.readFile(entry.id, this.ctx, entry.metadata);
2386
+ }
2387
+ async readFileBuffer(path) {
2388
+ const norm = normalisePath(path, this.workspaceBase);
2389
+ const entry = this.entries.get(norm);
2390
+ if (!entry) throw new Error(`ENOENT: no such file: ${path}`);
2391
+ return this.resolver.readFileBuffer(entry.id, this.ctx, entry.metadata);
2392
+ }
2393
+ // --------------------------------------------------------------------------
2394
+ // Metadata operations — pure, resolved from the tree
2395
+ // --------------------------------------------------------------------------
2396
+ async exists(path) {
2397
+ const norm = normalisePath(path, this.workspaceBase);
2398
+ return this.entries.has(norm) || this.directories.has(norm);
2399
+ }
2400
+ async stat(path) {
2401
+ const norm = normalisePath(path, this.workspaceBase);
2402
+ const entry = this.entries.get(norm);
2403
+ if (entry) {
2404
+ return {
2405
+ isFile: true,
2406
+ isDirectory: false,
2407
+ isSymbolicLink: false,
2408
+ size: entry.size,
2409
+ mtime: new Date(entry.mtime)
2410
+ };
2411
+ }
2412
+ if (this.directories.has(norm)) {
2413
+ return {
2414
+ isFile: false,
2415
+ isDirectory: true,
2416
+ isSymbolicLink: false,
2417
+ size: 0,
2418
+ mtime: /* @__PURE__ */ new Date()
2419
+ };
2420
+ }
2421
+ throw new Error(`ENOENT: no such file or directory: ${path}`);
2422
+ }
2423
+ async readdir(path) {
2424
+ const norm = normalisePath(path, this.workspaceBase);
2425
+ if (!this.directories.has(norm)) {
2426
+ throw new Error(`ENOENT: no such directory: ${path}`);
2427
+ }
2428
+ const prefix = norm === "/" ? "/" : norm + "/";
2429
+ const names = /* @__PURE__ */ new Set();
2430
+ for (const p of this.entries.keys()) {
2431
+ if (p.startsWith(prefix)) {
2432
+ const rest = p.slice(prefix.length);
2433
+ const seg = rest.split("/")[0];
2434
+ if (seg) names.add(seg);
2435
+ }
2436
+ }
2437
+ for (const d of this.directories) {
2438
+ if (d.startsWith(prefix) && d !== norm) {
2439
+ const rest = d.slice(prefix.length);
2440
+ const seg = rest.split("/")[0];
2441
+ if (seg) names.add(seg);
2442
+ }
2443
+ }
2444
+ return [...names].sort();
2445
+ }
2446
+ async readdirWithFileTypes(path) {
2447
+ const names = await this.readdir(path);
2448
+ const norm = normalisePath(path, this.workspaceBase);
2449
+ const prefix = norm === "/" ? "/" : norm + "/";
2450
+ return names.map((name) => {
2451
+ const full = prefix + name;
2452
+ const isFile = this.entries.has(full);
2453
+ const isDirectory = this.directories.has(full);
2454
+ return { name, isFile, isDirectory, isSymbolicLink: false };
2455
+ });
2456
+ }
2457
+ // --------------------------------------------------------------------------
2458
+ // Write operations — delegate to resolver, record mutations
2459
+ // --------------------------------------------------------------------------
2460
+ async writeFile(path, content) {
2461
+ const norm = normalisePath(path, this.workspaceBase);
2462
+ const existing = this.entries.get(norm);
2463
+ if (existing) {
2464
+ await this.resolver.writeFile(
2465
+ existing.id,
2466
+ content,
2467
+ this.ctx,
2468
+ existing.metadata
2469
+ );
2470
+ const size = typeof content === "string" ? new TextEncoder().encode(content).byteLength : content.byteLength;
2471
+ const updated = {
2472
+ ...existing,
2473
+ size,
2474
+ mtime: (/* @__PURE__ */ new Date()).toISOString()
2475
+ };
2476
+ this.entries.set(norm, updated);
2477
+ this.mutations.push({ type: "update", path: norm, entry: updated });
2478
+ } else {
2479
+ const entry = await this.resolver.createFile(norm, content, this.ctx);
2480
+ const normalised = { ...entry, path: norm };
2481
+ this.entries.set(norm, normalised);
2482
+ this.addParentDirectories(norm);
2483
+ this.mutations.push({ type: "add", entry: normalised });
2484
+ }
2485
+ }
2486
+ async appendFile(path, content) {
2487
+ const norm = normalisePath(path, this.workspaceBase);
2488
+ const existing = this.entries.get(norm);
2489
+ if (!existing) {
2490
+ return this.writeFile(path, content);
2491
+ }
2492
+ const current = await this.resolver.readFile(
2493
+ existing.id,
2494
+ this.ctx,
2495
+ existing.metadata
2496
+ );
2497
+ const appended = typeof content === "string" ? current + content : current + new TextDecoder().decode(content);
2498
+ await this.resolver.writeFile(
2499
+ existing.id,
2500
+ appended,
2501
+ this.ctx,
2502
+ existing.metadata
2503
+ );
2504
+ const size = new TextEncoder().encode(appended).byteLength;
2505
+ const updated = {
2506
+ ...existing,
2507
+ size,
2508
+ mtime: (/* @__PURE__ */ new Date()).toISOString()
2509
+ };
2510
+ this.entries.set(norm, updated);
2511
+ this.mutations.push({ type: "update", path: norm, entry: updated });
2512
+ }
2513
+ async mkdir(_path, _options) {
2514
+ const norm = normalisePath(_path, this.workspaceBase);
2515
+ if (this.directories.has(norm)) return;
2516
+ if (_options?.recursive) {
2517
+ this.addParentDirectories(norm + "/placeholder");
2518
+ this.directories.add(norm);
2519
+ } else {
2520
+ const parent = parentDir(norm);
2521
+ if (!this.directories.has(parent)) {
2522
+ throw new Error(`ENOENT: no such directory: ${parent}`);
2523
+ }
2524
+ this.directories.add(norm);
2525
+ }
2526
+ }
2527
+ async rm(path, options) {
2528
+ const norm = normalisePath(path, this.workspaceBase);
2529
+ const entry = this.entries.get(norm);
2530
+ if (entry) {
2531
+ await this.resolver.deleteFile(entry.id, this.ctx, entry.metadata);
2532
+ this.entries.delete(norm);
2533
+ this.mutations.push({ type: "remove", path: norm });
2534
+ return;
2535
+ }
2536
+ if (this.directories.has(norm)) {
2537
+ if (!options?.recursive) {
2538
+ throw new Error(`EISDIR: is a directory (use recursive): ${path}`);
2539
+ }
2540
+ const prefix = norm === "/" ? "/" : norm + "/";
2541
+ for (const [p, e] of this.entries) {
2542
+ if (p.startsWith(prefix)) {
2543
+ await this.resolver.deleteFile(e.id, this.ctx, e.metadata);
2544
+ this.entries.delete(p);
2545
+ this.mutations.push({ type: "remove", path: p });
2546
+ }
2547
+ }
2548
+ for (const d of this.directories) {
2549
+ if (d.startsWith(prefix)) this.directories.delete(d);
2550
+ }
2551
+ this.directories.delete(norm);
2552
+ return;
2553
+ }
2554
+ if (!options?.force) {
2555
+ throw new Error(`ENOENT: no such file or directory: ${path}`);
2556
+ }
2557
+ }
2558
+ async cp(src, dest, _options) {
2559
+ const normSrc = normalisePath(src, this.workspaceBase);
2560
+ const normDest = normalisePath(dest, this.workspaceBase);
2561
+ const entry = this.entries.get(normSrc);
2562
+ if (entry) {
2563
+ const content = await this.resolver.readFile(
2564
+ entry.id,
2565
+ this.ctx,
2566
+ entry.metadata
2567
+ );
2568
+ await this.writeFile(normDest, content);
2569
+ return;
2570
+ }
2571
+ if (!this.directories.has(normSrc)) {
2572
+ throw new Error(`ENOENT: no such file or directory: ${src}`);
2573
+ }
2574
+ if (!_options?.recursive) {
2575
+ throw new Error(`EISDIR: is a directory (use recursive): ${src}`);
2576
+ }
2577
+ const prefix = normSrc === "/" ? "/" : normSrc + "/";
2578
+ for (const [p, e] of this.entries) {
2579
+ if (p.startsWith(prefix)) {
2580
+ const relative = p.slice(normSrc.length);
2581
+ const content = await this.resolver.readFile(
2582
+ e.id,
2583
+ this.ctx,
2584
+ e.metadata
2585
+ );
2586
+ await this.writeFile(normDest + relative, content);
2587
+ }
2588
+ }
2589
+ }
2590
+ async mv(src, dest) {
2591
+ await this.cp(src, dest, { recursive: true });
2592
+ await this.rm(src, { recursive: true });
2593
+ }
2594
+ // --------------------------------------------------------------------------
2595
+ // Unsupported
2596
+ // --------------------------------------------------------------------------
2597
+ async readlink(_path) {
2598
+ throw new SandboxNotSupportedError("readlink");
2599
+ }
2600
+ resolvePath(base, path$1) {
2601
+ return path.posix.resolve(normalisePath(base, this.workspaceBase), path$1);
2602
+ }
2603
+ // --------------------------------------------------------------------------
2604
+ // Helpers
2605
+ // --------------------------------------------------------------------------
2606
+ addParentDirectories(filePath) {
2607
+ let dir = parentDir(normalisePath(filePath, this.workspaceBase));
2608
+ while (!this.directories.has(dir)) {
2609
+ this.directories.add(dir);
2610
+ dir = parentDir(dir);
2611
+ }
2612
+ }
2613
+ };
2614
+
2615
+ // src/lib/virtual-fs/with-virtual-fs.ts
2616
+ function withVirtualFs(client, resolver, handler) {
2617
+ return async (args, context) => {
2618
+ const state = await queryParentWorkflowState(client);
2619
+ const { fileTree, resolverContext, workspaceBase } = state;
2620
+ if (!fileTree) {
2621
+ return {
2622
+ toolResponse: `Error: No fileTree in agent state. The ${context.toolName} tool requires a virtual filesystem.`,
2623
+ data: null
2624
+ };
2625
+ }
2626
+ const virtualFs = new VirtualFileSystem(
2627
+ fileTree,
2628
+ resolver,
2629
+ resolverContext,
2630
+ workspaceBase ?? "/"
2631
+ );
2632
+ const response = await handler(args, { ...context, virtualFs });
2633
+ const mutations = virtualFs.getMutations();
2634
+ return {
2635
+ toolResponse: response.toolResponse,
2636
+ data: {
2637
+ ...response.data ?? {},
2638
+ treeMutations: mutations
2639
+ }
2640
+ };
2641
+ };
2642
+ }
2643
+
2644
+ // src/lib/virtual-fs/manager.ts
2645
+ function createVirtualFsActivities(resolver, scope) {
2646
+ const ops = {
2647
+ resolveFileTree: async (ctx) => {
2648
+ const fileTree = await resolver.resolveEntries(ctx);
2649
+ return { fileTree };
2650
+ }
2651
+ };
2652
+ const prefix = scope;
2653
+ const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
2654
+ return Object.fromEntries(
2655
+ Object.entries(ops).map(([k, v]) => [`${prefix}${cap(k)}`, v])
2656
+ );
2657
+ }
2173
2658
 
2174
2659
  // src/tools/bash/handler.ts
2175
2660
  var bashHandler = async (args, { sandbox }) => {
@@ -2430,13 +2915,16 @@ exports.NodeFsSandboxFileSystem = NodeFsSandboxFileSystem;
2430
2915
  exports.SandboxManager = SandboxManager;
2431
2916
  exports.SandboxNotFoundError = SandboxNotFoundError;
2432
2917
  exports.SandboxNotSupportedError = SandboxNotSupportedError;
2918
+ exports.VirtualFileSystem = VirtualFileSystem;
2433
2919
  exports.applyVirtualTreeMutations = applyVirtualTreeMutations;
2434
2920
  exports.askUserQuestionTool = askUserQuestionTool;
2435
2921
  exports.bashHandler = bashHandler;
2436
2922
  exports.bashTool = bashTool;
2923
+ exports.composeHooks = composeHooks;
2437
2924
  exports.createAgentStateManager = createAgentStateManager;
2438
2925
  exports.createAskUserQuestionHandler = createAskUserQuestionHandler;
2439
2926
  exports.createBashToolDescription = createBashToolDescription;
2927
+ exports.createObservabilityHooks = createObservabilityHooks;
2440
2928
  exports.createReadSkillHandler = createReadSkillHandler;
2441
2929
  exports.createReadSkillTool = createReadSkillTool;
2442
2930
  exports.createRunAgentActivity = createRunAgentActivity;
@@ -2447,6 +2935,7 @@ exports.createTaskListHandler = createTaskListHandler;
2447
2935
  exports.createTaskUpdateHandler = createTaskUpdateHandler;
2448
2936
  exports.createThreadManager = createThreadManager;
2449
2937
  exports.createToolRouter = createToolRouter;
2938
+ exports.createVirtualFsActivities = createVirtualFsActivities;
2450
2939
  exports.defineSubagent = defineSubagent;
2451
2940
  exports.defineSubagentWorkflow = defineSubagentWorkflow;
2452
2941
  exports.defineTool = defineTool;
@@ -2464,6 +2953,7 @@ exports.hasFileWithMimeType = hasFileWithMimeType;
2464
2953
  exports.hasNoOtherToolCalls = hasNoOtherToolCalls;
2465
2954
  exports.isTerminalStatus = isTerminalStatus;
2466
2955
  exports.parseSkillFile = parseSkillFile;
2956
+ exports.proxyVirtualFsOps = proxyVirtualFsOps;
2467
2957
  exports.queryParentWorkflowState = queryParentWorkflowState;
2468
2958
  exports.readFileHandler = readFileHandler;
2469
2959
  exports.readFileTool = readFileTool;
@@ -2475,6 +2965,7 @@ exports.toTree = toTree;
2475
2965
  exports.withAutoAppend = withAutoAppend;
2476
2966
  exports.withParentWorkflowState = withParentWorkflowState;
2477
2967
  exports.withSandbox = withSandbox;
2968
+ exports.withVirtualFs = withVirtualFs;
2478
2969
  exports.writeFileHandler = writeFileHandler;
2479
2970
  exports.writeFileTool = writeFileTool;
2480
2971
  //# sourceMappingURL=index.cjs.map