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.
- package/README.md +121 -13
- package/dist/{activities-3xj_fEJK.d.ts → activities-1xrWRrGJ.d.cts} +2 -3
- package/dist/{activities-BzYq6jf7.d.cts → activities-DOViDCTE.d.ts} +2 -3
- package/dist/adapters/thread/anthropic/index.d.cts +5 -6
- package/dist/adapters/thread/anthropic/index.d.ts +5 -6
- package/dist/adapters/thread/anthropic/workflow.d.cts +4 -5
- package/dist/adapters/thread/anthropic/workflow.d.ts +4 -5
- package/dist/adapters/thread/google-genai/index.d.cts +5 -6
- package/dist/adapters/thread/google-genai/index.d.ts +5 -6
- package/dist/adapters/thread/google-genai/workflow.d.cts +4 -5
- package/dist/adapters/thread/google-genai/workflow.d.ts +4 -5
- package/dist/adapters/thread/langchain/index.d.cts +5 -6
- package/dist/adapters/thread/langchain/index.d.ts +5 -6
- package/dist/adapters/thread/langchain/workflow.d.cts +4 -5
- package/dist/adapters/thread/langchain/workflow.d.ts +4 -5
- package/dist/index.cjs +499 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -15
- package/dist/index.d.ts +66 -15
- package/dist/index.js +495 -10
- package/dist/index.js.map +1 -1
- package/dist/{proxy-7e7v8ccg.d.ts → proxy-78nc985d.d.ts} +1 -1
- package/dist/{proxy-CsB8r0RR.d.cts → proxy-Bm2UTiO_.d.cts} +1 -1
- package/dist/{thread-manager-D8C5QvLi.d.ts → thread-manager-07BaYu_z.d.ts} +1 -1
- package/dist/{thread-manager-DdVFl1IY.d.cts → thread-manager-BRE5KkHB.d.cts} +1 -1
- package/dist/{thread-manager-DFJ3sKKU.d.cts → thread-manager-CatBkarc.d.cts} +1 -1
- package/dist/{thread-manager-B5qA4v7V.d.ts → thread-manager-CxbWo7q_.d.ts} +1 -1
- package/dist/types-BkVoEyiH.d.ts +1211 -0
- package/dist/{types-BZ75HpYd.d.ts → types-DAv_SLN8.d.ts} +1 -1
- package/dist/{types-HbjqzyJH.d.cts → types-Dpz2gXLk.d.cts} +1 -1
- package/dist/types-seDYom4M.d.cts +1211 -0
- package/dist/workflow-B4T3la0p.d.cts +750 -0
- package/dist/workflow-DCmaXLZ_.d.ts +750 -0
- package/dist/workflow.cjs +171 -5
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +5 -579
- package/dist/workflow.d.ts +5 -579
- package/dist/workflow.js +170 -7
- package/dist/workflow.js.map +1 -1
- package/package.json +3 -23
- package/src/index.ts +7 -0
- package/src/lib/observability/hooks.ts +117 -0
- package/src/lib/observability/index.ts +13 -0
- package/src/lib/observability/sinks.ts +88 -0
- package/src/lib/sandbox/manager.ts +3 -3
- package/src/lib/session/session-edge-cases.integration.test.ts +1 -0
- package/src/lib/session/session.integration.test.ts +1 -0
- package/src/lib/session/session.ts +63 -0
- package/src/lib/session/types.ts +21 -0
- package/src/lib/state/manager.integration.test.ts +1 -0
- package/src/lib/subagent/handler.ts +17 -0
- package/src/lib/subagent/subagent.integration.test.ts +1 -0
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +2 -0
- package/src/lib/tool-router/router.integration.test.ts +2 -0
- package/src/lib/tool-router/router.ts +24 -2
- package/src/{adapters/sandbox/virtual → lib/virtual-fs}/filesystem.ts +4 -4
- package/src/lib/virtual-fs/index.ts +18 -0
- package/src/lib/virtual-fs/manager.ts +48 -0
- package/src/lib/virtual-fs/proxy.ts +45 -0
- package/src/{adapters/sandbox/virtual → lib/virtual-fs}/types.ts +43 -33
- package/src/{adapters/sandbox/virtual/virtual-sandbox.test.ts → lib/virtual-fs/virtual-fs.test.ts} +15 -130
- package/src/lib/virtual-fs/with-virtual-fs.ts +94 -0
- package/src/workflow.ts +25 -8
- package/tsup.config.ts +0 -2
- package/dist/adapters/sandbox/virtual/index.cjs +0 -487
- package/dist/adapters/sandbox/virtual/index.cjs.map +0 -1
- package/dist/adapters/sandbox/virtual/index.d.cts +0 -90
- package/dist/adapters/sandbox/virtual/index.d.ts +0 -90
- package/dist/adapters/sandbox/virtual/index.js +0 -479
- package/dist/adapters/sandbox/virtual/index.js.map +0 -1
- package/dist/adapters/sandbox/virtual/workflow.cjs +0 -33
- package/dist/adapters/sandbox/virtual/workflow.cjs.map +0 -1
- package/dist/adapters/sandbox/virtual/workflow.d.cts +0 -28
- package/dist/adapters/sandbox/virtual/workflow.d.ts +0 -28
- package/dist/adapters/sandbox/virtual/workflow.js +0 -31
- package/dist/adapters/sandbox/virtual/workflow.js.map +0 -1
- package/dist/queries-DVnukByF.d.cts +0 -44
- package/dist/queries-kjlvsUfz.d.ts +0 -44
- package/dist/types-BclYm5Ic.d.cts +0 -581
- package/dist/types-BclYm5Ic.d.ts +0 -581
- package/dist/types-BgsAwN3L.d.cts +0 -125
- package/dist/types-BtqbM1bO.d.ts +0 -490
- package/dist/types-BuCEZ4dF.d.cts +0 -490
- package/dist/types-yU5AINiP.d.ts +0 -125
- package/src/adapters/sandbox/virtual/index.ts +0 -92
- package/src/adapters/sandbox/virtual/provider.ts +0 -121
- package/src/adapters/sandbox/virtual/proxy.ts +0 -53
- package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +0 -97
- package/src/lib/.env +0 -1
- package/src/tools/bash/.env +0 -1
- /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/mutations.ts +0 -0
- /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/queries.ts +0 -0
- /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
|
-
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
|
2075
|
-
*
|
|
2076
|
-
* // registers:
|
|
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
|