zeitlich 0.2.49 → 0.2.50

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 (123) hide show
  1. package/README.md +26 -23
  2. package/dist/{activities-zG_FBoY2.d.ts → activities-IuOIvPHO.d.ts} +6 -6
  3. package/dist/{activities-7OcT_vdR.d.cts → activities-cIlq1y1y.d.cts} +6 -6
  4. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  5. package/dist/adapters/sandbox/daytona/index.d.cts +3 -3
  6. package/dist/adapters/sandbox/daytona/index.d.ts +3 -3
  7. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  8. package/dist/adapters/sandbox/daytona/workflow.d.cts +2 -2
  9. package/dist/adapters/sandbox/daytona/workflow.d.ts +2 -2
  10. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  11. package/dist/adapters/sandbox/e2b/index.d.cts +1 -1
  12. package/dist/adapters/sandbox/e2b/index.d.ts +1 -1
  13. package/dist/adapters/sandbox/e2b/index.js.map +1 -1
  14. package/dist/adapters/sandbox/e2b/workflow.d.cts +1 -1
  15. package/dist/adapters/sandbox/e2b/workflow.d.ts +1 -1
  16. package/dist/adapters/thread/anthropic/index.cjs +45 -42
  17. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  18. package/dist/adapters/thread/anthropic/index.d.cts +10 -10
  19. package/dist/adapters/thread/anthropic/index.d.ts +10 -10
  20. package/dist/adapters/thread/anthropic/index.js +45 -42
  21. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  22. package/dist/adapters/thread/anthropic/workflow.d.cts +7 -7
  23. package/dist/adapters/thread/anthropic/workflow.d.ts +7 -7
  24. package/dist/adapters/thread/google-genai/index.cjs +117 -54
  25. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  26. package/dist/adapters/thread/google-genai/index.d.cts +27 -23
  27. package/dist/adapters/thread/google-genai/index.d.ts +27 -23
  28. package/dist/adapters/thread/google-genai/index.js +117 -54
  29. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  30. package/dist/adapters/thread/google-genai/workflow.d.cts +8 -8
  31. package/dist/adapters/thread/google-genai/workflow.d.ts +8 -8
  32. package/dist/adapters/thread/langchain/index.cjs +45 -42
  33. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  34. package/dist/adapters/thread/langchain/index.d.cts +10 -10
  35. package/dist/adapters/thread/langchain/index.d.ts +10 -10
  36. package/dist/adapters/thread/langchain/index.js +45 -42
  37. package/dist/adapters/thread/langchain/index.js.map +1 -1
  38. package/dist/adapters/thread/langchain/workflow.d.cts +7 -7
  39. package/dist/adapters/thread/langchain/workflow.d.ts +7 -7
  40. package/dist/{cold-store-CkWoNtMh.d.cts → cold-store-C0uvYTSi.d.cts} +1 -1
  41. package/dist/{cold-store-DKMAO1Dd.d.ts → cold-store-CCnZYWjx.d.ts} +1 -1
  42. package/dist/index.cjs +15050 -420
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.d.cts +79 -83
  45. package/dist/index.d.ts +79 -83
  46. package/dist/index.js +15051 -417
  47. package/dist/index.js.map +1 -1
  48. package/dist/{proxy-B7CWEV-T.d.cts → proxy-BVznA2_p.d.cts} +1 -1
  49. package/dist/{proxy-ByFHMVRX.d.ts → proxy-C4J1pNUk.d.ts} +1 -1
  50. package/dist/{thread-manager-Cibe0X5m.d.cts → thread-manager-BqjzWsP7.d.ts} +4 -4
  51. package/dist/{thread-manager-B9rtMEVn.d.cts → thread-manager-CzIs47uG.d.cts} +4 -4
  52. package/dist/{thread-manager-nK-WcFzM.d.ts → thread-manager-Dzl1fHhV.d.cts} +4 -4
  53. package/dist/{thread-manager-7AW4rhfu.d.ts → thread-manager-SkSWRPRc.d.ts} +4 -4
  54. package/dist/{types-gVa5XCWD.d.ts → types-BQvXWcft.d.ts} +1 -1
  55. package/dist/{types-DO4Tkwxo.d.ts → types-CbPnU4RM.d.ts} +3 -3
  56. package/dist/{types-CJ7tCdl6.d.ts → types-D8W5TnSa.d.cts} +3 -3
  57. package/dist/{types-CJ7tCdl6.d.cts → types-D8W5TnSa.d.ts} +3 -3
  58. package/dist/{types-DeVNWqlb.d.ts → types-DZnUqCAP.d.cts} +709 -709
  59. package/dist/{types-CjY93AWZ.d.cts → types-OEN1xrFg.d.cts} +1 -1
  60. package/dist/{types-XUUFvrJ9.d.cts → types-YNesmGKV.d.ts} +709 -709
  61. package/dist/{types-BR-k7h0e.d.cts → types-d2RvEP6v.d.cts} +3 -3
  62. package/dist/{workflow-uhOIj9D-.d.ts → workflow-B3oTe2_D.d.cts} +34 -3
  63. package/dist/{workflow-KbGsxpfh.d.cts → workflow-Bkzg0cjB.d.ts} +34 -3
  64. package/dist/workflow.cjs +15008 -377
  65. package/dist/workflow.cjs.map +1 -1
  66. package/dist/workflow.d.cts +3 -3
  67. package/dist/workflow.d.ts +3 -3
  68. package/dist/workflow.js +15009 -374
  69. package/dist/workflow.js.map +1 -1
  70. package/package.json +10 -37
  71. package/src/adapters/thread/anthropic/activities.ts +1 -1
  72. package/src/adapters/thread/anthropic/fork-transform.test.ts +17 -11
  73. package/src/adapters/thread/anthropic/model-invoker.test.ts +4 -3
  74. package/src/adapters/thread/anthropic/model-invoker.ts +1 -1
  75. package/src/adapters/thread/anthropic/thread-manager.test.ts +2 -2
  76. package/src/adapters/thread/anthropic/thread-manager.ts +1 -1
  77. package/src/adapters/thread/google-genai/activities.ts +1 -1
  78. package/src/adapters/thread/google-genai/fork-transform.test.ts +17 -11
  79. package/src/adapters/thread/google-genai/model-invoker.test.ts +337 -0
  80. package/src/adapters/thread/google-genai/model-invoker.ts +107 -23
  81. package/src/adapters/thread/google-genai/thread-manager.test.ts +2 -2
  82. package/src/adapters/thread/google-genai/thread-manager.ts +1 -1
  83. package/src/adapters/thread/langchain/activities.ts +1 -1
  84. package/src/adapters/thread/langchain/fork-transform.test.ts +17 -11
  85. package/src/adapters/thread/langchain/model-invoker.ts +1 -1
  86. package/src/adapters/thread/langchain/thread-manager.test.ts +2 -2
  87. package/src/adapters/thread/langchain/thread-manager.ts +1 -1
  88. package/src/index.ts +2 -2
  89. package/src/lib/sandbox/capability-types.test.ts +2 -2
  90. package/src/lib/sandbox/manager.ts +2 -6
  91. package/src/lib/sandbox/sandbox.test.ts +1 -1
  92. package/src/lib/sandbox/types.ts +2 -2
  93. package/src/lib/session/session.integration.test.ts +92 -0
  94. package/src/lib/session/session.ts +23 -11
  95. package/src/lib/thread/keys.test.ts +9 -9
  96. package/src/lib/thread/keys.ts +1 -1
  97. package/src/lib/thread/manager.test.ts +24 -14
  98. package/src/lib/thread/manager.ts +19 -23
  99. package/src/lib/thread/snapshot.test.ts +51 -43
  100. package/src/lib/thread/snapshot.ts +54 -32
  101. package/src/lib/thread/test-utils.ts +106 -59
  102. package/src/lib/thread/tiered.test.ts +1 -1
  103. package/src/lib/thread/types.ts +2 -2
  104. package/src/lib/tool-router/router.integration.test.ts +44 -0
  105. package/src/lib/tool-router/router.ts +140 -32
  106. package/src/lib/workflow.ts +49 -0
  107. package/src/{adapters/sandbox/inmemory/proxy.ts → test-utils/in-memory-sandbox-proxy.ts} +5 -16
  108. package/src/{adapters/sandbox/inmemory/index.ts → test-utils/in-memory-sandbox.ts} +11 -3
  109. package/src/tools/bash/bash.test.ts +1 -1
  110. package/src/tools/edit/handler.test.ts +1 -1
  111. package/tsup.config.ts +2 -4
  112. package/dist/adapters/sandbox/inmemory/index.cjs +0 -214
  113. package/dist/adapters/sandbox/inmemory/index.cjs.map +0 -1
  114. package/dist/adapters/sandbox/inmemory/index.d.cts +0 -40
  115. package/dist/adapters/sandbox/inmemory/index.d.ts +0 -40
  116. package/dist/adapters/sandbox/inmemory/index.js +0 -211
  117. package/dist/adapters/sandbox/inmemory/index.js.map +0 -1
  118. package/dist/adapters/sandbox/inmemory/workflow.cjs +0 -36
  119. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +0 -1
  120. package/dist/adapters/sandbox/inmemory/workflow.d.cts +0 -27
  121. package/dist/adapters/sandbox/inmemory/workflow.d.ts +0 -27
  122. package/dist/adapters/sandbox/inmemory/workflow.js +0 -34
  123. package/dist/adapters/sandbox/inmemory/workflow.js.map +0 -1
@@ -1,861 +1,861 @@
1
- import { ActivityFunctionWithOptions, QueryDefinition, ChildWorkflowOptions, ActivityInterfaceFor } from '@temporalio/workflow';
1
+ import { QueryDefinition, ActivityFunctionWithOptions, ChildWorkflowOptions, ActivityInterfaceFor } from '@temporalio/workflow';
2
2
  import { UpdateDefinition } from '@temporalio/common/lib/interfaces';
3
3
  import { z } from 'zod';
4
- import { g as SandboxSnapshot, c as SandboxFileSystem, F as FileStat, D as DirentEntry, a as SandboxCreateOptions, S as SandboxOps, h as SandboxCapability } from './types-CJ7tCdl6.cjs';
4
+ import { d as SandboxFileSystem, F as FileStat, D as DirentEntry, g as SandboxSnapshot, f as SandboxCreateOptions, c as SandboxOps, h as SandboxCapability } from './types-D8W5TnSa.js';
5
5
 
6
6
  /**
7
- * A tool definition with a name, description, and Zod schema for arguments.
8
- * Does not include a handler - use ToolWithHandler for tools with handlers.
7
+ * JSON primitive types that Temporal can serialize
9
8
  */
10
- interface ToolDefinition<TName extends string = string, TSchema extends z.ZodType = z.ZodType> {
11
- name: TName;
12
- description: string;
13
- schema: TSchema;
14
- strict?: boolean;
15
- max_uses?: number;
16
- }
9
+ type JsonPrimitive = string | number | boolean | null | undefined;
17
10
  /**
18
- * A tool definition with an integrated handler function.
19
- * This is the primary type for defining tools in the router.
11
+ * JSON-serializable value (recursive type for Temporal compatibility)
20
12
  */
21
- interface ToolWithHandler<TName extends string = string, TSchema extends z.ZodType = z.ZodType, TResult = unknown, TContext extends RouterContext = RouterContext, TToolResponse = JsonValue> {
22
- name: TName;
23
- description: string;
24
- schema: TSchema;
25
- handler: ToolHandler<z.infer<TSchema>, TResult, TContext, TToolResponse>;
26
- strict?: boolean;
27
- max_uses?: number;
28
- /** Whether this tool is available to the agent (default: true). Disabled tools are excluded from definitions and rejected at parse time. */
29
- enabled?: boolean | (() => boolean);
30
- /** Per-tool lifecycle hooks (run in addition to global hooks) */
31
- hooks?: ToolHooks<z.infer<TSchema>, TResult>;
32
- }
13
+ type JsonValue = JsonPrimitive | JsonValue[] | {
14
+ [key: string]: JsonValue;
15
+ };
33
16
  /**
34
- * A map of tool keys to tool definitions with handlers.
17
+ * Type constraint ensuring T only contains JSON-serializable values.
18
+ * Use this for custom state to ensure Temporal workflow compatibility.
35
19
  *
36
- * Handler uses `any` intentionally this is a type-system boundary where heterogeneous
37
- * tool types are stored together. Type safety for individual tools is enforced by
38
- * `defineTool()` at the definition site and generic inference utilities like
39
- * `InferToolResults<T>` at the consumption site.
20
+ * Allows: primitives, arrays, plain objects, and JsonValue
21
+ * Rejects: functions, symbols, undefined, class instances with methods
40
22
  */
41
- type ToolMap = Record<string, {
42
- name: string;
43
- description: string | (() => string);
44
- schema: z.ZodType | (() => z.ZodType);
45
- handler: ToolHandler<any, any, any, any>;
46
- strict?: boolean;
47
- max_uses?: number;
48
- enabled?: boolean | (() => boolean);
49
- hooks?: ToolHooks<any, any>;
50
- }>;
23
+ type JsonSerializable<T> = {
24
+ [K in keyof T]: T[K] extends JsonValue ? T[K] : T[K] extends JsonPrimitive ? T[K] : T[K] extends (infer U)[] ? U extends JsonValue ? T[K] : JsonSerializable<U>[] : T[K] extends object ? JsonSerializable<T[K]> : never;
25
+ };
51
26
  /**
52
- * Extract the tool names from a tool map (uses the tool's name property, not the key).
27
+ * Full state type combining base state with custom state
53
28
  */
54
- type ToolNames<T extends ToolMap> = T[keyof T]["name"];
29
+ type AgentState<TCustom extends JsonSerializable<TCustom>> = BaseAgentState & TCustom;
55
30
  /**
56
- * A raw tool call as received from the LLM before parsing.
31
+ * The slice of agent state that is persisted alongside the thread in the
32
+ * thread store (e.g. Redis) so that a workflow can terminate, store its
33
+ * state, and be continued or forked later with that state rehydrated.
34
+ *
35
+ * Only fields that make sense to carry across workflow runs belong here.
36
+ * Runtime bookkeeping like status, version, turns, tools, fileTree, token
37
+ * counters, and the system prompt is intentionally NOT persisted — each run
38
+ * rebuilds those from scratch.
57
39
  */
58
- interface RawToolCall {
59
- id?: string;
60
- name: string;
61
- args: unknown;
40
+ interface PersistedThreadState {
41
+ /** Task map serialized as entries so it round-trips through JSON. */
42
+ tasks: [string, WorkflowTask][];
43
+ /** All custom state fields declared by the caller. */
44
+ custom: Record<string, JsonValue>;
62
45
  }
63
46
  /**
64
- * A parsed tool call with validated arguments for a specific tool.
47
+ * Agent state manager interface
48
+ * Note: Temporal handlers must be set up in the workflow file due to
49
+ * Temporal's workflow isolation requirements. This manager provides
50
+ * the state and helpers needed for those handlers.
65
51
  */
66
- interface ParsedToolCall<TName extends string = string, TArgs = unknown> {
67
- id: string;
68
- name: TName;
69
- args: TArgs;
52
+ interface AgentStateManager<TCustom extends JsonSerializable<TCustom>> {
53
+ /** Typed query definition registered for this agent's state */
54
+ readonly stateQuery: QueryDefinition<AgentState<TCustom>>;
55
+ /** Typed update definition registered for waiting on this agent's state change */
56
+ readonly stateChangeUpdate: UpdateDefinition<AgentState<TCustom>, [number]>;
57
+ /** Get current status */
58
+ getStatus(): AgentStatus;
59
+ /** Check if agent is running */
60
+ isRunning(): boolean;
61
+ /** Check if agent is in terminal state */
62
+ isTerminal(): boolean;
63
+ /** Get current state version */
64
+ getVersion(): number;
65
+ /** Set status to RUNNING */
66
+ run(): void;
67
+ /** Set status to WAITING_FOR_INPUT */
68
+ waitForInput(): void;
69
+ /** Set status to COMPLETED */
70
+ complete(): void;
71
+ /** Set status to FAILED */
72
+ fail(): void;
73
+ /** Set status to CANCELLED */
74
+ cancel(): void;
75
+ /** Increment state version (call after state changes) */
76
+ incrementVersion(): void;
77
+ /** Increment turns (call after each turn) */
78
+ incrementTurns(): void;
79
+ /** Get current turns */
80
+ getTurns(): number;
81
+ /** Get the system prompt */
82
+ getSystemPrompt(): unknown;
83
+ /** Set the system prompt */
84
+ setSystemPrompt(newSystemPrompt: unknown): void;
85
+ /** Get a custom state value by key */
86
+ get<K extends keyof TCustom>(key: K): TCustom[K];
87
+ /** Set a custom state value by key */
88
+ set<K extends keyof TCustom>(key: K, value: TCustom[K]): void;
89
+ /** Bulk-merge a partial update into custom state */
90
+ mergeUpdate(update: Partial<AgentState<TCustom>>): void;
91
+ /** Get full state for query handler */
92
+ getCurrentState(): AgentState<TCustom>;
93
+ /** Check if should return from waitForStateChange */
94
+ shouldReturnFromWait(lastKnownVersion: number): boolean;
95
+ /** Get all tasks */
96
+ getTasks(): WorkflowTask[];
97
+ /** Get a task by ID */
98
+ getTask(id: string): WorkflowTask | undefined;
99
+ /** Add or update a task */
100
+ setTask(task: WorkflowTask): void;
101
+ /** Delete a task by ID */
102
+ deleteTask(id: string): boolean;
103
+ /** Set the tools (converts Zod schemas to JSON Schema for serialization) */
104
+ setTools(newTools: ToolDefinition[]): void;
105
+ /**
106
+ * Snapshot the fields that should survive across workflow runs
107
+ * (tasks + all custom state). Safe to pass directly to
108
+ * {@link ThreadOps.saveThreadState}. Rehydrate on the next run with
109
+ * `mergeUpdate({ tasks: new Map(slice.tasks), ...slice.custom })`.
110
+ */
111
+ getPersistedSlice(): PersistedThreadState;
112
+ /** Update the usage */
113
+ updateUsage(usage: TokenUsage): void;
114
+ /** Get the total usage */
115
+ getTotalUsage(): {
116
+ totalInputTokens: number;
117
+ totalOutputTokens: number;
118
+ totalCachedWriteTokens: number;
119
+ totalCachedReadTokens: number;
120
+ totalReasonTokens: number;
121
+ turns: number;
122
+ };
70
123
  }
124
+
71
125
  /**
72
- * Union type of all possible parsed tool calls from a tool map.
73
- */
74
- type ParsedToolCallUnion<T extends ToolMap> = {
75
- [K in keyof T]: ParsedToolCall<T[K]["name"], z.infer<T[K]["schema"]>>;
76
- }[keyof T];
77
- /**
78
- * Function signature for appending tool results to a thread.
79
- */
80
- type AppendToolResultFn = ActivityFunctionWithOptions<(id: string, config: ToolResultConfig) => Promise<void>>;
81
- /**
82
- * The response from a tool handler.
83
- * Contains the content for the tool message and the result to return from processToolCalls.
126
+ * Ephemeral virtual filesystem backed by a {@link FileResolver}.
84
127
  *
85
- * Tools that don't return additional data should use `data: null` (TResult defaults to null).
86
- * Tools that may fail to produce data should type TResult as `SomeType | null`.
128
+ * Created fresh for each tool invocation from the current workflow file tree.
129
+ * Directory structure is inferred from file paths. All mutations are tracked
130
+ * and can be retrieved via {@link getMutations} after the handler completes.
87
131
  */
88
- interface ToolHandlerResponse<TResult = null, TToolResponse = JsonValue> {
89
- /** Content sent back to the LLM as the tool call response */
90
- toolResponse: TToolResponse;
91
- /** Data returned to the workflow and hooks for further processing */
92
- data: TResult;
132
+ declare class VirtualFileSystem<TCtx = unknown, TMeta = FileEntryMetadata> implements SandboxFileSystem {
133
+ private resolver;
134
+ private ctx;
135
+ readonly workspaceBase: string;
136
+ private entries;
137
+ private directories;
138
+ private mutations;
139
+ private inlineFiles;
140
+ constructor(tree: FileEntry<TMeta>[], resolver: FileResolver<TCtx, TMeta>, ctx: TCtx, workspaceBase?: string, inlineFiles?: Record<string, string>);
141
+ /** Return all mutations accumulated during this invocation. */
142
+ getMutations(): TreeMutation<TMeta>[];
143
+ /** Look up a file entry by virtual path. */
144
+ getEntry(path: string): FileEntry<TMeta> | undefined;
145
+ readFile(path: string): Promise<string>;
146
+ readFileBuffer(path: string): Promise<Uint8Array>;
93
147
  /**
94
- * When true, the tool result has already been appended to the thread
95
- * by the handler itself (e.g. via `withAutoAppend`), so the router
96
- * will skip the `appendToolResult` call. This avoids sending large
97
- * payloads through Temporal's activity payload limit.
148
+ * Resolve the string content for an entry, preferring inline content
149
+ * carried on the entry itself before consulting the resolver. Used by
150
+ * `readFile`, `appendFile`, and `cp` so all read paths agree on the
151
+ * lookup precedence.
98
152
  */
99
- resultAppended?: boolean;
153
+ private readEntryContent;
154
+ exists(path: string): Promise<boolean>;
155
+ stat(path: string): Promise<FileStat>;
156
+ readdir(path: string): Promise<string[]>;
157
+ readdirWithFileTypes(path: string): Promise<DirentEntry[]>;
158
+ writeFile(path: string, content: string | Uint8Array): Promise<void>;
159
+ appendFile(path: string, content: string | Uint8Array): Promise<void>;
160
+ mkdir(_path: string, _options?: {
161
+ recursive?: boolean;
162
+ }): Promise<void>;
163
+ rm(path: string, options?: {
164
+ recursive?: boolean;
165
+ force?: boolean;
166
+ }): Promise<void>;
167
+ cp(src: string, dest: string, _options?: {
168
+ recursive?: boolean;
169
+ }): Promise<void>;
170
+ mv(src: string, dest: string): Promise<void>;
171
+ readlink(_path: string): Promise<string>;
172
+ resolvePath(base: string, path: string): string;
173
+ private addParentDirectories;
174
+ }
175
+
176
+ /** Allowed value types for file-entry metadata. */
177
+ type FileEntryMetadata = Record<string, string | number | boolean | null>;
178
+ /** JSON-serializable metadata for a single file in the virtual tree. */
179
+ interface FileEntry<TMeta = FileEntryMetadata> {
180
+ id: string;
181
+ /** Virtual path, e.g. "/src/index.ts" */
182
+ path: string;
183
+ size: number;
184
+ /** ISO-8601 date string (JSON-safe) */
185
+ mtime: string;
186
+ metadata: TMeta;
100
187
  /**
101
- * When true, the session will rewind: any in-flight parallel tool
102
- * calls are cancelled and the LLM call is retried. The session reuses
103
- * the same `assistantMessageId` for the retry; the next `runAgent`
104
- * activity truncates the thread from that id on entry, wiping the
105
- * triggering assistant message and any tool results already appended
106
- * before re-invoking the LLM.
188
+ * Optional inline content carried directly on the entry. When present the
189
+ * {@link VirtualFileSystem} returns this string from `readFile` /
190
+ * `readFileBuffer` (and uses it as the source for `cp` / `appendFile`)
191
+ * without consulting the resolver.
107
192
  *
108
- * The `toolResponse` for a rewinding tool call is ignored (never
109
- * appended) since the thread is rewound back to the pre-assistant
110
- * state on the next invocation.
111
- */
112
- rewind?: boolean;
113
- /** Token usage from the tool execution (e.g. child agent invocations) */
114
- usage?: TokenUsage;
115
- /** Thread ID used by the handler (surfaced to the LLM for subagent thread continuation) */
116
- threadId?: string;
117
- /** Sandbox ID created or used by the handler (e.g. child agent sandbox) */
118
- sandboxId?: string;
119
- /** Snapshot captured on exit when `sandboxShutdown === "snapshot"`. */
120
- snapshot?: SandboxSnapshot;
121
- /** Snapshot captured immediately after sandbox seeding (before the agent loop starts) when `sandbox.mode === "new"` and `sandboxShutdown === "snapshot"`. Intended as a reusable "base" for new threads that want to skip re-seeding. */
122
- baseSnapshot?: SandboxSnapshot;
123
- /** Unvalidated metadata passthrough from handler to hooks (e.g. infrastructure state) */
124
- metadata?: Record<string, unknown>;
125
- }
126
- /**
127
- * Base context the router always injects into every handler invocation.
128
- * Handlers can rely on these fields being present without casting.
129
- */
130
- interface RouterContext {
131
- threadId: string;
132
- /** Redis key suffix for thread storage. Defaults to 'messages'. */
133
- threadKey?: string;
134
- toolCallId: string;
135
- toolName: string;
136
- sandboxId?: string;
137
- /**
138
- * Id of the assistant message that issued this tool call (the message
139
- * the session passed as `assistantMessageId` into `runAgent`). Present
140
- * for any tool call processed through `processToolCalls` from a
141
- * session; may be absent when the router is driven manually (e.g.
142
- * tests, custom orchestrators).
143
- *
144
- * Subagent handlers that fork the parent's thread mid-call use this
145
- * to truncate the orphan trailing assistant message from the forked
146
- * thread so the child's first model call sees a well-formed history.
147
- */
148
- assistantMessageId?: string;
149
- /**
150
- * Persist the parent session's current `PersistedThreadState` slice
151
- * (tasks + custom state) to the durable thread store. Wired up by
152
- * the session — absent for manually-driven routers (tests, custom
153
- * orchestrators).
193
+ * Use this for files that exist purely in workflow state and have no
194
+ * backing in the consumer's data layer (e.g. skill resources bundled at
195
+ * session creation time). Because the content travels with the entry,
196
+ * any tool handler that constructs a `VirtualFileSystem` from `fileTree`
197
+ * sees the same content — no separate `inlineFiles` plumbing required.
154
198
  *
155
- * Subagent handlers invoke this before spawning a child that will
156
- * read the parent's thread (`newThreadSource: "from-parent"` or an
157
- * explicit parent threadId): the parent's slice otherwise only
158
- * lands in storage at session-exit time, so the child would load a
159
- * stale (or empty) snapshot. Best-effort failures are logged by
160
- * the session but never thrown.
199
+ * Read-only: entries with `inlineContent` reject in-place mutations
200
+ * (`writeFile`, `appendFile`, `rm`, `mv` of the entry, `cp` over the
201
+ * entry as destination). The resolver has no contract for the synthetic
202
+ * `id` these entries carry, so attempting a mutation throws an `EROFS`
203
+ * error instead of silently routing a doomed call through the resolver.
161
204
  */
162
- persistThreadState?: () => Promise<void>;
205
+ inlineContent?: string;
163
206
  }
164
207
  /**
165
- * A handler function for a specific tool.
166
- * Receives the parsed args and a context that always includes {@link RouterContext}
167
- * fields, plus any additional properties when TContext extends RouterContext.
208
+ * Flat list of file entries.
209
+ * Directories are inferred from file paths at runtime.
168
210
  */
169
- type ToolHandler<TArgs, TResult, TContext extends RouterContext = RouterContext, TToolResponse = JsonValue> = (args: TArgs, context: TContext) => ToolHandlerResponse<TResult, TToolResponse> | Promise<ToolHandlerResponse<TResult, TToolResponse>>;
211
+ type VirtualFileTree<TMeta = FileEntryMetadata> = FileEntry<TMeta>[];
212
+ type TreeMutation<TMeta = FileEntryMetadata> = {
213
+ type: "add";
214
+ entry: FileEntry<TMeta>;
215
+ } | {
216
+ type: "remove";
217
+ path: string;
218
+ } | {
219
+ type: "update";
220
+ path: string;
221
+ entry: Partial<FileEntry<TMeta>>;
222
+ };
170
223
  /**
171
- * Activity-compatible tool handler that always returns a Promise.
172
- * Use this for tool handlers registered as Temporal activities.
224
+ * Consumer-provided bridge to the existing DB / S3 / CRUD layer.
173
225
  *
174
- * @example
175
- * ```typescript
176
- * const readHandler: ActivityToolHandler<
177
- * FileReadArgs,
178
- * ReadResult,
179
- * RouterContext & { scopedNodes: FileNode[]; provider: FileSystemProvider }
180
- * > = async (args, context) => {
181
- * // context.threadId, context.sandboxId etc. are always available
182
- * return readHandler(args, context.scopedNodes, context.provider);
183
- * };
184
- * ```
226
+ * Generic over `TCtx` so every call receives workflow-level context
227
+ * (e.g. `{ projectId: string }`) without the resolver holding state.
228
+ *
229
+ * Generic over `TMeta` so resolved entries carry typed metadata.
185
230
  */
186
- type ActivityToolHandler<TArgs, TResult, TContext extends RouterContext = RouterContext, TToolResponse = JsonValue> = (args: TArgs, context: TContext) => Promise<ToolHandlerResponse<TResult, TToolResponse>>;
231
+ interface FileResolver<TCtx = unknown, TMeta = FileEntryMetadata> {
232
+ resolveEntries(ctx: TCtx): Promise<FileEntry<TMeta>[]>;
233
+ readFile(id: string, ctx: TCtx, metadata: TMeta): Promise<string>;
234
+ readFileBuffer(id: string, ctx: TCtx, metadata: TMeta): Promise<Uint8Array>;
235
+ writeFile(id: string, content: string | Uint8Array, ctx: TCtx, metadata: TMeta): Promise<void>;
236
+ createFile(path: string, content: string | Uint8Array, ctx: TCtx): Promise<FileEntry<TMeta>>;
237
+ deleteFile(id: string, ctx: TCtx, metadata: TMeta): Promise<void>;
238
+ }
187
239
  /**
188
- * Extract the args type for a specific tool name from a tool map.
240
+ * Workflow-side operations for the virtual filesystem.
241
+ *
242
+ * Unlike {@link SandboxOps}, this only exposes what is actually needed:
243
+ * resolving the initial file tree from the consumer's data layer.
189
244
  */
190
- type ToolArgs<T extends ToolMap, TName extends ToolNames<T>> = z.infer<Extract<T[keyof T], {
191
- name: TName;
192
- }>["schema"]>;
245
+ interface VirtualFsOps<TCtx = unknown, TMeta = FileEntryMetadata> {
246
+ resolveFileTree(ctx: TCtx): Promise<{
247
+ fileTree: FileEntry<TMeta>[];
248
+ }>;
249
+ }
193
250
  /**
194
- * Extract the result type for a specific tool name from a tool map.
251
+ * Maps generic {@link VirtualFsOps} method names to scope-prefixed names.
252
+ *
253
+ * @example
254
+ * ```typescript
255
+ * type Ops = PrefixedVirtualFsOps<"codingAgent">;
256
+ * // → { virtualFsCodingAgentResolveFileTree: ... }
257
+ * ```
195
258
  */
196
- type ToolResult<T extends ToolMap, TName extends ToolNames<T>> = Extract<T[keyof T], {
197
- name: TName;
198
- }>["handler"] extends ToolHandler<unknown, infer R, RouterContext, any> ? Awaited<R> : never;
259
+ type PrefixedVirtualFsOps<TPrefix extends string, TCtx = unknown, TMeta = FileEntryMetadata> = {
260
+ [K in keyof VirtualFsOps<TCtx, TMeta> as `virtualFs${Capitalize<TPrefix>}${Capitalize<K & string>}`]: VirtualFsOps<TCtx, TMeta>[K];
261
+ };
199
262
  /**
200
- * The result of processing a tool call.
263
+ * The portion of workflow `AgentState` that the virtual filesystem reads via
264
+ * {@link queryParentWorkflowState}. Populated automatically by the session
265
+ * when `virtualFs` config is provided.
201
266
  */
202
- interface ToolCallResult<TName extends string = string, TResult = unknown> {
203
- toolCallId: string;
204
- name: TName;
205
- data: TResult;
206
- usage?: TokenUsage;
207
- /** Unvalidated metadata passthrough from handler to hooks (e.g. infrastructure state) */
208
- metadata?: Record<string, unknown>;
267
+ interface VirtualFsState<TCtx = unknown, TMeta = FileEntryMetadata> {
268
+ fileTree: FileEntry<TMeta>[];
269
+ virtualFsCtx: TCtx;
270
+ /** In-memory file contents keyed by path, bypassing the resolver (e.g. skill resources). */
271
+ inlineFiles?: Record<string, string>;
209
272
  }
210
273
  /**
211
- * Infer result types from a tool map based on handler return types.
274
+ * Extended router context injected by {@link withVirtualFs}.
275
+ * Guarantees a live (ephemeral) virtual filesystem built from the workflow
276
+ * file tree.
212
277
  */
213
- type InferToolResults<T extends ToolMap> = {
214
- [K in keyof T as T[K]["name"]]: T[K]["handler"] extends ToolHandler<any, infer R, any, any> ? Awaited<R> : never;
215
- };
278
+ interface VirtualFsContext<TCtx = unknown, TMeta = FileEntryMetadata> extends RouterContext {
279
+ virtualFs: VirtualFileSystem<TCtx, TMeta>;
280
+ }
281
+
216
282
  /**
217
- * Union of all possible tool call results based on handler return types.
283
+ * Agent execution status
218
284
  */
219
- type ToolCallResultUnion<TResults extends Record<string, unknown>> = {
220
- [TName in keyof TResults & string]: ToolCallResult<TName, TResults[TName]>;
221
- }[keyof TResults & string];
285
+ type AgentStatus = "RUNNING" | "WAITING_FOR_INPUT" | "COMPLETED" | "FAILED" | "CANCELLED";
222
286
  /**
223
- * Context passed to processToolCalls for hook execution and handler invocation
287
+ * Base state that all agents must have
224
288
  */
225
- interface ProcessToolCallsContext {
226
- /** Current turn number (for hooks) */
227
- turn?: number;
228
- /** Active sandbox ID (when a sandbox is configured for this session) */
229
- sandboxId?: string;
230
- /**
231
- * Id of the assistant message that produced these tool calls. The
232
- * router forwards it into every handler's {@link RouterContext} so
233
- * handlers can reference the message they were issued from (e.g.
234
- * subagent forks that need to truncate the orphan assistant message
235
- * out of a parent-forked thread).
236
- */
237
- assistantMessageId?: string;
238
- /**
239
- * Optional callback that flushes the session's in-memory
240
- * `PersistedThreadState` slice to the durable thread store. The
241
- * router forwards it into every handler's {@link RouterContext}
242
- * verbatim. The session uses this to let mid-loop tool handlers
243
- * (notably subagents that fork or continue the parent's thread)
244
- * persist the parent's slice before the child reads it.
245
- */
246
- persistThreadState?: () => Promise<void>;
289
+ interface BaseAgentState {
290
+ tools: SerializableToolDefinition[];
291
+ status: AgentStatus;
292
+ version: number;
293
+ turns: number;
294
+ tasks: Map<string, WorkflowTask>;
295
+ fileTree: FileEntry[];
296
+ /** In-memory file contents keyed by path, bypassing the resolver (e.g. skill resources). */
297
+ inlineFiles?: Record<string, string>;
298
+ virtualFsCtx?: unknown;
299
+ systemPrompt?: unknown;
300
+ totalInputTokens: number;
301
+ totalOutputTokens: number;
302
+ cachedWriteTokens: number;
303
+ cachedReadTokens: number;
247
304
  }
248
305
  /**
249
- * Signal that a tool handler requested a rewind. Attached to the
250
- * {@link ProcessToolCallsResult} so the session can reuse the same
251
- * `assistantMessageId` for the retry; the next `runAgent` activity
252
- * then truncates the thread from that id on entry.
306
+ * File representation for agent workflows
253
307
  */
254
- interface RewindSignal {
255
- toolCallId: string;
256
- toolName: string;
308
+ interface AgentFile {
309
+ /** Database/S3 file ID */
310
+ id: string;
311
+ /** Virtual path for agent (e.g., "evidence/invoice.pdf") */
312
+ path: string;
313
+ /** Original filename */
314
+ filename: string;
315
+ /** Generic description for prompt */
316
+ description?: string;
317
+ /** MIME type of the file */
318
+ mimeType?: string;
319
+ }
320
+ interface TokenUsage {
321
+ inputTokens?: number;
322
+ outputTokens?: number;
323
+ cachedWriteTokens?: number;
324
+ cachedReadTokens?: number;
325
+ reasonTokens?: number;
257
326
  }
258
327
  /**
259
- * Result returned by {@link ToolRouter.processToolCalls}.
260
- *
261
- * The object is a standard array of tool call results for successful
262
- * tool calls (cancelled or rewinding siblings are omitted), extended
263
- * with a `rewind` property when any tool in the batch requested a
264
- * rewind. Using an array-with-property lets existing code that treats
265
- * the return value as `ToolCallResultUnion[]` continue to work.
328
+ * Configuration for a Zeitlich agent
266
329
  */
267
- type ProcessToolCallsResult<TResults extends Record<string, unknown>> = ToolCallResultUnion<TResults>[] & {
268
- rewind?: RewindSignal;
269
- };
330
+ interface AgentConfig {
331
+ /** The name of the agent, should be unique within the workflows, ideally Pascal Case */
332
+ agentName: string;
333
+ /** Description, used for sub agents */
334
+ description?: string;
335
+ }
270
336
  /**
271
- * Result from PreToolUse hook - can block or modify execution
337
+ * A JSON-serializable tool definition for state storage.
338
+ * Uses a plain JSON Schema object instead of a live Zod instance,
339
+ * so it survives Temporal serialization without losing constraints (min, max, etc.).
272
340
  */
273
- interface PreToolUseHookResult {
274
- /** Skip this tool call entirely */
275
- skip?: boolean;
276
- /** Modified args to use instead (must match schema) */
277
- modifiedArgs?: unknown;
341
+ interface SerializableToolDefinition {
342
+ name: string;
343
+ description: string;
344
+ schema: Record<string, unknown>;
345
+ strict?: boolean;
346
+ max_uses?: number;
278
347
  }
279
348
  /**
280
- * Result from PostToolUseFailure hook - can recover from errors
281
- */
282
- interface PostToolUseFailureHookResult {
283
- /** Provide a fallback result instead of throwing */
284
- fallbackContent?: JsonValue;
285
- /** Whether to suppress the error (still logs, but continues) */
286
- suppress?: boolean;
287
- }
288
- /**
289
- * Per-tool lifecycle hooks - defined directly on a tool definition.
290
- * Runs in addition to global hooks (global pre → tool pre → execute → tool post → global post).
349
+ * Configuration passed to runAgent activity
291
350
  */
292
- interface ToolHooks<TArgs = unknown, TResult = unknown> {
293
- /** Called before this tool executes - can skip or modify args */
294
- onPreToolUse?: (ctx: {
295
- args: TArgs;
296
- threadId: string;
297
- turn: number;
298
- }) => PreToolUseHookResult | Promise<PreToolUseHookResult>;
299
- /** Called after this tool executes successfully */
300
- onPostToolUse?: (ctx: {
301
- args: TArgs;
302
- result: TResult;
303
- threadId: string;
304
- turn: number;
305
- durationMs: number;
306
- metadata?: Record<string, unknown>;
307
- }) => void | Promise<void>;
308
- /** Called when this tool execution fails */
309
- onPostToolUseFailure?: (ctx: {
310
- args: TArgs;
311
- error: Error;
312
- threadId: string;
313
- turn: number;
314
- }) => PostToolUseFailureHookResult | Promise<PostToolUseFailureHookResult>;
351
+ interface RunAgentConfig extends AgentConfig {
352
+ /** The thread ID to use for the session */
353
+ threadId: string;
354
+ /** Redis key suffix for thread storage. Defaults to 'messages'. */
355
+ threadKey?: string;
356
+ /** Metadata for the session */
357
+ metadata?: Record<string, unknown>;
358
+ /**
359
+ * The id under which the assistant message produced by this call will
360
+ * be appended. The activity truncates the thread from this id on
361
+ * entry (no-op on the first attempt) so that:
362
+ *
363
+ * - Rewind retries can reuse the same id and the previous (bad)
364
+ * assistant + its tool results are wiped before the retry LLM call.
365
+ * - Resetting the Temporal workflow to this activity restores the
366
+ * pre-call thread state: replay re-truncates, re-invokes, and
367
+ * appends under the same id.
368
+ */
369
+ assistantMessageId: string;
315
370
  }
316
371
  /**
317
- * Context for PreToolUse hook - called before tool execution
372
+ * Configuration for appending a tool result
318
373
  */
319
- interface PreToolUseHookContext<T extends ToolMap> {
320
- /** The tool call about to be executed */
321
- toolCall: ParsedToolCallUnion<T>;
322
- /** Thread identifier */
374
+ interface ToolResultConfig {
323
375
  threadId: string;
324
- /** Current turn number */
325
- turn: number;
376
+ /** Redis key suffix for thread storage. Defaults to 'messages'. */
377
+ threadKey?: string;
378
+ toolCallId: string;
379
+ /** The name of the tool that produced this result */
380
+ toolName: string;
381
+ /** Content for the tool result — string, object, or array. The adapter converts to its SDK-native format. */
382
+ content: JsonValue;
326
383
  }
327
384
  /**
328
- * PreToolUse hook - called before tool execution, can block or modify
385
+ * Status of a workflow task
329
386
  */
330
- type PreToolUseHook<T extends ToolMap> = (ctx: PreToolUseHookContext<T>) => PreToolUseHookResult | Promise<PreToolUseHookResult>;
387
+ type TaskStatus = "pending" | "in_progress" | "completed";
331
388
  /**
332
- * Context for PostToolUse hook - called after successful tool execution
389
+ * A task managed within a workflow for tracking work items
333
390
  */
334
- interface PostToolUseHookContext<T extends ToolMap, TResult = unknown> {
335
- /** The tool call that was executed */
336
- toolCall: ParsedToolCallUnion<T>;
337
- /** The result from the tool handler */
338
- result: TResult;
339
- /** Thread identifier */
340
- threadId: string;
341
- /** Current turn number */
342
- turn: number;
343
- /** Execution duration in milliseconds */
344
- durationMs: number;
391
+ interface WorkflowTask {
392
+ /** Unique task identifier */
393
+ id: string;
394
+ /** Brief, actionable title in imperative form */
395
+ subject: string;
396
+ /** Detailed description of what needs to be done */
397
+ description: string;
398
+ /** Present continuous form shown in spinner when in_progress */
399
+ activeForm: string;
400
+ /** Current status of the task */
401
+ status: TaskStatus;
402
+ /** Arbitrary key-value pairs for tracking */
403
+ metadata: Record<string, string>;
404
+ /** IDs of tasks that must complete before this one can start */
405
+ blockedBy: string[];
406
+ /** IDs of tasks that are waiting for this one to complete */
407
+ blocks: string[];
345
408
  }
346
409
  /**
347
- * PostToolUse hook - called after successful tool execution
348
- */
349
- type PostToolUseHook<T extends ToolMap, TResult = unknown> = (ctx: PostToolUseHookContext<T, TResult>) => void | Promise<void>;
350
- /**
351
- * Context for PostToolUseFailure hook - called when tool execution fails
410
+ * Exit reasons for session termination
352
411
  */
353
- interface PostToolUseFailureHookContext<T extends ToolMap> {
354
- /** The tool call that failed */
355
- toolCall: ParsedToolCallUnion<T>;
356
- /** The error that occurred */
357
- error: Error;
358
- /** Thread identifier */
359
- threadId: string;
360
- /** Current turn number */
361
- turn: number;
362
- }
412
+ type SessionExitReason = "completed" | "max_turns" | "waiting_for_input" | "failed" | "cancelled";
363
413
  /**
364
- * PostToolUseFailure hook - called when tool execution fails
414
+ * Helper to check if status is terminal
365
415
  */
366
- type PostToolUseFailureHook<T extends ToolMap> = (ctx: PostToolUseFailureHookContext<T>) => PostToolUseFailureHookResult | Promise<PostToolUseFailureHookResult>;
416
+ declare function isTerminalStatus(status: AgentStatus): boolean;
417
+
367
418
  /**
368
- * Tool execution hooks the subset of hooks consumed by the tool router.
369
- * Session/message lifecycle hooks live in lib/hooks/types.ts.
419
+ * A tool definition with a name, description, and Zod schema for arguments.
420
+ * Does not include a handler - use ToolWithHandler for tools with handlers.
370
421
  */
371
- interface ToolRouterHooks<T extends ToolMap, TResult = unknown> {
372
- /** Called before each tool execution - can block or modify */
373
- onPreToolUse?: PreToolUseHook<T>;
374
- /** Called after each successful tool execution */
375
- onPostToolUse?: PostToolUseHook<T, TResult>;
376
- /** Called when tool execution fails */
377
- onPostToolUseFailure?: PostToolUseFailureHook<T>;
422
+ interface ToolDefinition<TName extends string = string, TSchema extends z.ZodType = z.ZodType> {
423
+ name: TName;
424
+ description: string;
425
+ schema: TSchema;
426
+ strict?: boolean;
427
+ max_uses?: number;
378
428
  }
379
429
  /**
380
- * Options for creating a tool router.
430
+ * A tool definition with an integrated handler function.
431
+ * This is the primary type for defining tools in the router.
381
432
  */
382
- interface ToolRouterOptions<T extends ToolMap> {
383
- /** Map of tools with their handlers */
384
- tools: T;
385
- /** Thread ID for appending tool results */
386
- threadId: string;
387
- /** Redis key suffix for thread storage. Defaults to 'messages'. */
388
- threadKey?: string;
389
- /** Function to append tool results to the thread (called automatically after each handler).
390
- * Accepts a Temporal activity proxy with {@link ActivityFunctionWithOptions}. */
391
- appendToolResult: AppendToolResultFn;
392
- /** Whether to process tools in parallel (default: true) */
393
- parallel?: boolean;
394
- /** Tool execution lifecycle hooks (pre/post tool use) */
395
- hooks?: ToolRouterHooks<T, ToolCallResultUnion<InferToolResults<T>>>;
396
- /** Additional tools to auto-register (e.g. subagent, skill tools built by register helpers) */
397
- plugins?: ToolMap[string][];
433
+ interface ToolWithHandler<TName extends string = string, TSchema extends z.ZodType = z.ZodType, TResult = unknown, TContext extends RouterContext = RouterContext, TToolResponse = JsonValue> {
434
+ name: TName;
435
+ description: string;
436
+ schema: TSchema;
437
+ handler: ToolHandler<z.infer<TSchema>, TResult, TContext, TToolResponse>;
438
+ strict?: boolean;
439
+ max_uses?: number;
440
+ /** Whether this tool is available to the agent (default: true). Disabled tools are excluded from definitions and rejected at parse time. */
441
+ enabled?: boolean | (() => boolean);
442
+ /** Per-tool lifecycle hooks (run in addition to global hooks) */
443
+ hooks?: ToolHooks<z.infer<TSchema>, TResult>;
398
444
  }
399
445
  /**
400
- * The tool router interface with full type inference for both args and results.
446
+ * A map of tool keys to tool definitions with handlers.
447
+ *
448
+ * Handler uses `any` intentionally — this is a type-system boundary where heterogeneous
449
+ * tool types are stored together. Type safety for individual tools is enforced by
450
+ * `defineTool()` at the definition site and generic inference utilities like
451
+ * `InferToolResults<T>` at the consumption site.
401
452
  */
402
- interface ToolRouter<T extends ToolMap> {
403
- /** Check if the router has any tools */
404
- hasTools(): boolean;
405
- /**
406
- * Parse and validate a raw tool call against the router's tools.
407
- * Returns a typed tool call with validated arguments.
408
- */
409
- parseToolCall(toolCall: RawToolCall): ParsedToolCallUnion<T>;
410
- /**
411
- * Check if a tool with the given name exists in the router.
412
- */
413
- hasTool(name: string): boolean;
414
- /**
415
- * Get all tool names in the router.
416
- */
417
- getToolNames(): ToolNames<T>[];
418
- /**
419
- * Get all tool definitions (without handlers) for passing to LLM.
420
- */
421
- getToolDefinitions(): ToolDefinition[];
422
- /**
423
- * Process all tool calls using the registered handlers.
424
- * Returns typed results based on handler return types.
425
- * @param toolCalls - Array of parsed tool calls to process
426
- * @param context - Optional context including turn number for hooks
427
- */
428
- processToolCalls(toolCalls: ParsedToolCallUnion<T>[], context?: ProcessToolCallsContext): Promise<ProcessToolCallsResult<InferToolResults<T>>>;
429
- /**
430
- * Process tool calls matching a specific name with a custom handler.
431
- * Useful for overriding the default handler for specific cases.
432
- */
433
- processToolCallsByName<TName extends ToolNames<T>, TResult>(toolCalls: ParsedToolCallUnion<T>[], toolName: TName, handler: ToolHandler<ToolArgs<T, TName>, TResult>, context?: ProcessToolCallsContext): Promise<ToolCallResult<TName, TResult>[]>;
434
- /**
435
- * Filter tool calls by name.
436
- */
437
- filterByName<TName extends ToolNames<T>>(toolCalls: ParsedToolCallUnion<T>[], name: TName): ParsedToolCall<TName, ToolArgs<T, TName>>[];
438
- /**
439
- * Check if any tool call matches the given name.
440
- */
441
- hasToolCall(toolCalls: ParsedToolCallUnion<T>[], name: ToolNames<T>): boolean;
442
- /**
443
- * Filter results by tool name.
444
- */
445
- getResultsByName<TName extends ToolNames<T>>(results: ToolCallResultUnion<InferToolResults<T>>[], name: TName): ToolCallResult<TName, ToolResult<T, TName>>[];
446
- }
447
-
453
+ type ToolMap = Record<string, {
454
+ name: string;
455
+ description: string | (() => string);
456
+ schema: z.ZodType | (() => z.ZodType);
457
+ handler: ToolHandler<any, any, any, any>;
458
+ strict?: boolean;
459
+ max_uses?: number;
460
+ enabled?: boolean | (() => boolean);
461
+ hooks?: ToolHooks<any, any>;
462
+ }>;
448
463
  /**
449
- * JSON primitive types that Temporal can serialize
464
+ * Extract the tool names from a tool map (uses the tool's name property, not the key).
450
465
  */
451
- type JsonPrimitive = string | number | boolean | null | undefined;
466
+ type ToolNames<T extends ToolMap> = T[keyof T]["name"];
452
467
  /**
453
- * JSON-serializable value (recursive type for Temporal compatibility)
468
+ * A raw tool call as received from the LLM before parsing.
454
469
  */
455
- type JsonValue = JsonPrimitive | JsonValue[] | {
456
- [key: string]: JsonValue;
457
- };
470
+ interface RawToolCall {
471
+ id?: string;
472
+ name: string;
473
+ args: unknown;
474
+ }
458
475
  /**
459
- * Type constraint ensuring T only contains JSON-serializable values.
460
- * Use this for custom state to ensure Temporal workflow compatibility.
461
- *
462
- * Allows: primitives, arrays, plain objects, and JsonValue
463
- * Rejects: functions, symbols, undefined, class instances with methods
476
+ * A parsed tool call with validated arguments for a specific tool.
464
477
  */
465
- type JsonSerializable<T> = {
466
- [K in keyof T]: T[K] extends JsonValue ? T[K] : T[K] extends JsonPrimitive ? T[K] : T[K] extends (infer U)[] ? U extends JsonValue ? T[K] : JsonSerializable<U>[] : T[K] extends object ? JsonSerializable<T[K]> : never;
467
- };
478
+ interface ParsedToolCall<TName extends string = string, TArgs = unknown> {
479
+ id: string;
480
+ name: TName;
481
+ args: TArgs;
482
+ }
468
483
  /**
469
- * Full state type combining base state with custom state
484
+ * Union type of all possible parsed tool calls from a tool map.
470
485
  */
471
- type AgentState<TCustom extends JsonSerializable<TCustom>> = BaseAgentState & TCustom;
486
+ type ParsedToolCallUnion<T extends ToolMap> = {
487
+ [K in keyof T]: ParsedToolCall<T[K]["name"], z.infer<T[K]["schema"]>>;
488
+ }[keyof T];
472
489
  /**
473
- * The slice of agent state that is persisted alongside the thread in the
474
- * thread store (e.g. Redis) so that a workflow can terminate, store its
475
- * state, and be continued or forked later with that state rehydrated.
476
- *
477
- * Only fields that make sense to carry across workflow runs belong here.
478
- * Runtime bookkeeping like status, version, turns, tools, fileTree, token
479
- * counters, and the system prompt is intentionally NOT persisted — each run
480
- * rebuilds those from scratch.
490
+ * Function signature for appending tool results to a thread.
481
491
  */
482
- interface PersistedThreadState {
483
- /** Task map serialized as entries so it round-trips through JSON. */
484
- tasks: [string, WorkflowTask][];
485
- /** All custom state fields declared by the caller. */
486
- custom: Record<string, JsonValue>;
487
- }
492
+ type AppendToolResultFn = ActivityFunctionWithOptions<(id: string, config: ToolResultConfig) => Promise<void>>;
488
493
  /**
489
- * Agent state manager interface
490
- * Note: Temporal handlers must be set up in the workflow file due to
491
- * Temporal's workflow isolation requirements. This manager provides
492
- * the state and helpers needed for those handlers.
494
+ * The response from a tool handler.
495
+ * Contains the content for the tool message and the result to return from processToolCalls.
496
+ *
497
+ * Tools that don't return additional data should use `data: null` (TResult defaults to null).
498
+ * Tools that may fail to produce data should type TResult as `SomeType | null`.
493
499
  */
494
- interface AgentStateManager<TCustom extends JsonSerializable<TCustom>> {
495
- /** Typed query definition registered for this agent's state */
496
- readonly stateQuery: QueryDefinition<AgentState<TCustom>>;
497
- /** Typed update definition registered for waiting on this agent's state change */
498
- readonly stateChangeUpdate: UpdateDefinition<AgentState<TCustom>, [number]>;
499
- /** Get current status */
500
- getStatus(): AgentStatus;
501
- /** Check if agent is running */
502
- isRunning(): boolean;
503
- /** Check if agent is in terminal state */
504
- isTerminal(): boolean;
505
- /** Get current state version */
506
- getVersion(): number;
507
- /** Set status to RUNNING */
508
- run(): void;
509
- /** Set status to WAITING_FOR_INPUT */
510
- waitForInput(): void;
511
- /** Set status to COMPLETED */
512
- complete(): void;
513
- /** Set status to FAILED */
514
- fail(): void;
515
- /** Set status to CANCELLED */
516
- cancel(): void;
517
- /** Increment state version (call after state changes) */
518
- incrementVersion(): void;
519
- /** Increment turns (call after each turn) */
520
- incrementTurns(): void;
521
- /** Get current turns */
522
- getTurns(): number;
523
- /** Get the system prompt */
524
- getSystemPrompt(): unknown;
525
- /** Set the system prompt */
526
- setSystemPrompt(newSystemPrompt: unknown): void;
527
- /** Get a custom state value by key */
528
- get<K extends keyof TCustom>(key: K): TCustom[K];
529
- /** Set a custom state value by key */
530
- set<K extends keyof TCustom>(key: K, value: TCustom[K]): void;
531
- /** Bulk-merge a partial update into custom state */
532
- mergeUpdate(update: Partial<AgentState<TCustom>>): void;
533
- /** Get full state for query handler */
534
- getCurrentState(): AgentState<TCustom>;
535
- /** Check if should return from waitForStateChange */
536
- shouldReturnFromWait(lastKnownVersion: number): boolean;
537
- /** Get all tasks */
538
- getTasks(): WorkflowTask[];
539
- /** Get a task by ID */
540
- getTask(id: string): WorkflowTask | undefined;
541
- /** Add or update a task */
542
- setTask(task: WorkflowTask): void;
543
- /** Delete a task by ID */
544
- deleteTask(id: string): boolean;
545
- /** Set the tools (converts Zod schemas to JSON Schema for serialization) */
546
- setTools(newTools: ToolDefinition[]): void;
500
+ interface ToolHandlerResponse<TResult = null, TToolResponse = JsonValue> {
501
+ /** Content sent back to the LLM as the tool call response */
502
+ toolResponse: TToolResponse;
503
+ /** Data returned to the workflow and hooks for further processing */
504
+ data: TResult;
547
505
  /**
548
- * Snapshot the fields that should survive across workflow runs
549
- * (tasks + all custom state). Safe to pass directly to
550
- * {@link ThreadOps.saveThreadState}. Rehydrate on the next run with
551
- * `mergeUpdate({ tasks: new Map(slice.tasks), ...slice.custom })`.
506
+ * When true, the tool result has already been appended to the thread
507
+ * by the handler itself (e.g. via `withAutoAppend`), so the router
508
+ * will skip the `appendToolResult` call. This avoids sending large
509
+ * payloads through Temporal's activity payload limit.
552
510
  */
553
- getPersistedSlice(): PersistedThreadState;
554
- /** Update the usage */
555
- updateUsage(usage: TokenUsage): void;
556
- /** Get the total usage */
557
- getTotalUsage(): {
558
- totalInputTokens: number;
559
- totalOutputTokens: number;
560
- totalCachedWriteTokens: number;
561
- totalCachedReadTokens: number;
562
- totalReasonTokens: number;
563
- turns: number;
564
- };
511
+ resultAppended?: boolean;
512
+ /**
513
+ * When true, the session will rewind: any in-flight parallel tool
514
+ * calls are cancelled and the LLM call is retried. The session reuses
515
+ * the same `assistantMessageId` for the retry; the next `runAgent`
516
+ * activity truncates the thread from that id on entry, wiping the
517
+ * triggering assistant message and any tool results already appended
518
+ * before re-invoking the LLM.
519
+ *
520
+ * The `toolResponse` for a rewinding tool call is ignored (never
521
+ * appended) since the thread is rewound back to the pre-assistant
522
+ * state on the next invocation.
523
+ */
524
+ rewind?: boolean;
525
+ /** Token usage from the tool execution (e.g. child agent invocations) */
526
+ usage?: TokenUsage;
527
+ /** Thread ID used by the handler (surfaced to the LLM for subagent thread continuation) */
528
+ threadId?: string;
529
+ /** Sandbox ID created or used by the handler (e.g. child agent sandbox) */
530
+ sandboxId?: string;
531
+ /** Snapshot captured on exit when `sandboxShutdown === "snapshot"`. */
532
+ snapshot?: SandboxSnapshot;
533
+ /** Snapshot captured immediately after sandbox seeding (before the agent loop starts) when `sandbox.mode === "new"` and `sandboxShutdown === "snapshot"`. Intended as a reusable "base" for new threads that want to skip re-seeding. */
534
+ baseSnapshot?: SandboxSnapshot;
535
+ /** Unvalidated metadata passthrough from handler to hooks (e.g. infrastructure state) */
536
+ metadata?: Record<string, unknown>;
565
537
  }
566
-
567
538
  /**
568
- * Ephemeral virtual filesystem backed by a {@link FileResolver}.
569
- *
570
- * Created fresh for each tool invocation from the current workflow file tree.
571
- * Directory structure is inferred from file paths. All mutations are tracked
572
- * and can be retrieved via {@link getMutations} after the handler completes.
539
+ * Base context the router always injects into every handler invocation.
540
+ * Handlers can rely on these fields being present without casting.
573
541
  */
574
- declare class VirtualFileSystem<TCtx = unknown, TMeta = FileEntryMetadata> implements SandboxFileSystem {
575
- private resolver;
576
- private ctx;
577
- readonly workspaceBase: string;
578
- private entries;
579
- private directories;
580
- private mutations;
581
- private inlineFiles;
582
- constructor(tree: FileEntry<TMeta>[], resolver: FileResolver<TCtx, TMeta>, ctx: TCtx, workspaceBase?: string, inlineFiles?: Record<string, string>);
583
- /** Return all mutations accumulated during this invocation. */
584
- getMutations(): TreeMutation<TMeta>[];
585
- /** Look up a file entry by virtual path. */
586
- getEntry(path: string): FileEntry<TMeta> | undefined;
587
- readFile(path: string): Promise<string>;
588
- readFileBuffer(path: string): Promise<Uint8Array>;
542
+ interface RouterContext {
543
+ threadId: string;
544
+ /** Redis key suffix for thread storage. Defaults to 'messages'. */
545
+ threadKey?: string;
546
+ toolCallId: string;
547
+ toolName: string;
548
+ sandboxId?: string;
589
549
  /**
590
- * Resolve the string content for an entry, preferring inline content
591
- * carried on the entry itself before consulting the resolver. Used by
592
- * `readFile`, `appendFile`, and `cp` so all read paths agree on the
593
- * lookup precedence.
550
+ * Id of the assistant message that issued this tool call (the message
551
+ * the session passed as `assistantMessageId` into `runAgent`). Present
552
+ * for any tool call processed through `processToolCalls` from a
553
+ * session; may be absent when the router is driven manually (e.g.
554
+ * tests, custom orchestrators).
555
+ *
556
+ * Subagent handlers that fork the parent's thread mid-call use this
557
+ * to truncate the orphan trailing assistant message from the forked
558
+ * thread so the child's first model call sees a well-formed history.
594
559
  */
595
- private readEntryContent;
596
- exists(path: string): Promise<boolean>;
597
- stat(path: string): Promise<FileStat>;
598
- readdir(path: string): Promise<string[]>;
599
- readdirWithFileTypes(path: string): Promise<DirentEntry[]>;
600
- writeFile(path: string, content: string | Uint8Array): Promise<void>;
601
- appendFile(path: string, content: string | Uint8Array): Promise<void>;
602
- mkdir(_path: string, _options?: {
603
- recursive?: boolean;
604
- }): Promise<void>;
605
- rm(path: string, options?: {
606
- recursive?: boolean;
607
- force?: boolean;
608
- }): Promise<void>;
609
- cp(src: string, dest: string, _options?: {
610
- recursive?: boolean;
611
- }): Promise<void>;
612
- mv(src: string, dest: string): Promise<void>;
613
- readlink(_path: string): Promise<string>;
614
- resolvePath(base: string, path: string): string;
615
- private addParentDirectories;
616
- }
617
-
618
- /** Allowed value types for file-entry metadata. */
619
- type FileEntryMetadata = Record<string, string | number | boolean | null>;
620
- /** JSON-serializable metadata for a single file in the virtual tree. */
621
- interface FileEntry<TMeta = FileEntryMetadata> {
622
- id: string;
623
- /** Virtual path, e.g. "/src/index.ts" */
624
- path: string;
625
- size: number;
626
- /** ISO-8601 date string (JSON-safe) */
627
- mtime: string;
628
- metadata: TMeta;
560
+ assistantMessageId?: string;
629
561
  /**
630
- * Optional inline content carried directly on the entry. When present the
631
- * {@link VirtualFileSystem} returns this string from `readFile` /
632
- * `readFileBuffer` (and uses it as the source for `cp` / `appendFile`)
633
- * without consulting the resolver.
634
- *
635
- * Use this for files that exist purely in workflow state and have no
636
- * backing in the consumer's data layer (e.g. skill resources bundled at
637
- * session creation time). Because the content travels with the entry,
638
- * any tool handler that constructs a `VirtualFileSystem` from `fileTree`
639
- * sees the same content — no separate `inlineFiles` plumbing required.
562
+ * Persist the parent session's current `PersistedThreadState` slice
563
+ * (tasks + custom state) to the durable thread store. Wired up by
564
+ * the session absent for manually-driven routers (tests, custom
565
+ * orchestrators).
640
566
  *
641
- * Read-only: entries with `inlineContent` reject in-place mutations
642
- * (`writeFile`, `appendFile`, `rm`, `mv` of the entry, `cp` over the
643
- * entry as destination). The resolver has no contract for the synthetic
644
- * `id` these entries carry, so attempting a mutation throws an `EROFS`
645
- * error instead of silently routing a doomed call through the resolver.
567
+ * Subagent handlers invoke this before spawning a child that will
568
+ * read the parent's thread (`newThreadSource: "from-parent"` or an
569
+ * explicit parent threadId): the parent's slice otherwise only
570
+ * lands in storage at session-exit time, so the child would load a
571
+ * stale (or empty) snapshot. Best-effort failures are logged by
572
+ * the session but never thrown.
646
573
  */
647
- inlineContent?: string;
574
+ persistThreadState?: () => Promise<void>;
648
575
  }
649
576
  /**
650
- * Flat list of file entries.
651
- * Directories are inferred from file paths at runtime.
577
+ * A handler function for a specific tool.
578
+ * Receives the parsed args and a context that always includes {@link RouterContext}
579
+ * fields, plus any additional properties when TContext extends RouterContext.
652
580
  */
653
- type VirtualFileTree<TMeta = FileEntryMetadata> = FileEntry<TMeta>[];
654
- type TreeMutation<TMeta = FileEntryMetadata> = {
655
- type: "add";
656
- entry: FileEntry<TMeta>;
657
- } | {
658
- type: "remove";
659
- path: string;
660
- } | {
661
- type: "update";
662
- path: string;
663
- entry: Partial<FileEntry<TMeta>>;
664
- };
581
+ type ToolHandler<TArgs, TResult, TContext extends RouterContext = RouterContext, TToolResponse = JsonValue> = (args: TArgs, context: TContext) => ToolHandlerResponse<TResult, TToolResponse> | Promise<ToolHandlerResponse<TResult, TToolResponse>>;
665
582
  /**
666
- * Consumer-provided bridge to the existing DB / S3 / CRUD layer.
667
- *
668
- * Generic over `TCtx` so every call receives workflow-level context
669
- * (e.g. `{ projectId: string }`) without the resolver holding state.
583
+ * Activity-compatible tool handler that always returns a Promise.
584
+ * Use this for tool handlers registered as Temporal activities.
670
585
  *
671
- * Generic over `TMeta` so resolved entries carry typed metadata.
586
+ * @example
587
+ * ```typescript
588
+ * const readHandler: ActivityToolHandler<
589
+ * FileReadArgs,
590
+ * ReadResult,
591
+ * RouterContext & { scopedNodes: FileNode[]; provider: FileSystemProvider }
592
+ * > = async (args, context) => {
593
+ * // context.threadId, context.sandboxId etc. are always available
594
+ * return readHandler(args, context.scopedNodes, context.provider);
595
+ * };
596
+ * ```
672
597
  */
673
- interface FileResolver<TCtx = unknown, TMeta = FileEntryMetadata> {
674
- resolveEntries(ctx: TCtx): Promise<FileEntry<TMeta>[]>;
675
- readFile(id: string, ctx: TCtx, metadata: TMeta): Promise<string>;
676
- readFileBuffer(id: string, ctx: TCtx, metadata: TMeta): Promise<Uint8Array>;
677
- writeFile(id: string, content: string | Uint8Array, ctx: TCtx, metadata: TMeta): Promise<void>;
678
- createFile(path: string, content: string | Uint8Array, ctx: TCtx): Promise<FileEntry<TMeta>>;
679
- deleteFile(id: string, ctx: TCtx, metadata: TMeta): Promise<void>;
680
- }
598
+ type ActivityToolHandler<TArgs, TResult, TContext extends RouterContext = RouterContext, TToolResponse = JsonValue> = (args: TArgs, context: TContext) => Promise<ToolHandlerResponse<TResult, TToolResponse>>;
681
599
  /**
682
- * Workflow-side operations for the virtual filesystem.
683
- *
684
- * Unlike {@link SandboxOps}, this only exposes what is actually needed:
685
- * resolving the initial file tree from the consumer's data layer.
600
+ * Extract the args type for a specific tool name from a tool map.
686
601
  */
687
- interface VirtualFsOps<TCtx = unknown, TMeta = FileEntryMetadata> {
688
- resolveFileTree(ctx: TCtx): Promise<{
689
- fileTree: FileEntry<TMeta>[];
690
- }>;
602
+ type ToolArgs<T extends ToolMap, TName extends ToolNames<T>> = z.infer<Extract<T[keyof T], {
603
+ name: TName;
604
+ }>["schema"]>;
605
+ /**
606
+ * Extract the result type for a specific tool name from a tool map.
607
+ */
608
+ type ToolResult<T extends ToolMap, TName extends ToolNames<T>> = Extract<T[keyof T], {
609
+ name: TName;
610
+ }>["handler"] extends ToolHandler<unknown, infer R, RouterContext, any> ? Awaited<R> : never;
611
+ /**
612
+ * The result of processing a tool call.
613
+ */
614
+ interface ToolCallResult<TName extends string = string, TResult = unknown> {
615
+ toolCallId: string;
616
+ name: TName;
617
+ data: TResult;
618
+ usage?: TokenUsage;
619
+ /** Unvalidated metadata passthrough from handler to hooks (e.g. infrastructure state) */
620
+ metadata?: Record<string, unknown>;
691
621
  }
692
622
  /**
693
- * Maps generic {@link VirtualFsOps} method names to scope-prefixed names.
694
- *
695
- * @example
696
- * ```typescript
697
- * type Ops = PrefixedVirtualFsOps<"codingAgent">;
698
- * // → { virtualFsCodingAgentResolveFileTree: ... }
699
- * ```
623
+ * Infer result types from a tool map based on handler return types.
700
624
  */
701
- type PrefixedVirtualFsOps<TPrefix extends string, TCtx = unknown, TMeta = FileEntryMetadata> = {
702
- [K in keyof VirtualFsOps<TCtx, TMeta> as `virtualFs${Capitalize<TPrefix>}${Capitalize<K & string>}`]: VirtualFsOps<TCtx, TMeta>[K];
625
+ type InferToolResults<T extends ToolMap> = {
626
+ [K in keyof T as T[K]["name"]]: T[K]["handler"] extends ToolHandler<any, infer R, any, any> ? Awaited<R> : never;
703
627
  };
704
628
  /**
705
- * The portion of workflow `AgentState` that the virtual filesystem reads via
706
- * {@link queryParentWorkflowState}. Populated automatically by the session
707
- * when `virtualFs` config is provided.
629
+ * Union of all possible tool call results based on handler return types.
708
630
  */
709
- interface VirtualFsState<TCtx = unknown, TMeta = FileEntryMetadata> {
710
- fileTree: FileEntry<TMeta>[];
711
- virtualFsCtx: TCtx;
712
- /** In-memory file contents keyed by path, bypassing the resolver (e.g. skill resources). */
713
- inlineFiles?: Record<string, string>;
631
+ type ToolCallResultUnion<TResults extends Record<string, unknown>> = {
632
+ [TName in keyof TResults & string]: ToolCallResult<TName, TResults[TName]>;
633
+ }[keyof TResults & string];
634
+ /**
635
+ * Context passed to processToolCalls for hook execution and handler invocation
636
+ */
637
+ interface ProcessToolCallsContext {
638
+ /** Current turn number (for hooks) */
639
+ turn?: number;
640
+ /** Active sandbox ID (when a sandbox is configured for this session) */
641
+ sandboxId?: string;
642
+ /**
643
+ * Id of the assistant message that produced these tool calls. The
644
+ * router forwards it into every handler's {@link RouterContext} so
645
+ * handlers can reference the message they were issued from (e.g.
646
+ * subagent forks that need to truncate the orphan assistant message
647
+ * out of a parent-forked thread).
648
+ */
649
+ assistantMessageId?: string;
650
+ /**
651
+ * Optional callback that flushes the session's in-memory
652
+ * `PersistedThreadState` slice to the durable thread store. The
653
+ * router forwards it into every handler's {@link RouterContext}
654
+ * verbatim. The session uses this to let mid-loop tool handlers
655
+ * (notably subagents that fork or continue the parent's thread)
656
+ * persist the parent's slice before the child reads it.
657
+ */
658
+ persistThreadState?: () => Promise<void>;
714
659
  }
715
660
  /**
716
- * Extended router context injected by {@link withVirtualFs}.
717
- * Guarantees a live (ephemeral) virtual filesystem built from the workflow
718
- * file tree.
661
+ * Signal that a tool handler requested a rewind. Attached to the
662
+ * {@link ProcessToolCallsResult} so the session can reuse the same
663
+ * `assistantMessageId` for the retry; the next `runAgent` activity
664
+ * then truncates the thread from that id on entry.
719
665
  */
720
- interface VirtualFsContext<TCtx = unknown, TMeta = FileEntryMetadata> extends RouterContext {
721
- virtualFs: VirtualFileSystem<TCtx, TMeta>;
666
+ interface RewindSignal {
667
+ toolCallId: string;
668
+ toolName: string;
722
669
  }
723
-
724
670
  /**
725
- * Agent execution status
671
+ * Result returned by {@link ToolRouter.processToolCalls}.
672
+ *
673
+ * The object is a standard array of tool call results for successful
674
+ * tool calls (cancelled or rewinding siblings are omitted), extended
675
+ * with a `rewind` property when any tool in the batch requested a
676
+ * rewind. Using an array-with-property lets existing code that treats
677
+ * the return value as `ToolCallResultUnion[]` continue to work.
726
678
  */
727
- type AgentStatus = "RUNNING" | "WAITING_FOR_INPUT" | "COMPLETED" | "FAILED" | "CANCELLED";
679
+ type ProcessToolCallsResult<TResults extends Record<string, unknown>> = ToolCallResultUnion<TResults>[] & {
680
+ rewind?: RewindSignal;
681
+ };
728
682
  /**
729
- * Base state that all agents must have
683
+ * Result from PreToolUse hook - can block or modify execution
730
684
  */
731
- interface BaseAgentState {
732
- tools: SerializableToolDefinition[];
733
- status: AgentStatus;
734
- version: number;
735
- turns: number;
736
- tasks: Map<string, WorkflowTask>;
737
- fileTree: FileEntry[];
738
- /** In-memory file contents keyed by path, bypassing the resolver (e.g. skill resources). */
739
- inlineFiles?: Record<string, string>;
740
- virtualFsCtx?: unknown;
741
- systemPrompt?: unknown;
742
- totalInputTokens: number;
743
- totalOutputTokens: number;
744
- cachedWriteTokens: number;
745
- cachedReadTokens: number;
685
+ interface PreToolUseHookResult {
686
+ /** Skip this tool call entirely */
687
+ skip?: boolean;
688
+ /** Modified args to use instead (must match schema) */
689
+ modifiedArgs?: unknown;
746
690
  }
747
691
  /**
748
- * File representation for agent workflows
692
+ * Result from PostToolUseFailure hook - can recover from errors
749
693
  */
750
- interface AgentFile {
751
- /** Database/S3 file ID */
752
- id: string;
753
- /** Virtual path for agent (e.g., "evidence/invoice.pdf") */
754
- path: string;
755
- /** Original filename */
756
- filename: string;
757
- /** Generic description for prompt */
758
- description?: string;
759
- /** MIME type of the file */
760
- mimeType?: string;
761
- }
762
- interface TokenUsage {
763
- inputTokens?: number;
764
- outputTokens?: number;
765
- cachedWriteTokens?: number;
766
- cachedReadTokens?: number;
767
- reasonTokens?: number;
694
+ interface PostToolUseFailureHookResult {
695
+ /** Provide a fallback result instead of throwing */
696
+ fallbackContent?: JsonValue;
697
+ /** Whether to suppress the error (still logs, but continues) */
698
+ suppress?: boolean;
768
699
  }
769
700
  /**
770
- * Configuration for a Zeitlich agent
701
+ * Per-tool lifecycle hooks - defined directly on a tool definition.
702
+ * Runs in addition to global hooks (global pre → tool pre → execute → tool post → global post).
771
703
  */
772
- interface AgentConfig {
773
- /** The name of the agent, should be unique within the workflows, ideally Pascal Case */
774
- agentName: string;
775
- /** Description, used for sub agents */
776
- description?: string;
704
+ interface ToolHooks<TArgs = unknown, TResult = unknown> {
705
+ /** Called before this tool executes - can skip or modify args */
706
+ onPreToolUse?: (ctx: {
707
+ args: TArgs;
708
+ threadId: string;
709
+ turn: number;
710
+ }) => PreToolUseHookResult | Promise<PreToolUseHookResult>;
711
+ /** Called after this tool executes successfully */
712
+ onPostToolUse?: (ctx: {
713
+ args: TArgs;
714
+ result: TResult;
715
+ threadId: string;
716
+ turn: number;
717
+ durationMs: number;
718
+ metadata?: Record<string, unknown>;
719
+ }) => void | Promise<void>;
720
+ /** Called when this tool execution fails */
721
+ onPostToolUseFailure?: (ctx: {
722
+ args: TArgs;
723
+ error: Error;
724
+ threadId: string;
725
+ turn: number;
726
+ }) => PostToolUseFailureHookResult | Promise<PostToolUseFailureHookResult>;
777
727
  }
778
728
  /**
779
- * A JSON-serializable tool definition for state storage.
780
- * Uses a plain JSON Schema object instead of a live Zod instance,
781
- * so it survives Temporal serialization without losing constraints (min, max, etc.).
729
+ * Context for PreToolUse hook - called before tool execution
782
730
  */
783
- interface SerializableToolDefinition {
784
- name: string;
785
- description: string;
786
- schema: Record<string, unknown>;
787
- strict?: boolean;
788
- max_uses?: number;
731
+ interface PreToolUseHookContext<T extends ToolMap> {
732
+ /** The tool call about to be executed */
733
+ toolCall: ParsedToolCallUnion<T>;
734
+ /** Thread identifier */
735
+ threadId: string;
736
+ /** Current turn number */
737
+ turn: number;
789
738
  }
790
739
  /**
791
- * Configuration passed to runAgent activity
740
+ * PreToolUse hook - called before tool execution, can block or modify
792
741
  */
793
- interface RunAgentConfig extends AgentConfig {
794
- /** The thread ID to use for the session */
742
+ type PreToolUseHook<T extends ToolMap> = (ctx: PreToolUseHookContext<T>) => PreToolUseHookResult | Promise<PreToolUseHookResult>;
743
+ /**
744
+ * Context for PostToolUse hook - called after successful tool execution
745
+ */
746
+ interface PostToolUseHookContext<T extends ToolMap, TResult = unknown> {
747
+ /** The tool call that was executed */
748
+ toolCall: ParsedToolCallUnion<T>;
749
+ /** The result from the tool handler */
750
+ result: TResult;
751
+ /** Thread identifier */
795
752
  threadId: string;
796
- /** Redis key suffix for thread storage. Defaults to 'messages'. */
797
- threadKey?: string;
798
- /** Metadata for the session */
799
- metadata?: Record<string, unknown>;
800
- /**
801
- * The id under which the assistant message produced by this call will
802
- * be appended. The activity truncates the thread from this id on
803
- * entry (no-op on the first attempt) so that:
804
- *
805
- * - Rewind retries can reuse the same id and the previous (bad)
806
- * assistant + its tool results are wiped before the retry LLM call.
807
- * - Resetting the Temporal workflow to this activity restores the
808
- * pre-call thread state: replay re-truncates, re-invokes, and
809
- * appends under the same id.
810
- */
811
- assistantMessageId: string;
753
+ /** Current turn number */
754
+ turn: number;
755
+ /** Execution duration in milliseconds */
756
+ durationMs: number;
812
757
  }
813
758
  /**
814
- * Configuration for appending a tool result
759
+ * PostToolUse hook - called after successful tool execution
815
760
  */
816
- interface ToolResultConfig {
761
+ type PostToolUseHook<T extends ToolMap, TResult = unknown> = (ctx: PostToolUseHookContext<T, TResult>) => void | Promise<void>;
762
+ /**
763
+ * Context for PostToolUseFailure hook - called when tool execution fails
764
+ */
765
+ interface PostToolUseFailureHookContext<T extends ToolMap> {
766
+ /** The tool call that failed */
767
+ toolCall: ParsedToolCallUnion<T>;
768
+ /** The error that occurred */
769
+ error: Error;
770
+ /** Thread identifier */
817
771
  threadId: string;
818
- /** Redis key suffix for thread storage. Defaults to 'messages'. */
819
- threadKey?: string;
820
- toolCallId: string;
821
- /** The name of the tool that produced this result */
822
- toolName: string;
823
- /** Content for the tool result — string, object, or array. The adapter converts to its SDK-native format. */
824
- content: JsonValue;
772
+ /** Current turn number */
773
+ turn: number;
825
774
  }
826
775
  /**
827
- * Status of a workflow task
776
+ * PostToolUseFailure hook - called when tool execution fails
828
777
  */
829
- type TaskStatus = "pending" | "in_progress" | "completed";
778
+ type PostToolUseFailureHook<T extends ToolMap> = (ctx: PostToolUseFailureHookContext<T>) => PostToolUseFailureHookResult | Promise<PostToolUseFailureHookResult>;
830
779
  /**
831
- * A task managed within a workflow for tracking work items
780
+ * Tool execution hooks the subset of hooks consumed by the tool router.
781
+ * Session/message lifecycle hooks live in lib/hooks/types.ts.
832
782
  */
833
- interface WorkflowTask {
834
- /** Unique task identifier */
835
- id: string;
836
- /** Brief, actionable title in imperative form */
837
- subject: string;
838
- /** Detailed description of what needs to be done */
839
- description: string;
840
- /** Present continuous form shown in spinner when in_progress */
841
- activeForm: string;
842
- /** Current status of the task */
843
- status: TaskStatus;
844
- /** Arbitrary key-value pairs for tracking */
845
- metadata: Record<string, string>;
846
- /** IDs of tasks that must complete before this one can start */
847
- blockedBy: string[];
848
- /** IDs of tasks that are waiting for this one to complete */
849
- blocks: string[];
783
+ interface ToolRouterHooks<T extends ToolMap, TResult = unknown> {
784
+ /** Called before each tool execution - can block or modify */
785
+ onPreToolUse?: PreToolUseHook<T>;
786
+ /** Called after each successful tool execution */
787
+ onPostToolUse?: PostToolUseHook<T, TResult>;
788
+ /** Called when tool execution fails */
789
+ onPostToolUseFailure?: PostToolUseFailureHook<T>;
850
790
  }
851
791
  /**
852
- * Exit reasons for session termination
792
+ * Options for creating a tool router.
853
793
  */
854
- type SessionExitReason = "completed" | "max_turns" | "waiting_for_input" | "failed" | "cancelled";
794
+ interface ToolRouterOptions<T extends ToolMap> {
795
+ /** Map of tools with their handlers */
796
+ tools: T;
797
+ /** Thread ID for appending tool results */
798
+ threadId: string;
799
+ /** Redis key suffix for thread storage. Defaults to 'messages'. */
800
+ threadKey?: string;
801
+ /** Function to append tool results to the thread (called automatically after each handler).
802
+ * Accepts a Temporal activity proxy with {@link ActivityFunctionWithOptions}. */
803
+ appendToolResult: AppendToolResultFn;
804
+ /** Whether to process tools in parallel (default: true) */
805
+ parallel?: boolean;
806
+ /** Tool execution lifecycle hooks (pre/post tool use) */
807
+ hooks?: ToolRouterHooks<T, ToolCallResultUnion<InferToolResults<T>>>;
808
+ /** Additional tools to auto-register (e.g. subagent, skill tools built by register helpers) */
809
+ plugins?: ToolMap[string][];
810
+ }
855
811
  /**
856
- * Helper to check if status is terminal
812
+ * The tool router interface with full type inference for both args and results.
857
813
  */
858
- declare function isTerminalStatus(status: AgentStatus): boolean;
814
+ interface ToolRouter<T extends ToolMap> {
815
+ /** Check if the router has any tools */
816
+ hasTools(): boolean;
817
+ /**
818
+ * Parse and validate a raw tool call against the router's tools.
819
+ * Returns a typed tool call with validated arguments.
820
+ */
821
+ parseToolCall(toolCall: RawToolCall): ParsedToolCallUnion<T>;
822
+ /**
823
+ * Check if a tool with the given name exists in the router.
824
+ */
825
+ hasTool(name: string): boolean;
826
+ /**
827
+ * Get all tool names in the router.
828
+ */
829
+ getToolNames(): ToolNames<T>[];
830
+ /**
831
+ * Get all tool definitions (without handlers) for passing to LLM.
832
+ */
833
+ getToolDefinitions(): ToolDefinition[];
834
+ /**
835
+ * Process all tool calls using the registered handlers.
836
+ * Returns typed results based on handler return types.
837
+ * @param toolCalls - Array of parsed tool calls to process
838
+ * @param context - Optional context including turn number for hooks
839
+ */
840
+ processToolCalls(toolCalls: ParsedToolCallUnion<T>[], context?: ProcessToolCallsContext): Promise<ProcessToolCallsResult<InferToolResults<T>>>;
841
+ /**
842
+ * Process tool calls matching a specific name with a custom handler.
843
+ * Useful for overriding the default handler for specific cases.
844
+ */
845
+ processToolCallsByName<TName extends ToolNames<T>, TResult>(toolCalls: ParsedToolCallUnion<T>[], toolName: TName, handler: ToolHandler<ToolArgs<T, TName>, TResult>, context?: ProcessToolCallsContext): Promise<ToolCallResult<TName, TResult>[]>;
846
+ /**
847
+ * Filter tool calls by name.
848
+ */
849
+ filterByName<TName extends ToolNames<T>>(toolCalls: ParsedToolCallUnion<T>[], name: TName): ParsedToolCall<TName, ToolArgs<T, TName>>[];
850
+ /**
851
+ * Check if any tool call matches the given name.
852
+ */
853
+ hasToolCall(toolCalls: ParsedToolCallUnion<T>[], name: ToolNames<T>): boolean;
854
+ /**
855
+ * Filter results by tool name.
856
+ */
857
+ getResultsByName<TName extends ToolNames<T>>(results: ToolCallResultUnion<InferToolResults<T>>[], name: TName): ToolCallResult<TName, ToolResult<T, TName>>[];
858
+ }
859
859
 
860
860
  /**
861
861
  * Context for SessionStart hook - called when session begins
@@ -1866,4 +1866,4 @@ interface ZeitlichSession<M = unknown, HasSandbox extends boolean = boolean> {
1866
1866
  }): Promise<SessionResult<M, T, HasSandbox>>;
1867
1867
  }
1868
1868
 
1869
- export { type SandboxShutdown as $, type AgentResponse as A, type BaseAgentState as B, type PostToolUseFailureHookContext as C, type PostToolUseFailureHookResult as D, type PostToolUseHook as E, type FileEntryMetadata as F, type PostToolUseHookContext as G, type Hooks as H, type InferToolResults as I, type JsonValue as J, type PreHumanMessageAppendHook as K, type PreHumanMessageAppendHookContext as L, type ModelInvokerConfig as M, type PreToolUseHook as N, type PreToolUseHookContext as O, type PersistedThreadState as P, type PreToolUseHookResult as Q, type RouterContext as R, type ScopedPrefix as S, type ThreadOps as T, type ProcessToolCallsContext as U, type VirtualFsContext as V, type ProcessToolCallsResult as W, type RawToolCall as X, type RewindSignal as Y, type RunAgentActivity as Z, type SandboxInit as _, type ModelInvoker as a, type SerializableToolDefinition as a0, type SessionConfig as a1, type SessionEndHook as a2, type SessionEndHookContext as a3, type SessionExitReason as a4, type SessionRequiredCaps as a5, type SessionResult as a6, type SessionStartHook as a7, type SessionStartHookContext as a8, type SubagentChildWorkflowOptions as a9, VirtualFileSystem as aA, type VirtualFileTree as aB, type VirtualFsOps as aC, type VirtualFsState as aD, type WorkflowTask as aE, type ZeitlichSession as aF, isTerminalStatus as aG, type ToolRouterOptions as aH, type SubagentConfig as aa, type SubagentContinuationCaps as ab, type SubagentDefinition as ac, type SubagentFnResult as ad, type SubagentHandlerResponse as ae, type SubagentHooks as af, type SubagentSandboxConfig as ag, type SubagentSandboxShutdown as ah, type SubagentSessionInput as ai, type SubagentWorkflow as aj, type SubagentWorkflowInput as ak, type TaskStatus as al, type ThreadInit as am, type TokenUsage as an, type ToolArgs as ao, type ToolCallResult as ap, type ToolCallResultUnion as aq, type ToolDefinition as ar, type ToolHandler as as, type ToolHooks as at, type ToolMap as au, type ToolNames as av, type ToolResult as aw, type ToolRouter as ax, type ToolRouterHooks as ay, type ToolWithHandler as az, type PrefixedThreadOps as b, type ToolHandlerResponse as c, type ActivityToolHandler as d, type ToolResultConfig as e, type RunAgentConfig as f, type SkillProvider as g, type SkillMetadata as h, type Skill as i, type FileResolver as j, type TreeMutation as k, type PrefixedVirtualFsOps as l, type AgentConfig as m, type AgentFile as n, type AgentState as o, type AgentStateManager as p, type AgentStatus as q, type AppendToolResultFn as r, type FileEntry as s, type JsonPrimitive as t, type JsonSerializable as u, type ParsedToolCall as v, type ParsedToolCallUnion as w, type PostHumanMessageAppendHook as x, type PostHumanMessageAppendHookContext as y, type PostToolUseFailureHook as z };
1869
+ export { type SandboxShutdown as $, type AgentResponse as A, type BaseAgentState as B, type PostToolUseFailureHookContext as C, type PostToolUseFailureHookResult as D, type PostToolUseHook as E, type FileEntryMetadata as F, type PostToolUseHookContext as G, type Hooks as H, type InferToolResults as I, type JsonValue as J, type PreHumanMessageAppendHook as K, type PreHumanMessageAppendHookContext as L, type ModelInvokerConfig as M, type PreToolUseHook as N, type PreToolUseHookContext as O, type PersistedThreadState as P, type PreToolUseHookResult as Q, type RouterContext as R, type ScopedPrefix as S, type ThreadOps as T, type ProcessToolCallsContext as U, type VirtualFsContext as V, type ProcessToolCallsResult as W, type RawToolCall as X, type RewindSignal as Y, type RunAgentActivity as Z, type SandboxInit as _, type ModelInvoker as a, type SerializableToolDefinition as a0, type SessionConfig as a1, type SessionEndHook as a2, type SessionEndHookContext as a3, type SessionExitReason as a4, type SessionRequiredCaps as a5, type SessionResult as a6, type SessionStartHook as a7, type SessionStartHookContext as a8, type SubagentChildWorkflowOptions as a9, VirtualFileSystem as aA, type VirtualFileTree as aB, type VirtualFsOps as aC, type VirtualFsState as aD, type WorkflowTask as aE, type ZeitlichSession as aF, isTerminalStatus as aG, type ToolRouterOptions as aH, type SubagentConfig as aa, type SubagentContinuationCaps as ab, type SubagentDefinition as ac, type SubagentFnResult as ad, type SubagentHandlerResponse as ae, type SubagentHooks as af, type SubagentSandboxConfig as ag, type SubagentSandboxShutdown as ah, type SubagentSessionInput as ai, type SubagentWorkflow as aj, type SubagentWorkflowInput as ak, type TaskStatus as al, type ThreadInit as am, type TokenUsage as an, type ToolArgs as ao, type ToolCallResult as ap, type ToolCallResultUnion as aq, type ToolDefinition as ar, type ToolHandler as as, type ToolHooks as at, type ToolMap as au, type ToolNames as av, type ToolResult as aw, type ToolRouter as ax, type ToolRouterHooks as ay, type ToolWithHandler as az, type PrefixedThreadOps as b, type ToolHandlerResponse as c, type ActivityToolHandler as d, type RunAgentConfig as e, type ToolResultConfig as f, type SkillProvider as g, type SkillMetadata as h, type Skill as i, type FileResolver as j, type TreeMutation as k, type PrefixedVirtualFsOps as l, type AgentConfig as m, type AgentFile as n, type AgentState as o, type AgentStateManager as p, type AgentStatus as q, type AppendToolResultFn as r, type FileEntry as s, type JsonPrimitive as t, type JsonSerializable as u, type ParsedToolCall as v, type ParsedToolCallUnion as w, type PostHumanMessageAppendHook as x, type PostHumanMessageAppendHookContext as y, type PostToolUseFailureHook as z };