zeitlich 0.2.13 → 0.2.14

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 (135) hide show
  1. package/README.md +49 -38
  2. package/dist/adapters/sandbox/daytona/index.cjs +205 -0
  3. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -0
  4. package/dist/adapters/sandbox/daytona/index.d.cts +86 -0
  5. package/dist/adapters/sandbox/daytona/index.d.ts +86 -0
  6. package/dist/adapters/sandbox/daytona/index.js +202 -0
  7. package/dist/adapters/sandbox/daytona/index.js.map +1 -0
  8. package/dist/adapters/sandbox/inmemory/index.cjs +174 -0
  9. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -0
  10. package/dist/adapters/sandbox/inmemory/index.d.cts +28 -0
  11. package/dist/adapters/sandbox/inmemory/index.d.ts +28 -0
  12. package/dist/adapters/sandbox/inmemory/index.js +172 -0
  13. package/dist/adapters/sandbox/inmemory/index.js.map +1 -0
  14. package/dist/adapters/sandbox/virtual/index.cjs +405 -0
  15. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -0
  16. package/dist/adapters/sandbox/virtual/index.d.cts +85 -0
  17. package/dist/adapters/sandbox/virtual/index.d.ts +85 -0
  18. package/dist/adapters/sandbox/virtual/index.js +400 -0
  19. package/dist/adapters/sandbox/virtual/index.js.map +1 -0
  20. package/dist/adapters/thread/google-genai/index.cjs +284 -0
  21. package/dist/adapters/thread/google-genai/index.cjs.map +1 -0
  22. package/dist/adapters/thread/google-genai/index.d.cts +145 -0
  23. package/dist/adapters/thread/google-genai/index.d.ts +145 -0
  24. package/dist/adapters/thread/google-genai/index.js +278 -0
  25. package/dist/adapters/thread/google-genai/index.js.map +1 -0
  26. package/dist/adapters/{langchain → thread/langchain}/index.cjs +7 -9
  27. package/dist/adapters/thread/langchain/index.cjs.map +1 -0
  28. package/dist/adapters/{langchain → thread/langchain}/index.d.cts +17 -21
  29. package/dist/adapters/{langchain → thread/langchain}/index.d.ts +17 -21
  30. package/dist/adapters/{langchain → thread/langchain}/index.js +7 -9
  31. package/dist/adapters/thread/langchain/index.js.map +1 -0
  32. package/dist/index.cjs +816 -545
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +235 -74
  35. package/dist/index.d.ts +235 -74
  36. package/dist/index.js +804 -540
  37. package/dist/index.js.map +1 -1
  38. package/dist/types-B4C9txdq.d.ts +389 -0
  39. package/dist/{thread-manager-qc0g5Rvd.d.cts → types-B9ljZewB.d.cts} +1 -6
  40. package/dist/{thread-manager-qc0g5Rvd.d.ts → types-B9ljZewB.d.ts} +1 -6
  41. package/dist/types-BMXzv7TN.d.cts +476 -0
  42. package/dist/types-BMXzv7TN.d.ts +476 -0
  43. package/dist/types-BVP87m_W.d.cts +121 -0
  44. package/dist/types-CDubRtad.d.cts +115 -0
  45. package/dist/types-CDubRtad.d.ts +115 -0
  46. package/dist/types-CwwgQ_9H.d.ts +121 -0
  47. package/dist/types-GpMU4b0w.d.cts +389 -0
  48. package/dist/workflow.cjs +444 -318
  49. package/dist/workflow.cjs.map +1 -1
  50. package/dist/workflow.d.cts +271 -222
  51. package/dist/workflow.d.ts +271 -222
  52. package/dist/workflow.js +440 -316
  53. package/dist/workflow.js.map +1 -1
  54. package/package.json +59 -6
  55. package/src/adapters/sandbox/daytona/filesystem.ts +136 -0
  56. package/src/adapters/sandbox/daytona/index.ts +149 -0
  57. package/src/adapters/sandbox/daytona/types.ts +34 -0
  58. package/src/adapters/sandbox/inmemory/index.ts +213 -0
  59. package/src/adapters/sandbox/virtual/filesystem.ts +345 -0
  60. package/src/adapters/sandbox/virtual/index.ts +88 -0
  61. package/src/adapters/sandbox/virtual/mutations.ts +38 -0
  62. package/src/adapters/sandbox/virtual/provider.ts +101 -0
  63. package/src/adapters/sandbox/virtual/tree.ts +82 -0
  64. package/src/adapters/sandbox/virtual/types.ts +127 -0
  65. package/src/adapters/sandbox/virtual/virtual-sandbox.test.ts +523 -0
  66. package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +91 -0
  67. package/src/adapters/thread/google-genai/activities.ts +121 -0
  68. package/src/adapters/thread/google-genai/index.ts +41 -0
  69. package/src/adapters/thread/google-genai/model-invoker.ts +154 -0
  70. package/src/adapters/thread/google-genai/thread-manager.ts +169 -0
  71. package/src/adapters/{langchain → thread/langchain}/activities.ts +11 -15
  72. package/src/adapters/{langchain → thread/langchain}/index.ts +1 -1
  73. package/src/adapters/{langchain → thread/langchain}/model-invoker.ts +15 -18
  74. package/src/adapters/{langchain → thread/langchain}/thread-manager.ts +1 -1
  75. package/src/index.ts +32 -24
  76. package/src/lib/activity.ts +87 -0
  77. package/src/lib/hooks/index.ts +11 -0
  78. package/src/lib/hooks/types.ts +98 -0
  79. package/src/lib/model/helpers.ts +6 -0
  80. package/src/lib/model/index.ts +13 -0
  81. package/src/lib/{model-invoker.ts → model/types.ts} +18 -1
  82. package/src/lib/sandbox/index.ts +19 -0
  83. package/src/lib/sandbox/manager.ts +76 -0
  84. package/src/lib/sandbox/sandbox.test.ts +158 -0
  85. package/src/lib/{fs.ts → sandbox/tree.ts} +6 -6
  86. package/src/lib/sandbox/types.ts +164 -0
  87. package/src/lib/session/index.ts +11 -0
  88. package/src/lib/{session.ts → session/session.ts} +76 -48
  89. package/src/lib/session/types.ts +93 -0
  90. package/src/lib/skills/fs-provider.ts +16 -15
  91. package/src/lib/skills/handler.ts +31 -0
  92. package/src/lib/skills/index.ts +5 -1
  93. package/src/lib/skills/register.ts +20 -0
  94. package/src/lib/skills/tool.ts +47 -0
  95. package/src/lib/state/index.ts +9 -0
  96. package/src/lib/{state-manager.ts → state/manager.ts} +10 -147
  97. package/src/lib/state/types.ts +134 -0
  98. package/src/lib/subagent/define.ts +71 -0
  99. package/src/lib/subagent/handler.ts +99 -0
  100. package/src/lib/subagent/index.ts +13 -0
  101. package/src/lib/subagent/register.ts +53 -0
  102. package/src/lib/subagent/tool.ts +80 -0
  103. package/src/lib/subagent/types.ts +92 -0
  104. package/src/lib/thread/index.ts +7 -0
  105. package/src/lib/{thread-manager.ts → thread/manager.ts} +1 -33
  106. package/src/lib/thread/types.ts +33 -0
  107. package/src/lib/tool-router/auto-append.ts +55 -0
  108. package/src/lib/tool-router/index.ts +41 -0
  109. package/src/lib/tool-router/router.ts +462 -0
  110. package/src/lib/tool-router/types.ts +478 -0
  111. package/src/lib/tool-router/with-sandbox.ts +70 -0
  112. package/src/lib/types.ts +5 -382
  113. package/src/tools/bash/bash.test.ts +53 -55
  114. package/src/tools/bash/handler.ts +23 -51
  115. package/src/tools/edit/handler.ts +67 -81
  116. package/src/tools/glob/handler.ts +60 -17
  117. package/src/tools/read-file/handler.ts +67 -0
  118. package/src/tools/read-skill/handler.ts +1 -31
  119. package/src/tools/read-skill/tool.ts +5 -47
  120. package/src/tools/subagent/handler.ts +1 -100
  121. package/src/tools/subagent/tool.ts +5 -93
  122. package/src/tools/task-create/handler.ts +1 -1
  123. package/src/tools/task-get/handler.ts +1 -1
  124. package/src/tools/task-list/handler.ts +1 -1
  125. package/src/tools/task-update/handler.ts +1 -1
  126. package/src/tools/write-file/handler.ts +47 -0
  127. package/src/workflow.ts +88 -47
  128. package/tsup.config.ts +8 -1
  129. package/dist/adapters/langchain/index.cjs.map +0 -1
  130. package/dist/adapters/langchain/index.js.map +0 -1
  131. package/dist/model-invoker-y_zlyMqu.d.cts +0 -892
  132. package/dist/model-invoker-y_zlyMqu.d.ts +0 -892
  133. package/src/lib/tool-router.ts +0 -977
  134. package/src/lib/workflow-helpers.ts +0 -50
  135. /package/src/lib/{thread-id.ts → thread/id.ts} +0 -0
@@ -0,0 +1,478 @@
1
+ import type {
2
+ ToolMessageContent,
3
+ TokenUsage,
4
+ ToolResultConfig,
5
+ } from "../types";
6
+ import type { z } from "zod";
7
+
8
+ // ============================================================================
9
+ // Tool Definition Types
10
+ // ============================================================================
11
+
12
+ /**
13
+ * A tool definition with a name, description, and Zod schema for arguments.
14
+ * Does not include a handler - use ToolWithHandler for tools with handlers.
15
+ */
16
+ export interface ToolDefinition<
17
+ TName extends string = string,
18
+ TSchema extends z.ZodType = z.ZodType,
19
+ > {
20
+ name: TName;
21
+ description: string;
22
+ schema: TSchema;
23
+ strict?: boolean;
24
+ max_uses?: number;
25
+ }
26
+
27
+ /**
28
+ * A tool definition with an integrated handler function.
29
+ * This is the primary type for defining tools in the router.
30
+ */
31
+ export interface ToolWithHandler<
32
+ TName extends string = string,
33
+ TSchema extends z.ZodType = z.ZodType,
34
+ TResult = unknown,
35
+ TContext extends RouterContext = RouterContext,
36
+ > {
37
+ name: TName;
38
+ description: string;
39
+ schema: TSchema;
40
+ handler: ToolHandler<z.infer<TSchema>, TResult, TContext>;
41
+ strict?: boolean;
42
+ max_uses?: number;
43
+ /** Whether this tool is available to the agent (default: true). Disabled tools are excluded from definitions and rejected at parse time. */
44
+ enabled?: boolean;
45
+ /** Per-tool lifecycle hooks (run in addition to global hooks) */
46
+ hooks?: ToolHooks<z.infer<TSchema>, TResult>;
47
+ }
48
+
49
+ /**
50
+ * A map of tool keys to tool definitions with handlers.
51
+ *
52
+ * Handler uses `any` intentionally — this is a type-system boundary where heterogeneous
53
+ * tool types are stored together. Type safety for individual tools is enforced by
54
+ * `defineTool()` at the definition site and generic inference utilities like
55
+ * `InferToolResults<T>` at the consumption site.
56
+ */
57
+ export type ToolMap = Record<
58
+ string,
59
+ {
60
+ name: string;
61
+ description: string;
62
+ schema: z.ZodType;
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ handler: ToolHandler<any, any, any>;
65
+ strict?: boolean;
66
+ max_uses?: number;
67
+ enabled?: boolean;
68
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
69
+ hooks?: ToolHooks<any, any>;
70
+ }
71
+ >;
72
+
73
+ /**
74
+ * Extract the tool names from a tool map (uses the tool's name property, not the key).
75
+ */
76
+ export type ToolNames<T extends ToolMap> = T[keyof T]["name"];
77
+
78
+ // ============================================================================
79
+ // Raw/Parsed Tool Call Types
80
+ // ============================================================================
81
+
82
+ /**
83
+ * A raw tool call as received from the LLM before parsing.
84
+ */
85
+ export interface RawToolCall {
86
+ id?: string;
87
+ name: string;
88
+ args: unknown;
89
+ }
90
+
91
+ /**
92
+ * A parsed tool call with validated arguments for a specific tool.
93
+ */
94
+ export interface ParsedToolCall<
95
+ TName extends string = string,
96
+ TArgs = unknown,
97
+ > {
98
+ id: string;
99
+ name: TName;
100
+ args: TArgs;
101
+ }
102
+
103
+ /**
104
+ * Union type of all possible parsed tool calls from a tool map.
105
+ */
106
+ export type ParsedToolCallUnion<T extends ToolMap> = {
107
+ [K in keyof T]: ParsedToolCall<T[K]["name"], z.infer<T[K]["schema"]>>;
108
+ }[keyof T];
109
+
110
+ // ============================================================================
111
+ // Handler Types
112
+ // ============================================================================
113
+
114
+ /**
115
+ * Function signature for appending tool results to a thread.
116
+ */
117
+ export type AppendToolResultFn = (config: ToolResultConfig) => Promise<void>;
118
+
119
+ /**
120
+ * The response from a tool handler.
121
+ * Contains the content for the tool message and the result to return from processToolCalls.
122
+ *
123
+ * Tools that don't return additional data should use `data: null` (TResult defaults to null).
124
+ * Tools that may fail to produce data should type TResult as `SomeType | null`.
125
+ */
126
+ export interface ToolHandlerResponse<TResult = null> {
127
+ /** Content sent back to the LLM as the tool call response */
128
+ toolResponse: ToolMessageContent;
129
+ /** Data returned to the workflow and hooks for further processing */
130
+ data: TResult;
131
+ /**
132
+ * When true, the tool result has already been appended to the thread
133
+ * by the handler itself (e.g. via `withAutoAppend`), so the router
134
+ * will skip the `appendToolResult` call. This avoids sending large
135
+ * payloads through Temporal's activity payload limit.
136
+ */
137
+ resultAppended?: boolean;
138
+ /** Token usage from the tool execution (e.g. child agent invocations) */
139
+ usage?: TokenUsage;
140
+ /** Thread ID used by the handler (surfaced to the LLM for subagent thread continuation) */
141
+ threadId?: string;
142
+ }
143
+
144
+ /**
145
+ * Base context the router always injects into every handler invocation.
146
+ * Handlers can rely on these fields being present without casting.
147
+ */
148
+ export interface RouterContext {
149
+ threadId: string;
150
+ toolCallId: string;
151
+ toolName: string;
152
+ sandboxId?: string;
153
+ }
154
+
155
+ /**
156
+ * A handler function for a specific tool.
157
+ * Receives the parsed args and a context that always includes {@link RouterContext}
158
+ * fields, plus any additional properties when TContext extends RouterContext.
159
+ */
160
+ export type ToolHandler<TArgs, TResult, TContext extends RouterContext = RouterContext> = (
161
+ args: TArgs,
162
+ context: TContext
163
+ ) => ToolHandlerResponse<TResult> | Promise<ToolHandlerResponse<TResult>>;
164
+
165
+ /**
166
+ * Activity-compatible tool handler that always returns a Promise.
167
+ * Use this for tool handlers registered as Temporal activities.
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const readHandler: ActivityToolHandler<
172
+ * FileReadArgs,
173
+ * ReadResult,
174
+ * RouterContext & { scopedNodes: FileNode[]; provider: FileSystemProvider }
175
+ * > = async (args, context) => {
176
+ * // context.threadId, context.sandboxId etc. are always available
177
+ * return readHandler(args, context.scopedNodes, context.provider);
178
+ * };
179
+ * ```
180
+ */
181
+ export type ActivityToolHandler<
182
+ TArgs,
183
+ TResult,
184
+ TContext extends RouterContext = RouterContext,
185
+ > = (args: TArgs, context: TContext) => Promise<ToolHandlerResponse<TResult>>;
186
+
187
+ /**
188
+ * Extract the args type for a specific tool name from a tool map.
189
+ */
190
+ export type ToolArgs<T extends ToolMap, TName extends ToolNames<T>> = z.infer<
191
+ Extract<T[keyof T], { name: TName }>["schema"]
192
+ >;
193
+
194
+ /**
195
+ * Extract the result type for a specific tool name from a tool map.
196
+ */
197
+ export type ToolResult<T extends ToolMap, TName extends ToolNames<T>> =
198
+ Extract<T[keyof T], { name: TName }>["handler"] extends ToolHandler<
199
+ unknown,
200
+ infer R,
201
+ RouterContext
202
+ >
203
+ ? Awaited<R>
204
+ : never;
205
+
206
+ // ============================================================================
207
+ // Tool Call Result Types
208
+ // ============================================================================
209
+
210
+ /**
211
+ * The result of processing a tool call.
212
+ */
213
+ export interface ToolCallResult<
214
+ TName extends string = string,
215
+ TResult = unknown,
216
+ > {
217
+ toolCallId: string;
218
+ name: TName;
219
+ data: TResult;
220
+ usage?: TokenUsage;
221
+ }
222
+
223
+ /**
224
+ * Infer result types from a tool map based on handler return types.
225
+ */
226
+ export type InferToolResults<T extends ToolMap> = {
227
+ /* eslint-disable @typescript-eslint/no-explicit-any */
228
+ [K in keyof T as T[K]["name"]]: T[K]["handler"] extends ToolHandler<
229
+ any,
230
+ infer R,
231
+ any
232
+ >
233
+ ? /* eslint-enable @typescript-eslint/no-explicit-any */
234
+ Awaited<R>
235
+ : never;
236
+ };
237
+
238
+ /**
239
+ * Union of all possible tool call results based on handler return types.
240
+ */
241
+ export type ToolCallResultUnion<TResults extends Record<string, unknown>> = {
242
+ [TName in keyof TResults & string]: ToolCallResult<TName, TResults[TName]>;
243
+ }[keyof TResults & string];
244
+
245
+ /**
246
+ * Context passed to processToolCalls for hook execution and handler invocation
247
+ */
248
+ export interface ProcessToolCallsContext {
249
+ /** Current turn number (for hooks) */
250
+ turn?: number;
251
+ /** Active sandbox ID (when a sandbox is configured for this session) */
252
+ sandboxId?: string;
253
+ }
254
+
255
+ // ============================================================================
256
+ // Hook Types
257
+ // ============================================================================
258
+
259
+ /**
260
+ * Result from PreToolUse hook - can block or modify execution
261
+ */
262
+ export interface PreToolUseHookResult {
263
+ /** Skip this tool call entirely */
264
+ skip?: boolean;
265
+ /** Modified args to use instead (must match schema) */
266
+ modifiedArgs?: unknown;
267
+ }
268
+
269
+ /**
270
+ * Result from PostToolUseFailure hook - can recover from errors
271
+ */
272
+ export interface PostToolUseFailureHookResult {
273
+ /** Provide a fallback result instead of throwing */
274
+ fallbackContent?: ToolMessageContent;
275
+ /** Whether to suppress the error (still logs, but continues) */
276
+ suppress?: boolean;
277
+ }
278
+
279
+ /**
280
+ * Per-tool lifecycle hooks - defined directly on a tool definition.
281
+ * Runs in addition to global hooks (global pre → tool pre → execute → tool post → global post).
282
+ */
283
+ export interface ToolHooks<TArgs = unknown, TResult = unknown> {
284
+ /** Called before this tool executes - can skip or modify args */
285
+ onPreToolUse?: (ctx: {
286
+ args: TArgs;
287
+ threadId: string;
288
+ turn: number;
289
+ }) => PreToolUseHookResult | Promise<PreToolUseHookResult>;
290
+ /** Called after this tool executes successfully */
291
+ onPostToolUse?: (ctx: {
292
+ args: TArgs;
293
+ result: TResult;
294
+ threadId: string;
295
+ turn: number;
296
+ durationMs: number;
297
+ }) => void | Promise<void>;
298
+ /** Called when this tool execution fails */
299
+ onPostToolUseFailure?: (ctx: {
300
+ args: TArgs;
301
+ error: Error;
302
+ threadId: string;
303
+ turn: number;
304
+ }) => PostToolUseFailureHookResult | Promise<PostToolUseFailureHookResult>;
305
+ }
306
+
307
+ /**
308
+ * Context for PreToolUse hook - called before tool execution
309
+ */
310
+ export interface PreToolUseHookContext<T extends ToolMap> {
311
+ /** The tool call about to be executed */
312
+ toolCall: ParsedToolCallUnion<T>;
313
+ /** Thread identifier */
314
+ threadId: string;
315
+ /** Current turn number */
316
+ turn: number;
317
+ }
318
+
319
+ /**
320
+ * PreToolUse hook - called before tool execution, can block or modify
321
+ */
322
+ export type PreToolUseHook<T extends ToolMap> = (
323
+ ctx: PreToolUseHookContext<T>
324
+ ) => PreToolUseHookResult | Promise<PreToolUseHookResult>;
325
+
326
+ /**
327
+ * Context for PostToolUse hook - called after successful tool execution
328
+ */
329
+ export interface PostToolUseHookContext<T extends ToolMap, TResult = unknown> {
330
+ /** The tool call that was executed */
331
+ toolCall: ParsedToolCallUnion<T>;
332
+ /** The result from the tool handler */
333
+ result: TResult;
334
+ /** Thread identifier */
335
+ threadId: string;
336
+ /** Current turn number */
337
+ turn: number;
338
+ /** Execution duration in milliseconds */
339
+ durationMs: number;
340
+ }
341
+
342
+ /**
343
+ * PostToolUse hook - called after successful tool execution
344
+ */
345
+ export type PostToolUseHook<T extends ToolMap, TResult = unknown> = (
346
+ ctx: PostToolUseHookContext<T, TResult>
347
+ ) => void | Promise<void>;
348
+
349
+ /**
350
+ * Context for PostToolUseFailure hook - called when tool execution fails
351
+ */
352
+ export interface PostToolUseFailureHookContext<T extends ToolMap> {
353
+ /** The tool call that failed */
354
+ toolCall: ParsedToolCallUnion<T>;
355
+ /** The error that occurred */
356
+ error: Error;
357
+ /** Thread identifier */
358
+ threadId: string;
359
+ /** Current turn number */
360
+ turn: number;
361
+ }
362
+
363
+ /**
364
+ * PostToolUseFailure hook - called when tool execution fails
365
+ */
366
+ export type PostToolUseFailureHook<T extends ToolMap> = (
367
+ ctx: PostToolUseFailureHookContext<T>
368
+ ) => PostToolUseFailureHookResult | Promise<PostToolUseFailureHookResult>;
369
+
370
+ /**
371
+ * Tool execution hooks — the subset of hooks consumed by the tool router.
372
+ * Session/message lifecycle hooks live in lib/hooks/types.ts.
373
+ */
374
+ export interface ToolRouterHooks<T extends ToolMap, TResult = unknown> {
375
+ /** Called before each tool execution - can block or modify */
376
+ onPreToolUse?: PreToolUseHook<T>;
377
+ /** Called after each successful tool execution */
378
+ onPostToolUse?: PostToolUseHook<T, TResult>;
379
+ /** Called when tool execution fails */
380
+ onPostToolUseFailure?: PostToolUseFailureHook<T>;
381
+ }
382
+
383
+ // ============================================================================
384
+ // Router Options & Interface
385
+ // ============================================================================
386
+
387
+ /**
388
+ * Options for creating a tool router.
389
+ */
390
+ export interface ToolRouterOptions<T extends ToolMap> {
391
+ /** Map of tools with their handlers */
392
+ tools: T;
393
+ /** Thread ID for appending tool results */
394
+ threadId: string;
395
+ /** Function to append tool results to the thread (called automatically after each handler) */
396
+ appendToolResult: AppendToolResultFn;
397
+ /** Whether to process tools in parallel (default: true) */
398
+ parallel?: boolean;
399
+ /** Tool execution lifecycle hooks (pre/post tool use) */
400
+ hooks?: ToolRouterHooks<T, ToolCallResultUnion<InferToolResults<T>>>;
401
+ /** Additional tools to auto-register (e.g. subagent, skill tools built by register helpers) */
402
+ plugins?: ToolMap[string][];
403
+ }
404
+
405
+ /**
406
+ * The tool router interface with full type inference for both args and results.
407
+ */
408
+ export interface ToolRouter<T extends ToolMap> {
409
+ /** Check if the router has any tools */
410
+ hasTools(): boolean;
411
+
412
+ /**
413
+ * Parse and validate a raw tool call against the router's tools.
414
+ * Returns a typed tool call with validated arguments.
415
+ */
416
+ parseToolCall(toolCall: RawToolCall): ParsedToolCallUnion<T>;
417
+
418
+ /**
419
+ * Check if a tool with the given name exists in the router.
420
+ */
421
+ hasTool(name: string): boolean;
422
+
423
+ /**
424
+ * Get all tool names in the router.
425
+ */
426
+ getToolNames(): ToolNames<T>[];
427
+
428
+ /**
429
+ * Get all tool definitions (without handlers) for passing to LLM.
430
+ */
431
+ getToolDefinitions(): ToolDefinition[];
432
+
433
+ /**
434
+ * Process all tool calls using the registered handlers.
435
+ * Returns typed results based on handler return types.
436
+ * @param toolCalls - Array of parsed tool calls to process
437
+ * @param context - Optional context including turn number for hooks
438
+ */
439
+ processToolCalls(
440
+ toolCalls: ParsedToolCallUnion<T>[],
441
+ context?: ProcessToolCallsContext
442
+ ): Promise<ToolCallResultUnion<InferToolResults<T>>[]>;
443
+
444
+ /**
445
+ * Process tool calls matching a specific name with a custom handler.
446
+ * Useful for overriding the default handler for specific cases.
447
+ */
448
+ processToolCallsByName<
449
+ TName extends ToolNames<T>,
450
+ TResult,
451
+ >(
452
+ toolCalls: ParsedToolCallUnion<T>[],
453
+ toolName: TName,
454
+ handler: ToolHandler<ToolArgs<T, TName>, TResult>,
455
+ context?: ProcessToolCallsContext
456
+ ): Promise<ToolCallResult<TName, TResult>[]>;
457
+
458
+ /**
459
+ * Filter tool calls by name.
460
+ */
461
+ filterByName<TName extends ToolNames<T>>(
462
+ toolCalls: ParsedToolCallUnion<T>[],
463
+ name: TName
464
+ ): ParsedToolCall<TName, ToolArgs<T, TName>>[];
465
+
466
+ /**
467
+ * Check if any tool call matches the given name.
468
+ */
469
+ hasToolCall(toolCalls: ParsedToolCallUnion<T>[], name: ToolNames<T>): boolean;
470
+
471
+ /**
472
+ * Filter results by tool name.
473
+ */
474
+ getResultsByName<TName extends ToolNames<T>>(
475
+ results: ToolCallResultUnion<InferToolResults<T>>[],
476
+ name: TName
477
+ ): ToolCallResult<TName, ToolResult<T, TName>>[];
478
+ }
@@ -0,0 +1,70 @@
1
+ import type { Sandbox } from "../sandbox/types";
2
+ import type { ActivityToolHandler, RouterContext, ToolHandlerResponse } from "./types";
3
+
4
+ /**
5
+ * Extended router context with a resolved {@link Sandbox} instance.
6
+ *
7
+ * Handlers typed with this context are guaranteed to have a live sandbox
8
+ * and a non-optional `sandboxId`. Use with {@link withSandbox} to
9
+ * automatically resolve the sandbox from the manager.
10
+ */
11
+ export interface SandboxContext extends RouterContext {
12
+ sandbox: Sandbox;
13
+ sandboxId: string;
14
+ }
15
+
16
+ /**
17
+ * Wraps a tool handler that requires a {@link Sandbox}, automatically
18
+ * resolving it from the manager via the `sandboxId` on the router context.
19
+ *
20
+ * If no `sandboxId` is present the wrapper short-circuits with an error
21
+ * response and `data: null`, so the inner handler never runs without a
22
+ * valid sandbox.
23
+ *
24
+ * The sandbox type parameter `TSandbox` is inferred from the manager's
25
+ * `getSandbox` return type, allowing handlers to receive provider-specific
26
+ * sandbox subtypes (e.g. `DaytonaSandbox`) without manual casting.
27
+ *
28
+ * @param manager - Any object with a `getSandbox` method (e.g. {@link SandboxManager})
29
+ * @param handler - The inner handler that expects a sandbox context
30
+ * @returns A standard `ActivityToolHandler` that can be registered on the router
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * import { withSandbox, type SandboxContext } from 'zeitlich';
35
+ *
36
+ * // Generic sandbox — works with any provider:
37
+ * const bashHandler: ActivityToolHandler<BashArgs, ExecResult, SandboxContext> =
38
+ * async (args, { sandbox }) => {
39
+ * const result = await sandbox.exec(args.command);
40
+ * return { toolResponse: result.stdout, data: result };
41
+ * };
42
+ * const handler = withSandbox(manager, bashHandler);
43
+ *
44
+ * // Provider-specific sandbox — use SandboxManager<DaytonaSandbox>:
45
+ * const daytonaManager = new SandboxManager<DaytonaSandbox>(provider);
46
+ * const handler2 = withSandbox(daytonaManager, async (args, { sandbox }) => {
47
+ * // sandbox is typed as DaytonaSandbox here
48
+ * await sandbox.fs.uploadFiles([...]);
49
+ * return { toolResponse: 'ok', data: null };
50
+ * });
51
+ * ```
52
+ */
53
+ export function withSandbox<TArgs, TResult, TSandbox extends Sandbox = Sandbox>(
54
+ manager: { getSandbox(id: string): Promise<TSandbox> },
55
+ handler: (
56
+ args: TArgs,
57
+ context: RouterContext & { sandbox: TSandbox; sandboxId: string },
58
+ ) => Promise<ToolHandlerResponse<TResult>>,
59
+ ): ActivityToolHandler<TArgs, TResult | null> {
60
+ return async (args, context) => {
61
+ if (!context.sandboxId) {
62
+ return {
63
+ toolResponse: `Error: No sandbox configured for this agent. The ${context.toolName} tool requires a sandbox.`,
64
+ data: null,
65
+ };
66
+ }
67
+ const sandbox = await manager.getSandbox(context.sandboxId);
68
+ return handler(args, { ...context, sandbox, sandboxId: context.sandboxId });
69
+ };
70
+ }