zeitlich 0.2.36 → 0.2.38
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 +146 -92
- package/dist/{activities-BVI2lTwr.d.ts → activities-BKhMtKDd.d.ts} +4 -2
- package/dist/{activities-hd4aNnZE.d.cts → activities-CDcwkRZs.d.cts} +4 -2
- package/dist/adapters/sandbox/bedrock/index.cjs +17 -14
- package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
- package/dist/adapters/sandbox/bedrock/index.d.cts +7 -6
- package/dist/adapters/sandbox/bedrock/index.d.ts +7 -6
- package/dist/adapters/sandbox/bedrock/index.js +17 -14
- package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.cjs +2 -0
- package/dist/adapters/sandbox/bedrock/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.d.cts +2 -2
- package/dist/adapters/sandbox/bedrock/workflow.d.ts +2 -2
- package/dist/adapters/sandbox/bedrock/workflow.js +2 -0
- package/dist/adapters/sandbox/bedrock/workflow.js.map +1 -1
- package/dist/adapters/sandbox/daytona/index.cjs +11 -3
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/index.d.cts +5 -4
- package/dist/adapters/sandbox/daytona/index.d.ts +5 -4
- package/dist/adapters/sandbox/daytona/index.js +11 -3
- package/dist/adapters/sandbox/daytona/index.js.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.cjs +2 -0
- package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/daytona/workflow.js +2 -0
- package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
- package/dist/adapters/sandbox/e2b/index.cjs +73 -12
- package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
- package/dist/adapters/sandbox/e2b/index.d.cts +26 -4
- package/dist/adapters/sandbox/e2b/index.d.ts +26 -4
- package/dist/adapters/sandbox/e2b/index.js +73 -12
- package/dist/adapters/sandbox/e2b/index.js.map +1 -1
- package/dist/adapters/sandbox/e2b/workflow.cjs +2 -0
- package/dist/adapters/sandbox/e2b/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/e2b/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/e2b/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/e2b/workflow.js +2 -0
- package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.cjs +8 -3
- package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.d.cts +5 -4
- package/dist/adapters/sandbox/inmemory/index.d.ts +5 -4
- package/dist/adapters/sandbox/inmemory/index.js +8 -3
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.cjs +2 -0
- package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.js +2 -0
- package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
- package/dist/adapters/thread/anthropic/index.cjs +94 -39
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +5 -5
- package/dist/adapters/thread/anthropic/index.d.ts +5 -5
- package/dist/adapters/thread/anthropic/index.js +94 -39
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.cjs +7 -2
- package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.d.cts +5 -5
- package/dist/adapters/thread/anthropic/workflow.d.ts +5 -5
- package/dist/adapters/thread/anthropic/workflow.js +7 -2
- package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
- package/dist/adapters/thread/google-genai/index.cjs +77 -28
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +5 -5
- package/dist/adapters/thread/google-genai/index.d.ts +5 -5
- package/dist/adapters/thread/google-genai/index.js +77 -28
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.cjs +7 -2
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +5 -5
- package/dist/adapters/thread/google-genai/workflow.d.ts +5 -5
- package/dist/adapters/thread/google-genai/workflow.js +7 -2
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
- package/dist/adapters/thread/langchain/index.cjs +57 -10
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +5 -5
- package/dist/adapters/thread/langchain/index.d.ts +5 -5
- package/dist/adapters/thread/langchain/index.js +57 -10
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +7 -2
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +5 -5
- package/dist/adapters/thread/langchain/workflow.d.ts +5 -5
- package/dist/adapters/thread/langchain/workflow.js +7 -2
- package/dist/adapters/thread/langchain/workflow.js.map +1 -1
- package/dist/index.cjs +322 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -14
- package/dist/index.d.ts +20 -14
- package/dist/index.js +323 -147
- package/dist/index.js.map +1 -1
- package/dist/{proxy-BjdFGPTm.d.ts → proxy-CUlKSvZS.d.ts} +1 -1
- package/dist/{proxy-7RnVaPdJ.d.cts → proxy-D_3x7RN4.d.cts} +1 -1
- package/dist/{thread-manager-CbpiGq1L.d.ts → thread-manager-CVu7o2cs.d.ts} +4 -2
- package/dist/{thread-manager-DzXm9eeI.d.cts → thread-manager-HSwyh28L.d.cts} +4 -2
- package/dist/{thread-manager-BBzNgQWH.d.cts → thread-manager-c1gPopAG.d.ts} +4 -2
- package/dist/{thread-manager-DjN5JYul.d.ts → thread-manager-wGi-LqIP.d.cts} +4 -2
- package/dist/{types-Mc_4BCfT.d.cts → types-BH_IRryz.d.ts} +10 -1
- package/dist/{types-yiXmqedU.d.ts → types-BaOw4hKI.d.cts} +10 -1
- package/dist/{types-DQ1l_gXL.d.cts → types-C06FwR96.d.cts} +121 -17
- package/dist/{types-wiGLvxWf.d.ts → types-DAsQ21Rt.d.ts} +1 -1
- package/dist/{types-CADc5V_P.d.ts → types-DNr31FzL.d.ts} +121 -17
- package/dist/{types-CBH54cwr.d.cts → types-lm8tMNJQ.d.cts} +1 -1
- package/dist/{types-DxCpFNv_.d.cts → types-yx0LzPGn.d.cts} +44 -5
- package/dist/{types-DxCpFNv_.d.ts → types-yx0LzPGn.d.ts} +44 -5
- package/dist/{workflow-DhtWRovz.d.cts → workflow-CSCkpwAL.d.ts} +2 -2
- package/dist/{workflow-P2pTSfKu.d.ts → workflow-DuvMZ8Vm.d.cts} +2 -2
- package/dist/workflow.cjs +274 -130
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +3 -3
- package/dist/workflow.d.ts +3 -3
- package/dist/workflow.js +275 -131
- package/dist/workflow.js.map +1 -1
- package/package.json +2 -2
- package/src/adapters/sandbox/bedrock/filesystem.ts +6 -12
- package/src/adapters/sandbox/bedrock/index.ts +22 -11
- package/src/adapters/sandbox/bedrock/proxy.ts +2 -0
- package/src/adapters/sandbox/daytona/index.ts +18 -3
- package/src/adapters/sandbox/daytona/proxy.ts +2 -0
- package/src/adapters/sandbox/e2b/filesystem.ts +5 -4
- package/src/adapters/sandbox/e2b/index.ts +87 -14
- package/src/adapters/sandbox/e2b/proxy.ts +2 -0
- package/src/adapters/sandbox/e2b/types.ts +16 -0
- package/src/adapters/sandbox/inmemory/index.ts +17 -3
- package/src/adapters/sandbox/inmemory/proxy.ts +2 -0
- package/src/adapters/thread/anthropic/activities.ts +58 -26
- package/src/adapters/thread/anthropic/model-invoker.ts +18 -7
- package/src/adapters/thread/anthropic/proxy.ts +6 -2
- package/src/adapters/thread/anthropic/thread-manager.test.ts +26 -7
- package/src/adapters/thread/anthropic/thread-manager.ts +63 -46
- package/src/adapters/thread/google-genai/activities.ts +20 -2
- package/src/adapters/thread/google-genai/model-invoker.ts +27 -7
- package/src/adapters/thread/google-genai/proxy.ts +6 -2
- package/src/adapters/thread/google-genai/thread-manager.test.ts +13 -3
- package/src/adapters/thread/google-genai/thread-manager.ts +57 -33
- package/src/adapters/thread/langchain/activities.ts +55 -24
- package/src/adapters/thread/langchain/hooks.test.ts +36 -49
- package/src/adapters/thread/langchain/hooks.ts +18 -5
- package/src/adapters/thread/langchain/model-invoker.ts +5 -4
- package/src/adapters/thread/langchain/proxy.ts +6 -2
- package/src/adapters/thread/langchain/thread-manager.test.ts +5 -1
- package/src/adapters/thread/langchain/thread-manager.ts +23 -9
- package/src/index.ts +4 -1
- package/src/lib/activity.ts +16 -6
- package/src/lib/hooks/types.ts +6 -6
- package/src/lib/lifecycle.ts +18 -3
- package/src/lib/model/proxy.ts +2 -2
- package/src/lib/model/types.ts +10 -0
- package/src/lib/observability/hooks.ts +4 -5
- package/src/lib/observability/index.ts +1 -4
- package/src/lib/sandbox/manager.ts +45 -20
- package/src/lib/sandbox/node-fs.ts +3 -6
- package/src/lib/sandbox/sandbox.test.ts +36 -3
- package/src/lib/sandbox/tree.integration.test.ts +10 -3
- package/src/lib/sandbox/types.ts +60 -6
- package/src/lib/session/session-edge-cases.integration.test.ts +316 -14
- package/src/lib/session/session.integration.test.ts +161 -1
- package/src/lib/session/session.ts +106 -21
- package/src/lib/session/types.ts +25 -5
- package/src/lib/skills/fs-provider.ts +12 -8
- package/src/lib/skills/handler.ts +1 -1
- package/src/lib/skills/parse.ts +3 -1
- package/src/lib/skills/register.ts +1 -3
- package/src/lib/skills/skills.integration.test.ts +25 -15
- package/src/lib/state/manager.integration.test.ts +12 -2
- package/src/lib/subagent/define.ts +1 -1
- package/src/lib/subagent/handler.ts +186 -71
- package/src/lib/subagent/index.ts +1 -5
- package/src/lib/subagent/register.ts +3 -2
- package/src/lib/subagent/signals.ts +1 -10
- package/src/lib/subagent/subagent.integration.test.ts +526 -248
- package/src/lib/subagent/tool.ts +4 -3
- package/src/lib/subagent/types.ts +50 -20
- package/src/lib/subagent/workflow.ts +9 -49
- package/src/lib/thread/id.test.ts +1 -1
- package/src/lib/thread/id.ts +1 -2
- package/src/lib/thread/manager.ts +18 -0
- package/src/lib/thread/proxy.ts +4 -4
- package/src/lib/thread/types.ts +20 -3
- package/src/lib/tool-router/index.ts +3 -5
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +93 -1
- package/src/lib/tool-router/router.integration.test.ts +12 -0
- package/src/lib/tool-router/router.ts +90 -16
- package/src/lib/tool-router/types.ts +45 -4
- package/src/lib/tool-router/with-sandbox.ts +19 -5
- package/src/lib/virtual-fs/filesystem.ts +1 -1
- package/src/lib/virtual-fs/index.ts +5 -1
- package/src/lib/virtual-fs/mutations.ts +2 -4
- package/src/lib/virtual-fs/queries.ts +9 -5
- package/src/lib/virtual-fs/types.ts +4 -1
- package/src/lib/virtual-fs/virtual-fs.test.ts +9 -11
- package/src/lib/workflow.test.ts +7 -4
- package/src/lib/workflow.ts +1 -5
- package/src/tools/ask-user-question/tool.ts +1 -3
- package/src/tools/glob/handler.ts +1 -4
- package/src/tools/task-get/handler.ts +4 -5
- package/src/tools/task-list/handler.ts +1 -4
- package/src/tools/task-update/handler.ts +4 -5
- package/src/workflow.ts +22 -7
- package/tsup.config.ts +9 -6
- package/src/lib/.env +0 -1
- package/src/tools/bash/.env +0 -1
|
@@ -15,12 +15,19 @@ import type {
|
|
|
15
15
|
ToolArgs,
|
|
16
16
|
ToolResult,
|
|
17
17
|
ProcessToolCallsContext,
|
|
18
|
+
ProcessToolCallsResult,
|
|
19
|
+
RewindSignal,
|
|
18
20
|
ToolWithHandler,
|
|
19
21
|
} from "./types";
|
|
20
22
|
|
|
21
23
|
import type { JsonValue } from "../state/types";
|
|
22
24
|
import type { z } from "zod";
|
|
23
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
uuid4,
|
|
27
|
+
log,
|
|
28
|
+
CancellationScope,
|
|
29
|
+
isCancellation,
|
|
30
|
+
} from "@temporalio/workflow";
|
|
24
31
|
|
|
25
32
|
/**
|
|
26
33
|
* Creates a tool router for declarative tool call processing.
|
|
@@ -162,7 +169,8 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
162
169
|
|
|
163
170
|
return {
|
|
164
171
|
content: JSON.stringify({
|
|
165
|
-
error:
|
|
172
|
+
error:
|
|
173
|
+
"The tool encountered an error. Please try again or use a different approach.",
|
|
166
174
|
}),
|
|
167
175
|
result: { error: errorStr, suppressed: true },
|
|
168
176
|
};
|
|
@@ -198,11 +206,22 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
198
206
|
}
|
|
199
207
|
}
|
|
200
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Internal per-tool-call outcome. `rewind` signals the caller that the
|
|
211
|
+
* handler requested a session-level rewind; when present, the result is
|
|
212
|
+
* not appended to the thread and siblings should be cancelled.
|
|
213
|
+
*/
|
|
214
|
+
type ProcessedToolCall =
|
|
215
|
+
| { kind: "result"; value: ToolCallResultUnion<TResults> }
|
|
216
|
+
| { kind: "rewind"; signal: RewindSignal }
|
|
217
|
+
| { kind: "skipped" };
|
|
218
|
+
|
|
201
219
|
async function processToolCall(
|
|
202
220
|
toolCall: ParsedToolCallUnion<T>,
|
|
203
221
|
turn: number,
|
|
204
222
|
sandboxId?: string,
|
|
205
|
-
|
|
223
|
+
onRewindRequested?: (signal: RewindSignal) => void
|
|
224
|
+
): Promise<ProcessedToolCall> {
|
|
206
225
|
const startTime = Date.now();
|
|
207
226
|
const tool = toolMap.get(toolCall.name);
|
|
208
227
|
|
|
@@ -219,7 +238,7 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
219
238
|
reason: "Skipped by PreToolUse hook",
|
|
220
239
|
}),
|
|
221
240
|
});
|
|
222
|
-
return
|
|
241
|
+
return { kind: "skipped" };
|
|
223
242
|
}
|
|
224
243
|
const effectiveArgs = preResult.args;
|
|
225
244
|
|
|
@@ -234,6 +253,7 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
234
253
|
let content!: JsonValue;
|
|
235
254
|
let resultAppended = false;
|
|
236
255
|
let metadata: Record<string, unknown> | undefined;
|
|
256
|
+
let rewindRequested = false;
|
|
237
257
|
|
|
238
258
|
try {
|
|
239
259
|
if (tool) {
|
|
@@ -252,11 +272,15 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
252
272
|
content = response.toolResponse as JsonValue;
|
|
253
273
|
resultAppended = response.resultAppended === true;
|
|
254
274
|
metadata = response.metadata;
|
|
275
|
+
rewindRequested = response.rewind === true;
|
|
255
276
|
} else {
|
|
256
277
|
result = { error: `Unknown tool: ${toolCall.name}` };
|
|
257
278
|
content = JSON.stringify(result, null, 2);
|
|
258
279
|
}
|
|
259
280
|
} catch (error) {
|
|
281
|
+
if (isCancellation(error)) {
|
|
282
|
+
throw error;
|
|
283
|
+
}
|
|
260
284
|
log.warn("tool call failed", {
|
|
261
285
|
toolName: toolCall.name,
|
|
262
286
|
toolCallId: toolCall.id,
|
|
@@ -275,6 +299,16 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
275
299
|
content = recovery.content;
|
|
276
300
|
}
|
|
277
301
|
|
|
302
|
+
if (rewindRequested) {
|
|
303
|
+
const signal: RewindSignal = {
|
|
304
|
+
toolCallId: toolCall.id,
|
|
305
|
+
toolName: toolCall.name,
|
|
306
|
+
};
|
|
307
|
+
log.info("tool requested rewind", { ...signal });
|
|
308
|
+
onRewindRequested?.(signal);
|
|
309
|
+
return { kind: "rewind", signal };
|
|
310
|
+
}
|
|
311
|
+
|
|
278
312
|
// --- Append result to thread (unless handler already did) ---
|
|
279
313
|
if (!resultAppended) {
|
|
280
314
|
const config = {
|
|
@@ -318,7 +352,7 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
318
352
|
durationMs
|
|
319
353
|
);
|
|
320
354
|
|
|
321
|
-
return toolResult;
|
|
355
|
+
return { kind: "result", value: toolResult };
|
|
322
356
|
}
|
|
323
357
|
|
|
324
358
|
return {
|
|
@@ -368,31 +402,71 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
368
402
|
async processToolCalls(
|
|
369
403
|
toolCalls: ParsedToolCallUnion<T>[],
|
|
370
404
|
context?: ProcessToolCallsContext
|
|
371
|
-
): Promise<
|
|
405
|
+
): Promise<ProcessToolCallsResult<TResults>> {
|
|
406
|
+
const attachRewind = (
|
|
407
|
+
arr: ToolCallResultUnion<TResults>[],
|
|
408
|
+
rewind: RewindSignal | undefined,
|
|
409
|
+
): ProcessToolCallsResult<TResults> => {
|
|
410
|
+
if (rewind) {
|
|
411
|
+
(arr as ProcessToolCallsResult<TResults>).rewind = rewind;
|
|
412
|
+
}
|
|
413
|
+
return arr as ProcessToolCallsResult<TResults>;
|
|
414
|
+
};
|
|
415
|
+
|
|
372
416
|
if (toolCalls.length === 0) {
|
|
373
|
-
return [];
|
|
417
|
+
return attachRewind([], undefined);
|
|
374
418
|
}
|
|
375
419
|
|
|
376
420
|
const turn = context?.turn ?? 0;
|
|
377
421
|
const sandboxId = context?.sandboxId;
|
|
378
422
|
|
|
423
|
+
let rewindSignal: RewindSignal | undefined;
|
|
424
|
+
|
|
379
425
|
if (options.parallel) {
|
|
380
|
-
const
|
|
381
|
-
|
|
426
|
+
const scope = new CancellationScope({ cancellable: true });
|
|
427
|
+
const onRewindRequested = (signal: RewindSignal): void => {
|
|
428
|
+
if (!rewindSignal) {
|
|
429
|
+
rewindSignal = signal;
|
|
430
|
+
// Cancel all other in-flight tool calls in this batch.
|
|
431
|
+
scope.cancel();
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const outcomes = await scope.run(async () =>
|
|
436
|
+
Promise.allSettled(
|
|
437
|
+
toolCalls.map((tc) =>
|
|
438
|
+
processToolCall(tc, turn, sandboxId, onRewindRequested)
|
|
439
|
+
)
|
|
440
|
+
)
|
|
382
441
|
);
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
)
|
|
442
|
+
|
|
443
|
+
const results: ToolCallResultUnion<TResults>[] = [];
|
|
444
|
+
for (const outcome of outcomes) {
|
|
445
|
+
if (outcome.status === "rejected") {
|
|
446
|
+
if (isCancellation(outcome.reason)) {
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
throw outcome.reason;
|
|
450
|
+
}
|
|
451
|
+
if (outcome.value.kind === "result") {
|
|
452
|
+
results.push(outcome.value.value);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return attachRewind(results, rewindSignal);
|
|
386
456
|
}
|
|
387
457
|
|
|
388
458
|
const results: ToolCallResultUnion<TResults>[] = [];
|
|
389
459
|
for (const toolCall of toolCalls) {
|
|
390
|
-
const
|
|
391
|
-
if (
|
|
392
|
-
|
|
460
|
+
const outcome = await processToolCall(toolCall, turn, sandboxId);
|
|
461
|
+
if (outcome.kind === "rewind") {
|
|
462
|
+
rewindSignal = outcome.signal;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
if (outcome.kind === "result") {
|
|
466
|
+
results.push(outcome.value);
|
|
393
467
|
}
|
|
394
468
|
}
|
|
395
|
-
return results;
|
|
469
|
+
return attachRewind(results, rewindSignal);
|
|
396
470
|
},
|
|
397
471
|
|
|
398
472
|
async processToolCallsByName<TName extends ToolNames<T>, TResult>(
|
|
@@ -124,7 +124,10 @@ export type AppendToolResultFn = ActivityFunctionWithOptions<
|
|
|
124
124
|
* Tools that don't return additional data should use `data: null` (TResult defaults to null).
|
|
125
125
|
* Tools that may fail to produce data should type TResult as `SomeType | null`.
|
|
126
126
|
*/
|
|
127
|
-
export interface ToolHandlerResponse<
|
|
127
|
+
export interface ToolHandlerResponse<
|
|
128
|
+
TResult = null,
|
|
129
|
+
TToolResponse = JsonValue,
|
|
130
|
+
> {
|
|
128
131
|
/** Content sent back to the LLM as the tool call response */
|
|
129
132
|
toolResponse: TToolResponse;
|
|
130
133
|
/** Data returned to the workflow and hooks for further processing */
|
|
@@ -136,6 +139,17 @@ export interface ToolHandlerResponse<TResult = null, TToolResponse = JsonValue>
|
|
|
136
139
|
* payloads through Temporal's activity payload limit.
|
|
137
140
|
*/
|
|
138
141
|
resultAppended?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* When true, the session will rewind: any in-flight parallel tool calls
|
|
144
|
+
* are cancelled, previously appended tool results and the triggering
|
|
145
|
+
* assistant message are removed from the thread, and the LLM call that
|
|
146
|
+
* produced the tool calls is retried.
|
|
147
|
+
*
|
|
148
|
+
* The `toolResponse` for a rewinding tool call is ignored (never
|
|
149
|
+
* appended) since the session truncates the thread back to the
|
|
150
|
+
* pre-invocation state.
|
|
151
|
+
*/
|
|
152
|
+
rewind?: boolean;
|
|
139
153
|
/** Token usage from the tool execution (e.g. child agent invocations) */
|
|
140
154
|
usage?: TokenUsage;
|
|
141
155
|
/** Thread ID used by the handler (surfaced to the LLM for subagent thread continuation) */
|
|
@@ -172,7 +186,9 @@ export type ToolHandler<
|
|
|
172
186
|
> = (
|
|
173
187
|
args: TArgs,
|
|
174
188
|
context: TContext
|
|
175
|
-
) =>
|
|
189
|
+
) =>
|
|
190
|
+
| ToolHandlerResponse<TResult, TToolResponse>
|
|
191
|
+
| Promise<ToolHandlerResponse<TResult, TToolResponse>>;
|
|
176
192
|
|
|
177
193
|
/**
|
|
178
194
|
* Activity-compatible tool handler that always returns a Promise.
|
|
@@ -195,7 +211,10 @@ export type ActivityToolHandler<
|
|
|
195
211
|
TResult,
|
|
196
212
|
TContext extends RouterContext = RouterContext,
|
|
197
213
|
TToolResponse = JsonValue,
|
|
198
|
-
> = (
|
|
214
|
+
> = (
|
|
215
|
+
args: TArgs,
|
|
216
|
+
context: TContext
|
|
217
|
+
) => Promise<ToolHandlerResponse<TResult, TToolResponse>>;
|
|
199
218
|
|
|
200
219
|
/**
|
|
201
220
|
* Extract the args type for a specific tool name from a tool map.
|
|
@@ -270,6 +289,28 @@ export interface ProcessToolCallsContext {
|
|
|
270
289
|
sandboxId?: string;
|
|
271
290
|
}
|
|
272
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Signal that a tool handler requested a rewind. Attached to the
|
|
294
|
+
* {@link ProcessToolCallsResult} so the session can roll the thread
|
|
295
|
+
* back to the pre-invocation snapshot and retry the LLM call.
|
|
296
|
+
*/
|
|
297
|
+
export interface RewindSignal {
|
|
298
|
+
toolCallId: string;
|
|
299
|
+
toolName: string;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Result returned by {@link ToolRouter.processToolCalls}.
|
|
304
|
+
*
|
|
305
|
+
* The object is a standard array of tool call results for successful
|
|
306
|
+
* tool calls (cancelled or rewinding siblings are omitted), extended
|
|
307
|
+
* with a `rewind` property when any tool in the batch requested a
|
|
308
|
+
* rewind. Using an array-with-property lets existing code that treats
|
|
309
|
+
* the return value as `ToolCallResultUnion[]` continue to work.
|
|
310
|
+
*/
|
|
311
|
+
export type ProcessToolCallsResult<TResults extends Record<string, unknown>> =
|
|
312
|
+
ToolCallResultUnion<TResults>[] & { rewind?: RewindSignal };
|
|
313
|
+
|
|
273
314
|
// ============================================================================
|
|
274
315
|
// Hook Types
|
|
275
316
|
// ============================================================================
|
|
@@ -461,7 +502,7 @@ export interface ToolRouter<T extends ToolMap> {
|
|
|
461
502
|
processToolCalls(
|
|
462
503
|
toolCalls: ParsedToolCallUnion<T>[],
|
|
463
504
|
context?: ProcessToolCallsContext
|
|
464
|
-
): Promise<
|
|
505
|
+
): Promise<ProcessToolCallsResult<InferToolResults<T>>>;
|
|
465
506
|
|
|
466
507
|
/**
|
|
467
508
|
* Process tool calls matching a specific name with a custom handler.
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { Sandbox } from "../sandbox/types";
|
|
2
2
|
import type { JsonValue } from "../state/types";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ActivityToolHandler,
|
|
5
|
+
RouterContext,
|
|
6
|
+
ToolHandlerResponse,
|
|
7
|
+
} from "./types";
|
|
4
8
|
|
|
5
9
|
/**
|
|
6
10
|
* Extended router context with a resolved {@link Sandbox} instance.
|
|
@@ -51,13 +55,23 @@ export interface SandboxContext extends RouterContext {
|
|
|
51
55
|
* });
|
|
52
56
|
* ```
|
|
53
57
|
*/
|
|
54
|
-
export function withSandbox<
|
|
58
|
+
export function withSandbox<
|
|
59
|
+
TArgs,
|
|
60
|
+
TResult,
|
|
61
|
+
TSandbox extends Sandbox = Sandbox,
|
|
62
|
+
TToolResponse = JsonValue,
|
|
63
|
+
>(
|
|
55
64
|
manager: { getSandbox(id: string): Promise<TSandbox> },
|
|
56
65
|
handler: (
|
|
57
66
|
args: TArgs,
|
|
58
|
-
context: RouterContext & { sandbox: TSandbox; sandboxId: string }
|
|
59
|
-
) => Promise<ToolHandlerResponse<TResult, TToolResponse
|
|
60
|
-
): ActivityToolHandler<
|
|
67
|
+
context: RouterContext & { sandbox: TSandbox; sandboxId: string }
|
|
68
|
+
) => Promise<ToolHandlerResponse<TResult, TToolResponse>>
|
|
69
|
+
): ActivityToolHandler<
|
|
70
|
+
TArgs,
|
|
71
|
+
TResult | null,
|
|
72
|
+
RouterContext,
|
|
73
|
+
TToolResponse | string
|
|
74
|
+
> {
|
|
61
75
|
return async (args, context) => {
|
|
62
76
|
if (!context.sandboxId) {
|
|
63
77
|
return {
|
|
@@ -67,7 +67,7 @@ export class VirtualFileSystem<
|
|
|
67
67
|
private resolver: FileResolver<TCtx, TMeta>,
|
|
68
68
|
private ctx: TCtx,
|
|
69
69
|
workspaceBase = "/",
|
|
70
|
-
inlineFiles?: Record<string, string
|
|
70
|
+
inlineFiles?: Record<string, string>
|
|
71
71
|
) {
|
|
72
72
|
this.workspaceBase = normalisePath(workspaceBase);
|
|
73
73
|
this.entries = new Map(
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
export { VirtualFileSystem } from "./filesystem";
|
|
2
2
|
export { withVirtualFs } from "./with-virtual-fs";
|
|
3
3
|
export { createVirtualFsActivities } from "./manager";
|
|
4
|
-
export {
|
|
4
|
+
export {
|
|
5
|
+
hasFileWithMimeType,
|
|
6
|
+
filesWithMimeType,
|
|
7
|
+
hasDirectory,
|
|
8
|
+
} from "./queries";
|
|
5
9
|
export { applyVirtualTreeMutations } from "./mutations";
|
|
6
10
|
export { formatVirtualFileTree } from "./tree";
|
|
7
11
|
export type { FileTreeAccessor } from "./queries";
|
|
@@ -13,7 +13,7 @@ export function applyVirtualTreeMutations<TMeta = FileEntryMetadata>(
|
|
|
13
13
|
get(key: "fileTree"): VirtualFileTree<TMeta>;
|
|
14
14
|
set(key: "fileTree", value: VirtualFileTree<TMeta>): void;
|
|
15
15
|
},
|
|
16
|
-
mutations: TreeMutation<TMeta>[]
|
|
16
|
+
mutations: TreeMutation<TMeta>[]
|
|
17
17
|
): VirtualFileTree<TMeta> {
|
|
18
18
|
let tree = [...stateManager.get("fileTree")];
|
|
19
19
|
|
|
@@ -26,9 +26,7 @@ export function applyVirtualTreeMutations<TMeta = FileEntryMetadata>(
|
|
|
26
26
|
tree = tree.filter((e) => e.path !== m.path);
|
|
27
27
|
break;
|
|
28
28
|
case "update":
|
|
29
|
-
tree = tree.map((e) =>
|
|
30
|
-
e.path === m.path ? { ...e, ...m.entry } : e
|
|
31
|
-
);
|
|
29
|
+
tree = tree.map((e) => (e.path === m.path ? { ...e, ...m.entry } : e));
|
|
32
30
|
break;
|
|
33
31
|
}
|
|
34
32
|
}
|
|
@@ -25,10 +25,12 @@ export interface FileTreeAccessor<TMeta> {
|
|
|
25
25
|
*/
|
|
26
26
|
export function hasFileWithMimeType<TMeta>(
|
|
27
27
|
stateManager: FileTreeAccessor<TMeta>,
|
|
28
|
-
pattern: string | string[]
|
|
28
|
+
pattern: string | string[]
|
|
29
29
|
): boolean {
|
|
30
30
|
const tree = stateManager.get("fileTree");
|
|
31
|
-
const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(
|
|
31
|
+
const matchers = (Array.isArray(pattern) ? pattern : [pattern]).map(
|
|
32
|
+
buildMatcher
|
|
33
|
+
);
|
|
32
34
|
return tree.some((entry) => {
|
|
33
35
|
const meta = entry.metadata as Record<string, unknown> | undefined;
|
|
34
36
|
const mime = meta?.mimeType;
|
|
@@ -41,7 +43,7 @@ export function hasFileWithMimeType<TMeta>(
|
|
|
41
43
|
*/
|
|
42
44
|
export function filesWithMimeType<TMeta>(
|
|
43
45
|
stateManager: FileTreeAccessor<TMeta>,
|
|
44
|
-
pattern: string
|
|
46
|
+
pattern: string
|
|
45
47
|
): FileEntry<TMeta>[] {
|
|
46
48
|
const tree = stateManager.get("fileTree");
|
|
47
49
|
const match = buildMatcher(pattern);
|
|
@@ -66,7 +68,7 @@ export function filesWithMimeType<TMeta>(
|
|
|
66
68
|
*/
|
|
67
69
|
export function hasDirectory<TMeta>(
|
|
68
70
|
stateManager: FileTreeAccessor<TMeta>,
|
|
69
|
-
pattern: string
|
|
71
|
+
pattern: string
|
|
70
72
|
): boolean {
|
|
71
73
|
const tree = stateManager.get("fileTree");
|
|
72
74
|
const match = buildGlobMatcher(pattern);
|
|
@@ -91,7 +93,9 @@ function buildMatcher(pattern: string): (value: string) => boolean {
|
|
|
91
93
|
function buildGlobMatcher(pattern: string): (value: string) => boolean {
|
|
92
94
|
if (!pattern.includes("*")) return (v) => v === pattern;
|
|
93
95
|
const re = new RegExp(
|
|
94
|
-
"^" +
|
|
96
|
+
"^" +
|
|
97
|
+
pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") +
|
|
98
|
+
"$"
|
|
95
99
|
);
|
|
96
100
|
return (v) => re.test(v);
|
|
97
101
|
}
|
|
@@ -104,7 +104,10 @@ export type PrefixedVirtualFsOps<
|
|
|
104
104
|
[K in keyof VirtualFsOps<
|
|
105
105
|
TCtx,
|
|
106
106
|
TMeta
|
|
107
|
-
> as `virtualFs${Capitalize<TPrefix>}${Capitalize<K & string>}`]: VirtualFsOps<
|
|
107
|
+
> as `virtualFs${Capitalize<TPrefix>}${Capitalize<K & string>}`]: VirtualFsOps<
|
|
108
|
+
TCtx,
|
|
109
|
+
TMeta
|
|
110
|
+
>[K];
|
|
108
111
|
};
|
|
109
112
|
|
|
110
113
|
// ============================================================================
|
|
@@ -53,7 +53,7 @@ function createMockResolver(): {
|
|
|
53
53
|
id,
|
|
54
54
|
typeof content === "string"
|
|
55
55
|
? content
|
|
56
|
-
: new TextDecoder().decode(content)
|
|
56
|
+
: new TextDecoder().decode(content)
|
|
57
57
|
);
|
|
58
58
|
},
|
|
59
59
|
|
|
@@ -63,7 +63,7 @@ function createMockResolver(): {
|
|
|
63
63
|
id,
|
|
64
64
|
typeof content === "string"
|
|
65
65
|
? content
|
|
66
|
-
: new TextDecoder().decode(content)
|
|
66
|
+
: new TextDecoder().decode(content)
|
|
67
67
|
);
|
|
68
68
|
const size =
|
|
69
69
|
typeof content === "string"
|
|
@@ -286,9 +286,7 @@ describe("VirtualFileSystem", () => {
|
|
|
286
286
|
});
|
|
287
287
|
|
|
288
288
|
it("rm force does not throw for missing path", async () => {
|
|
289
|
-
await expect(
|
|
290
|
-
fs.rm("/nonexistent", { force: true }),
|
|
291
|
-
).resolves.not.toThrow();
|
|
289
|
+
await expect(fs.rm("/nonexistent", { force: true })).resolves.not.toThrow();
|
|
292
290
|
});
|
|
293
291
|
|
|
294
292
|
// --- cp / mv ---
|
|
@@ -320,7 +318,7 @@ describe("VirtualFileSystem", () => {
|
|
|
320
318
|
|
|
321
319
|
it("readlink throws SandboxNotSupportedError", async () => {
|
|
322
320
|
await expect(fs.readlink("/src/index.ts")).rejects.toThrow(
|
|
323
|
-
SandboxNotSupportedError
|
|
321
|
+
SandboxNotSupportedError
|
|
324
322
|
);
|
|
325
323
|
});
|
|
326
324
|
});
|
|
@@ -349,7 +347,7 @@ describe("VirtualFileSystem — inlineFiles", () => {
|
|
|
349
347
|
resolver,
|
|
350
348
|
ctx,
|
|
351
349
|
"/",
|
|
352
|
-
inlineFiles
|
|
350
|
+
inlineFiles
|
|
353
351
|
);
|
|
354
352
|
}
|
|
355
353
|
|
|
@@ -418,7 +416,9 @@ describe("applyVirtualTreeMutations", () => {
|
|
|
418
416
|
let fileTree = tree;
|
|
419
417
|
return {
|
|
420
418
|
get: (_key: "fileTree"): FileEntry[] => fileTree,
|
|
421
|
-
set: (_key: "fileTree", value: FileEntry[]): void => {
|
|
419
|
+
set: (_key: "fileTree", value: FileEntry[]): void => {
|
|
420
|
+
fileTree = value;
|
|
421
|
+
},
|
|
422
422
|
current: (): FileEntry[] => fileTree,
|
|
423
423
|
};
|
|
424
424
|
}
|
|
@@ -490,9 +490,7 @@ describe("applyVirtualTreeMutations", () => {
|
|
|
490
490
|
it("does not mutate the original array passed to the state manager", () => {
|
|
491
491
|
const original = [...sampleTree];
|
|
492
492
|
const sm = mockStateManager(sampleTree);
|
|
493
|
-
applyVirtualTreeMutations(sm, [
|
|
494
|
-
{ type: "remove", path: "/src/index.ts" },
|
|
495
|
-
]);
|
|
493
|
+
applyVirtualTreeMutations(sm, [{ type: "remove", path: "/src/index.ts" }]);
|
|
496
494
|
expect(sampleTree).toEqual(original);
|
|
497
495
|
});
|
|
498
496
|
});
|
package/src/lib/workflow.test.ts
CHANGED
|
@@ -50,10 +50,13 @@ describe("defineWorkflow", () => {
|
|
|
50
50
|
return { ok: true };
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
await workflow(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
await workflow(
|
|
54
|
+
{},
|
|
55
|
+
{
|
|
56
|
+
thread: { mode: "fork", threadId: "prev-1" },
|
|
57
|
+
sandbox: { mode: "continue", sandboxId: "sb-1" },
|
|
58
|
+
}
|
|
59
|
+
);
|
|
57
60
|
|
|
58
61
|
expect(capturedSession).toEqual({
|
|
59
62
|
agentName: "test-workflow",
|
package/src/lib/workflow.ts
CHANGED
|
@@ -19,10 +19,7 @@ function matchGlob(pattern: string, path: string): boolean {
|
|
|
19
19
|
return new RegExp(`^${regex}$`).test(path);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
async function walk(
|
|
23
|
-
fs: Sandbox["fs"],
|
|
24
|
-
dir: string,
|
|
25
|
-
): Promise<string[]> {
|
|
22
|
+
async function walk(fs: Sandbox["fs"], dir: string): Promise<string[]> {
|
|
26
23
|
const results: string[] = [];
|
|
27
24
|
const entries = await fs.readdirWithFileTypes(dir);
|
|
28
25
|
for (const entry of entries) {
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AgentStateManager,
|
|
3
|
-
JsonSerializable,
|
|
4
|
-
} from "../../lib/state";
|
|
1
|
+
import type { AgentStateManager, JsonSerializable } from "../../lib/state";
|
|
5
2
|
import type { ToolHandler } from "../../lib/tool-router";
|
|
6
3
|
import type { WorkflowTask } from "../../lib/types";
|
|
7
4
|
import type { TaskGetArgs } from "./tool";
|
|
@@ -20,7 +17,9 @@ export function createTaskGetHandler<TCustom extends JsonSerializable<TCustom>>(
|
|
|
20
17
|
|
|
21
18
|
if (!task) {
|
|
22
19
|
return {
|
|
23
|
-
toolResponse: JSON.stringify({
|
|
20
|
+
toolResponse: JSON.stringify({
|
|
21
|
+
error: `Task not found: ${args.taskId}`,
|
|
22
|
+
}),
|
|
24
23
|
data: null,
|
|
25
24
|
};
|
|
26
25
|
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AgentStateManager,
|
|
3
|
-
JsonSerializable,
|
|
4
|
-
} from "../../lib/state";
|
|
1
|
+
import type { AgentStateManager, JsonSerializable } from "../../lib/state";
|
|
5
2
|
import type { ToolHandler } from "../../lib/tool-router";
|
|
6
3
|
import type { WorkflowTask } from "../../lib/types";
|
|
7
4
|
import type { TaskListArgs } from "./tool";
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AgentStateManager,
|
|
3
|
-
JsonSerializable,
|
|
4
|
-
} from "../../lib/state";
|
|
1
|
+
import type { AgentStateManager, JsonSerializable } from "../../lib/state";
|
|
5
2
|
import type { ToolHandler } from "../../lib/tool-router";
|
|
6
3
|
import type { WorkflowTask } from "../../lib/types";
|
|
7
4
|
import type { TaskUpdateArgs } from "./tool";
|
|
@@ -22,7 +19,9 @@ export function createTaskUpdateHandler<
|
|
|
22
19
|
|
|
23
20
|
if (!task) {
|
|
24
21
|
return {
|
|
25
|
-
toolResponse: JSON.stringify({
|
|
22
|
+
toolResponse: JSON.stringify({
|
|
23
|
+
error: `Task not found: ${args.taskId}`,
|
|
24
|
+
}),
|
|
26
25
|
data: null,
|
|
27
26
|
};
|
|
28
27
|
}
|
package/src/workflow.ts
CHANGED
|
@@ -15,12 +15,28 @@
|
|
|
15
15
|
|
|
16
16
|
// Session
|
|
17
17
|
export { createSession } from "./lib/session";
|
|
18
|
-
export type {
|
|
18
|
+
export type {
|
|
19
|
+
ZeitlichSession,
|
|
20
|
+
SessionResult,
|
|
21
|
+
ThreadOps,
|
|
22
|
+
PrefixedThreadOps,
|
|
23
|
+
ScopedPrefix,
|
|
24
|
+
SessionConfig,
|
|
25
|
+
} from "./lib/session";
|
|
19
26
|
export { defineWorkflow } from "./lib/workflow";
|
|
20
|
-
export type {
|
|
27
|
+
export type {
|
|
28
|
+
WorkflowConfig,
|
|
29
|
+
WorkflowInput,
|
|
30
|
+
WorkflowSessionInput,
|
|
31
|
+
} from "./lib/workflow";
|
|
21
32
|
|
|
22
33
|
// Lifecycle types
|
|
23
|
-
export type {
|
|
34
|
+
export type {
|
|
35
|
+
ThreadInit,
|
|
36
|
+
SandboxInit,
|
|
37
|
+
SandboxShutdown,
|
|
38
|
+
SubagentSandboxShutdown,
|
|
39
|
+
} from "./lib/lifecycle";
|
|
24
40
|
|
|
25
41
|
// Thread utilities
|
|
26
42
|
export { getShortId } from "./lib/thread/id";
|
|
@@ -78,6 +94,8 @@ export type {
|
|
|
78
94
|
// Other
|
|
79
95
|
AppendToolResultFn,
|
|
80
96
|
ProcessToolCallsContext,
|
|
97
|
+
ProcessToolCallsResult,
|
|
98
|
+
RewindSignal,
|
|
81
99
|
} from "./lib/tool-router";
|
|
82
100
|
|
|
83
101
|
// Session & message lifecycle hooks
|
|
@@ -94,10 +112,7 @@ export type {
|
|
|
94
112
|
} from "./lib/hooks";
|
|
95
113
|
|
|
96
114
|
// Observability
|
|
97
|
-
export {
|
|
98
|
-
createObservabilityHooks,
|
|
99
|
-
composeHooks,
|
|
100
|
-
} from "./lib/observability";
|
|
115
|
+
export { createObservabilityHooks, composeHooks } from "./lib/observability";
|
|
101
116
|
export type {
|
|
102
117
|
ObservabilityHooks,
|
|
103
118
|
ZeitlichObservabilitySinks,
|