@openrouter/agent 0.8.0 → 0.9.0

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 (87) hide show
  1. package/README.md +483 -5
  2. package/esm/index.d.ts +19 -4
  3. package/esm/index.d.ts.map +1 -1
  4. package/esm/index.js +9 -2
  5. package/esm/index.js.map +1 -1
  6. package/esm/inner-loop/call-model.d.ts.map +1 -1
  7. package/esm/inner-loop/call-model.js +40 -41
  8. package/esm/inner-loop/call-model.js.map +1 -1
  9. package/esm/inner-loop/resume-tool-results.d.ts +128 -0
  10. package/esm/inner-loop/resume-tool-results.d.ts.map +1 -0
  11. package/esm/inner-loop/resume-tool-results.js +244 -0
  12. package/esm/inner-loop/resume-tool-results.js.map +1 -0
  13. package/esm/lib/agent-tool.d.ts +92 -0
  14. package/esm/lib/agent-tool.d.ts.map +1 -0
  15. package/esm/lib/agent-tool.js +211 -0
  16. package/esm/lib/agent-tool.js.map +1 -0
  17. package/esm/lib/async-params.d.ts +82 -0
  18. package/esm/lib/async-params.d.ts.map +1 -1
  19. package/esm/lib/async-params.js +5 -0
  20. package/esm/lib/async-params.js.map +1 -1
  21. package/esm/lib/async-tool-registry.d.ts +134 -0
  22. package/esm/lib/async-tool-registry.d.ts.map +1 -0
  23. package/esm/lib/async-tool-registry.js +352 -0
  24. package/esm/lib/async-tool-registry.js.map +1 -0
  25. package/esm/lib/async-tools.d.ts +23 -0
  26. package/esm/lib/async-tools.d.ts.map +1 -0
  27. package/esm/lib/async-tools.js +19 -0
  28. package/esm/lib/async-tools.js.map +1 -0
  29. package/esm/lib/conversation-state.d.ts +1 -0
  30. package/esm/lib/conversation-state.d.ts.map +1 -1
  31. package/esm/lib/conversation-state.js +8 -0
  32. package/esm/lib/conversation-state.js.map +1 -1
  33. package/esm/lib/doom-loop.d.ts +533 -0
  34. package/esm/lib/doom-loop.d.ts.map +1 -0
  35. package/esm/lib/doom-loop.js +1221 -0
  36. package/esm/lib/doom-loop.js.map +1 -0
  37. package/esm/lib/hooks-schemas.d.ts +33 -0
  38. package/esm/lib/hooks-schemas.d.ts.map +1 -1
  39. package/esm/lib/hooks-schemas.js +54 -0
  40. package/esm/lib/hooks-schemas.js.map +1 -1
  41. package/esm/lib/hooks-types.d.ts +6 -2
  42. package/esm/lib/hooks-types.d.ts.map +1 -1
  43. package/esm/lib/hooks-types.js.map +1 -1
  44. package/esm/lib/model-result.d.ts +646 -10
  45. package/esm/lib/model-result.d.ts.map +1 -1
  46. package/esm/lib/model-result.js +2749 -153
  47. package/esm/lib/model-result.js.map +1 -1
  48. package/esm/lib/reusable-stream.d.ts +9 -0
  49. package/esm/lib/reusable-stream.d.ts.map +1 -1
  50. package/esm/lib/reusable-stream.js +17 -0
  51. package/esm/lib/reusable-stream.js.map +1 -1
  52. package/esm/lib/stream-transformers.d.ts +20 -0
  53. package/esm/lib/stream-transformers.d.ts.map +1 -1
  54. package/esm/lib/stream-transformers.js +45 -0
  55. package/esm/lib/stream-transformers.js.map +1 -1
  56. package/esm/lib/tool-check.d.ts +76 -0
  57. package/esm/lib/tool-check.d.ts.map +1 -0
  58. package/esm/lib/tool-check.js +241 -0
  59. package/esm/lib/tool-check.js.map +1 -0
  60. package/esm/lib/tool-concurrency.d.ts +42 -0
  61. package/esm/lib/tool-concurrency.d.ts.map +1 -0
  62. package/esm/lib/tool-concurrency.js +80 -0
  63. package/esm/lib/tool-concurrency.js.map +1 -0
  64. package/esm/lib/tool-context.d.ts +45 -1
  65. package/esm/lib/tool-context.d.ts.map +1 -1
  66. package/esm/lib/tool-context.js +60 -1
  67. package/esm/lib/tool-context.js.map +1 -1
  68. package/esm/lib/tool-executor.d.ts +52 -9
  69. package/esm/lib/tool-executor.d.ts.map +1 -1
  70. package/esm/lib/tool-executor.js +253 -21
  71. package/esm/lib/tool-executor.js.map +1 -1
  72. package/esm/lib/tool-orchestrator.d.ts.map +1 -1
  73. package/esm/lib/tool-orchestrator.js +87 -50
  74. package/esm/lib/tool-orchestrator.js.map +1 -1
  75. package/esm/lib/tool-task.d.ts +157 -0
  76. package/esm/lib/tool-task.d.ts.map +1 -0
  77. package/esm/lib/tool-task.js +206 -0
  78. package/esm/lib/tool-task.js.map +1 -0
  79. package/esm/lib/tool-types.d.ts +473 -14
  80. package/esm/lib/tool-types.d.ts.map +1 -1
  81. package/esm/lib/tool-types.js +59 -10
  82. package/esm/lib/tool-types.js.map +1 -1
  83. package/esm/lib/tool.d.ts +184 -2
  84. package/esm/lib/tool.d.ts.map +1 -1
  85. package/esm/lib/tool.js +193 -3
  86. package/esm/lib/tool.js.map +1 -1
  87. package/package.json +30 -2
@@ -1,12 +1,41 @@
1
1
  import type * as models from '@openrouter/sdk/models';
2
2
  import type { StreamEvents } from '@openrouter/sdk/models';
3
3
  import type { $ZodObject, $ZodShape, $ZodType, infer as zodInfer } from 'zod/v4/core';
4
+ import type { DoomLoopSerializedState } from './doom-loop.js';
5
+ import type { TaskLogLimits, ToolTaskMode, ToolTaskStatus } from './tool-task.js';
4
6
  /**
5
7
  * Tool type enum for enhanced tools
6
8
  */
7
9
  export declare enum ToolType {
8
10
  Function = "function"
9
11
  }
12
+ /**
13
+ * Narrow façade over a running task, handed to check-in `execute` handlers
14
+ * via `turnContext.task`. Lets a custom check read progress, steer, or
15
+ * cancel without exposing the full registry.
16
+ */
17
+ export interface ToolTaskHandle {
18
+ readonly taskId: string;
19
+ readonly toolName: string;
20
+ readonly mode: ToolTaskMode;
21
+ /** Current lifecycle status. */
22
+ status(): ToolTaskStatus;
23
+ /** The `status` check-view summary. */
24
+ statusView(): Record<string, unknown>;
25
+ /** Last `n` log entries (oldest first). */
26
+ tailLogs(n: number): Array<{
27
+ seq: number;
28
+ at: number;
29
+ data: unknown;
30
+ kind: string;
31
+ }>;
32
+ /** Rendered transcript, truncated to `maxChars` keeping the tail. */
33
+ transcript(maxChars?: number): string;
34
+ /** Queue a steering message for the run body (`ctx.onMessage`). */
35
+ send(message: unknown): void;
36
+ /** Cancel the task. Returns true when a working task was cancelled. */
37
+ cancel(reason?: string): boolean;
38
+ }
10
39
  /**
11
40
  * Turn context passed to tool execute functions and async parameter resolution
12
41
  * Contains information about the current conversation state
@@ -18,6 +47,21 @@ export interface TurnContext {
18
47
  numberOfTurns: number;
19
48
  /** The full request being sent to the API (only available during tool execution) */
20
49
  turnRequest?: models.ResponsesRequest;
50
+ /**
51
+ * CHECK CALLS ONLY: lifecycle status of the task the model is checking on.
52
+ * Absent on every other path.
53
+ */
54
+ toolCallStatus?: ToolTaskStatus;
55
+ /**
56
+ * CHECK CALLS ONLY: the data of every retained yield/log from the task's
57
+ * `run` so far, oldest first. Absent on every other path.
58
+ */
59
+ accumulatedYieldedEvents?: unknown[];
60
+ /**
61
+ * CHECK CALLS ONLY: façade over the task being checked (read progress,
62
+ * steer via `send`, cancel). Absent on every other path.
63
+ */
64
+ task?: ToolTaskHandle;
21
65
  }
22
66
  /**
23
67
  * Extract context schema type from a tool definition.
@@ -70,6 +114,90 @@ export type ToolExecuteContext<TName extends string = string, TContext extends R
70
114
  shared: Readonly<TShared>;
71
115
  /** Mutate the shared context in the store (persists across turns) */
72
116
  setSharedContext(partial: Partial<TShared>): void;
117
+ /**
118
+ * Abort signal for this tool call. Fires when the run is aborted
119
+ * (`callModel`'s `signal` option or `ModelResult.cancel()`), when the
120
+ * tool's `timeoutMs` (or the run-level `toolTimeoutMs`) elapses, or when
121
+ * a background task is cancelled via `cancelTask`. Cooperative: tool
122
+ * bodies should pass it to their own I/O (fetch, child processes, ...).
123
+ * Always present — a never-aborting signal is supplied when no
124
+ * cancellation sources exist.
125
+ */
126
+ readonly signal: AbortSignal;
127
+ /** The id of the tool call being executed (for correlating external work). */
128
+ readonly callId?: string;
129
+ /**
130
+ * The conversation id when a `StateAccessor` is configured. Deferred
131
+ * tools typically hand this to the external system so its webhook can
132
+ * locate the conversation to resume.
133
+ */
134
+ readonly conversationId?: string;
135
+ };
136
+ declare const DEFERRED_BRAND: unique symbol;
137
+ /**
138
+ * Returned by `ctx.defer()`. Returning it from `run` parks the call on a
139
+ * durable external task: the model gets a pending placeholder and the run
140
+ * pauses (`status: 'awaiting_async_tools'`) until the task is resolved via
141
+ * the tool's `.resolve()` / `.fail()` / `.cancel()` methods or
142
+ * `resumeToolResults()` — possibly from another process.
143
+ *
144
+ * Branded with TOutput so a handle from one tool cannot be returned from
145
+ * another whose outputSchema differs.
146
+ *
147
+ * Two distinct markers, deliberately:
148
+ * - `[DEFERRED_BRAND]` is TYPE-ONLY (`declare const` — no runtime value
149
+ * exists) and carries the TOutput brand for compile-time safety.
150
+ * - `__deferred` is the RUNTIME marker {@link isDeferredHandle} checks.
151
+ * `ctx.defer()` (the sole creator, in model-result.ts) always sets it.
152
+ * Changing either without the other breaks deferred-handle detection —
153
+ * keep them in sync.
154
+ */
155
+ export interface DeferredHandle<TOutput = unknown> {
156
+ readonly [DEFERRED_BRAND]: TOutput;
157
+ /** Runtime marker set by `ctx.defer()`; checked by {@link isDeferredHandle}. */
158
+ readonly __deferred: true;
159
+ readonly taskId: string;
160
+ readonly pollAfterMs?: number;
161
+ readonly expiresAt?: number;
162
+ readonly ack?: string | Record<string, unknown>;
163
+ }
164
+ /** Runtime guard for {@link DeferredHandle} (structural — brand is type-only). */
165
+ export declare function isDeferredHandle(value: unknown): value is DeferredHandle;
166
+ /** Options for `ctx.defer()`. */
167
+ export type DeferOptions = {
168
+ /** Poll-interval hint surfaced in the placeholder and to external pollers. */
169
+ pollAfterMs?: number;
170
+ /** Unix ms after which the task is considered expired. */
171
+ expiresAt?: number;
172
+ /** Model-facing note merged into the placeholder (overrides the tool-level `ack`). */
173
+ ack?: string | Record<string, unknown>;
174
+ };
175
+ /**
176
+ * Context passed to a unified tool's `run`. Extends the base execute
177
+ * context with the async-task affordances: deferral, logging, and the
178
+ * steering inbox.
179
+ */
180
+ export type ToolRunContext<TName extends string = string, TContext extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown, TShared extends Record<string, unknown> = Record<string, unknown>> = ToolExecuteContext<TName, TContext, TShared> & {
181
+ /**
182
+ * Park this call on a durable external task and return the handle:
183
+ * `return ctx.defer(ticket.id)`. Only valid on `lifecycle: 'deferred'`
184
+ * tools — throws elsewhere.
185
+ */
186
+ defer(taskId: string, options?: DeferOptions): DeferredHandle<TOutput>;
187
+ /**
188
+ * Append a log entry without yielding — progress for non-generator `run`
189
+ * bodies. Same pipeline as a yield: validated against `eventSchema` when
190
+ * declared, appended to the task log, surfaced as `tool.preliminary_result`.
191
+ */
192
+ log(entry: unknown): void;
193
+ /**
194
+ * Register a steering handler. Messages sent via a check call's
195
+ * `turnContext.task.send(...)` or `ModelResult.sendToTask()` are delivered
196
+ * here (queued until registration).
197
+ */
198
+ onMessage(handler: (message: unknown) => void): void;
199
+ /** The task's id (background/agent: engine-generated, present once escaped). */
200
+ readonly taskId?: string;
73
201
  };
74
202
  /**
75
203
  * Context map keyed by tool name for callModel's `context` option.
@@ -130,6 +258,45 @@ export type NextTurnParamsFunctions<TInput> = {
130
258
  export type ToolApprovalCheck<TInput> = {
131
259
  bivarianceHack(params: TInput, context: TurnContext): boolean | Promise<boolean>;
132
260
  }['bivarianceHack'];
261
+ /**
262
+ * Function form of {@link ToolLoopKey}: computes the key material that
263
+ * identifies a call of this tool for doom-loop detection.
264
+ *
265
+ * The returned value is canonicalized (RFC 8785) and hashed by the engine —
266
+ * tools declare *what identifies a call*, the engine owns *how it is
267
+ * fingerprinted*. Two calls whose key material canonicalizes identically
268
+ * count as the same call.
269
+ *
270
+ * - Return a focused subset for precise identity: a web-search tool returns
271
+ * its normalized query; a bash tool returns `{ command, cwd, env }`.
272
+ * - Return `null` to exempt THIS call from detection (e.g. a legitimate
273
+ * repeat the tool can recognize from its arguments).
274
+ * - Returning `undefined` (a bare `return;`) is treated as a bug: the
275
+ * engine warns and falls back to the full-arguments identity.
276
+ *
277
+ * Must be pure and deterministic. A throwing `loopKey` falls back to the
278
+ * full-arguments fingerprint (detection must never take down a run).
279
+ */
280
+ export type ToolLoopKeyFn<TInput> = {
281
+ bivarianceHack(params: TInput): unknown;
282
+ }['bivarianceHack'];
283
+ /**
284
+ * Doom-loop identity declaration for a tool (see the `doomLoop` option on
285
+ * `callModel`). Computed like every other tool hook — a plain function over
286
+ * the call's validated arguments:
287
+ *
288
+ * - function — computes key material per call ({@link ToolLoopKeyFn}), e.g.
289
+ * `({ command, cwd }) => ({ command, cwd })` for a bash tool. Returning
290
+ * `null` exempts individual calls (a legitimate repeat the tool can
291
+ * recognize from its arguments).
292
+ * - field-name array — the declarative subset identifying a call, e.g.
293
+ * `['command', 'cwd']` for a bash tool. Data, not code: serializable, so
294
+ * it survives tool caches and can be advertised over the MCP wire.
295
+ * - `false` — this tool is statically exempt (repetition is its job, e.g. a
296
+ * status poller).
297
+ * - absent — the full validated arguments object is the identity.
298
+ */
299
+ export type ToolLoopKey<TInput> = ToolLoopKeyFn<TInput> | readonly string[] | false;
133
300
  /**
134
301
  * Content item types for tool output to model.
135
302
  * These match the Responses API format for multimodal tool outputs.
@@ -175,6 +342,17 @@ export interface BaseToolFunction<TInput extends $ZodObject<$ZodShape>, TCtx ext
175
342
  name: string;
176
343
  description?: string;
177
344
  inputSchema: TInput;
345
+ /**
346
+ * Whether providers should enforce strict schema adherence when generating
347
+ * this tool's call arguments (OpenAI structured-outputs style). Serialized
348
+ * onto the wire tool definition; `null`/absent leaves provider default.
349
+ *
350
+ * OpenAI-style strict mode requires every declared object property to be
351
+ * listed in `required`. Use Zod `.nullable()` for values that may be absent
352
+ * conceptually; `.optional()` produces a schema those providers may reject.
353
+ * The SDK forwards the caller's schema unchanged.
354
+ */
355
+ strict?: boolean | null;
178
356
  /**
179
357
  * Zod schema declaring the context data this tool needs.
180
358
  * `readonly` keeps TCtx covariant so tools carrying a concrete schema stay
@@ -187,6 +365,34 @@ export interface BaseToolFunction<TInput extends $ZodObject<$ZodShape>, TCtx ext
187
365
  * Can be a boolean or an async function that receives the tool's input params and context
188
366
  */
189
367
  requireApproval?: boolean | ToolApprovalCheck<zodInfer<TInput>>;
368
+ /**
369
+ * Doom-loop identity for this tool's calls — see {@link ToolLoopKey}.
370
+ * A computed function over the call's arguments, a field-name array
371
+ * (serializable — used by MCP tool caches), or `false` (exempt).
372
+ * Absent: the full validated arguments object is the identity.
373
+ */
374
+ loopKey?: ToolLoopKey<zodInfer<TInput>>;
375
+ /**
376
+ * Deadline for one execution of this tool, in milliseconds. When it
377
+ * elapses the round stops waiting: the model receives a
378
+ * `{ error, code: 'tool_timeout' }` output immediately and the tool's
379
+ * `ctx.signal` is aborted. The timeout bounds the round's WAIT, not the
380
+ * tool body's execution — a body that ignores its signal keeps running
381
+ * detached (its result is discarded). Overrides the run-level
382
+ * `toolTimeoutMs` default.
383
+ */
384
+ timeoutMs?: number;
385
+ /**
386
+ * Maximum simultaneous in-flight executions of THIS tool across the run
387
+ * — round-synchronous and background alike (background bodies re-acquire
388
+ * the gate for their full duration). Typically encodes an external
389
+ * constraint (one DB connection, a rate-limited API key). Excess calls
390
+ * queue FIFO. Queue wait does NOT count against `timeoutMs` for
391
+ * round-synchronous calls (the deadline starts once the slot is held);
392
+ * background tasks queue against their own registry-tracked timeout.
393
+ * Unbounded when absent.
394
+ */
395
+ maxConcurrency?: number;
190
396
  }
191
397
  /**
192
398
  * Regular tool with synchronous or asynchronous execute function and optional outputSchema
@@ -195,6 +401,14 @@ export interface BaseToolFunction<TInput extends $ZodObject<$ZodShape>, TCtx ext
195
401
  */
196
402
  export interface ToolFunctionWithExecute<TInput extends $ZodObject<$ZodShape>, TOutput extends $ZodType = $ZodType<unknown>, TContext extends Record<string, unknown> = Record<string, unknown>, TName extends string = string, TCtx extends $ZodObject<$ZodShape> = $ZodObject<$ZodShape>> extends BaseToolFunction<TInput, TCtx> {
197
403
  outputSchema?: TOutput;
404
+ /**
405
+ * Absent on regular tools. Declared as `undefined`-only so
406
+ * `UnifiedToolFunction` (which requires `run` and `lifecycle`) is
407
+ * structurally DISJOINT from this interface — type-guard narrowing must
408
+ * not keep unified tools in the regular-execute union.
409
+ */
410
+ readonly run?: undefined;
411
+ readonly lifecycle?: undefined;
198
412
  execute(params: zodInfer<TInput>, context?: ToolExecuteContext<TName, TContext>): Promise<zodInfer<TOutput>> | zodInfer<TOutput>;
199
413
  /** Convert tool execution output to model-facing output */
200
414
  toModelOutput?: ToModelOutputFunction<zodInfer<TInput>, zodInfer<TOutput>>;
@@ -258,6 +472,110 @@ export interface HITLToolFunction<TInput extends $ZodObject<$ZodShape>, TOutput
258
472
  onResponseReceived?(rawResult: unknown, context?: ToolExecuteContext<TName, TContext>): Promise<zodInfer<TOutput>> | zodInfer<TOutput>;
259
473
  toModelOutput?: ToModelOutputFunction<zodInfer<TInput>, zodInfer<TOutput>>;
260
474
  }
475
+ /**
476
+ * Model-facing acknowledgement for an async tool's placeholder output.
477
+ * A string becomes the placeholder's `note`; an object is merged into the
478
+ * placeholder payload; a function computes either from the call's input.
479
+ */
480
+ export type AsyncToolAck<TInput> = string | Record<string, unknown> | {
481
+ bivarianceHack(input: TInput): string | Record<string, unknown>;
482
+ }['bivarianceHack'];
483
+ /** How a unified tool's execution relates to the tool round. */
484
+ export type ToolLifecycle = 'sync' | 'background' | 'deferred';
485
+ /**
486
+ * Check-in configuration for a long-running tool. The model interacts with
487
+ * running tasks through the SINGLE universal `task` tool (`action:
488
+ * 'check' | 'steer' | 'result' | 'cancel'`, addressed by `taskId`); the
489
+ * engine dispatches those calls to the owning tool's config here — so the
490
+ * wire surface stays constant while the implementation stays tool-specific.
491
+ *
492
+ * - `true` — explicit opt-in to the SDK default handling (long-running
493
+ * tools get it even without this).
494
+ * - `schema` — validates the task tool's free-form `params` object when the
495
+ * model passes one to this tool's custom handler.
496
+ * - `execute` — custom check handler, replacing the SDK default for
497
+ * `action: 'check'` calls targeting this tool's tasks. Receives the
498
+ * validated `params` and a TurnContext populated with `toolCallStatus`,
499
+ * `accumulatedYieldedEvents`, and the `task` handle. Its return value is
500
+ * the tool output the model sees.
501
+ */
502
+ export type ToolCheckConfig<TCheckParams = Record<string, unknown>> = true | {
503
+ /**
504
+ * Validation schema for the model-supplied `params` on check calls.
505
+ * SECURITY: `params` is MODEL INPUT (and models can be steered by
506
+ * injected tool/web content). Without a schema the handler receives
507
+ * the raw record — declare one whenever `execute` forwards params
508
+ * into steering/cancel or any other side effect, exactly as you
509
+ * would validate a tool's inputSchema.
510
+ */
511
+ schema?: $ZodObject<$ZodShape>;
512
+ execute?: {
513
+ bivarianceHack(params: TCheckParams, turnContext: TurnContext): unknown | Promise<unknown>;
514
+ }['bivarianceHack'];
515
+ };
516
+ /**
517
+ * The unified `run`-based tool kind. One shape covers every lifecycle:
518
+ *
519
+ * - `'sync'` (default): `run` is awaited in the round, exactly like `execute`.
520
+ * - `'background'`: the loop does not wait. Work settling within `graceMs`
521
+ * behaves like a sync call; otherwise the model receives a pending
522
+ * placeholder, the loop continues, and the return value is injected as a
523
+ * `tool_task_result` envelope when it settles.
524
+ * - `'deferred'`: `run` may return `ctx.defer(taskId)` to park the call on a
525
+ * durable external task — the run pauses (`awaiting_async_tools`) until
526
+ * the task is resolved via `.resolve()`/`.fail()`/`.cancel()` or
527
+ * `resumeToolResults()`, possibly from another process. Returning a plain
528
+ * value resolves immediately (typed fast path).
529
+ *
530
+ * `run` is an async function or an async generator. Generator yields become
531
+ * the task's LOG entries (feeding check-in views and
532
+ * `tool.preliminary_result` events); the generator's RETURN value is the
533
+ * final result, validated against `outputSchema`. (This is stricter than
534
+ * legacy `execute` generators, which accept a final yield as the result.)
535
+ */
536
+ export interface UnifiedToolFunction<TInput extends $ZodObject<$ZodShape>, TOutput extends $ZodType = $ZodType<unknown>, TEvent extends $ZodType = $ZodType<unknown>, TContext extends Record<string, unknown> = Record<string, unknown>, TName extends string = string, TCtx extends $ZodObject<$ZodShape> = $ZodObject<$ZodShape>> extends BaseToolFunction<TInput, TCtx> {
537
+ /** Discriminator against every legacy kind. */
538
+ readonly lifecycle: ToolLifecycle;
539
+ /**
540
+ * Required for 'background' | 'deferred' (results settle after the round,
541
+ * possibly in another process). Optional for 'sync' (inferred from run).
542
+ */
543
+ outputSchema?: TOutput;
544
+ /** Validates `run` yields / `ctx.log()` entries when declared. */
545
+ eventSchema?: TEvent;
546
+ /** Model-facing acknowledgement merged into the pending placeholder. */
547
+ ack?: AsyncToolAck<zodInfer<TInput>>;
548
+ /**
549
+ * Background only: hold the round this long (ms) before emitting a
550
+ * placeholder. Work settling in-window produces a plain synchronous
551
+ * output. Default 250; `0` always placeholders.
552
+ */
553
+ graceMs?: number;
554
+ /** Deferred only: default poll-interval hint for tasks from this tool. */
555
+ pollAfterMs?: number;
556
+ /** Check-in configuration — see {@link ToolCheckConfig}. */
557
+ check?: ToolCheckConfig;
558
+ /** Set by `tool.agent()`: marks the run as a child-conversation driver. */
559
+ readonly kind?: 'agent';
560
+ /** Per-task log ring-buffer overrides. */
561
+ logLimits?: Partial<TaskLogLimits>;
562
+ run(params: zodInfer<TInput>, context?: ToolRunContext<TName, TContext, zodInfer<TOutput>>): Promise<zodInfer<TOutput> | DeferredHandle<zodInfer<TOutput>>> | zodInfer<TOutput> | DeferredHandle<zodInfer<TOutput>> | AsyncGenerator<zodInfer<TEvent>, zodInfer<TOutput> | DeferredHandle<zodInfer<TOutput>>>;
563
+ /**
564
+ * Convert tool execution output to model-facing output.
565
+ *
566
+ * ROUND-SYNCHRONOUS RESULTS ONLY: applies to `lifecycle: 'sync'` results
567
+ * and background work that settles inside its grace window — the paths
568
+ * that produce a `function_call_output`. Late-delivered results
569
+ * (background past the grace window, deferred completions) arrive as a
570
+ * `tool_task_result` JSON envelope in a user-role message, which cannot
571
+ * carry this mapper's content-item arrays; they deliver the validated
572
+ * output verbatim.
573
+ */
574
+ toModelOutput?: ToModelOutputFunction<zodInfer<TInput>, zodInfer<TOutput>>;
575
+ /** Absent on unified tools — keeps them disjoint from legacy kinds. */
576
+ readonly execute?: undefined;
577
+ readonly onToolCalled?: undefined;
578
+ }
261
579
  /**
262
580
  * Tool with execute function (regular or generator)
263
581
  * @template TCtx - The concrete contextSchema type when one was provided to `tool()`
@@ -291,10 +609,19 @@ export type HITLTool<TInput extends $ZodObject<$ZodShape> = $ZodObject<$ZodShape
291
609
  function: HITLToolFunction<TInput, TOutput, TContext, string, TCtx>;
292
610
  };
293
611
  /**
294
- * Union type of all client-executed tool shapes (function, generator, manual, HITL).
295
- * These run in the user's process via the agent SDK's tool execution loop.
612
+ * Unified tool wrapper (`tool()` with `run`)
613
+ * @template TCtx - The concrete contextSchema type when one was provided
614
+ */
615
+ export type UnifiedTool<TInput extends $ZodObject<$ZodShape> = $ZodObject<$ZodShape>, TOutput extends $ZodType = $ZodType<unknown>, TEvent extends $ZodType = $ZodType<unknown>, TContext extends Record<string, unknown> = Record<string, unknown>, TCtx extends $ZodObject<$ZodShape> = $ZodObject<$ZodShape>> = {
616
+ type: ToolType.Function;
617
+ function: UnifiedToolFunction<TInput, TOutput, TEvent, TContext, string, TCtx>;
618
+ };
619
+ /**
620
+ * Union type of all client-executed tool shapes (function, generator, manual,
621
+ * HITL, unified `run`). These run in the user's process via the agent SDK's
622
+ * tool execution loop.
296
623
  */
297
- export type ClientTool = ToolWithExecute<$ZodObject<$ZodShape>, $ZodType<unknown>> | ToolWithGenerator<$ZodObject<$ZodShape>, $ZodType<unknown>, $ZodType<unknown>> | ManualTool<$ZodObject<$ZodShape>, $ZodType<unknown>> | HITLTool<$ZodObject<$ZodShape>, $ZodType<unknown>>;
624
+ export type ClientTool = ToolWithExecute<$ZodObject<$ZodShape>, $ZodType<unknown>> | ToolWithGenerator<$ZodObject<$ZodShape>, $ZodType<unknown>, $ZodType<unknown>> | ManualTool<$ZodObject<$ZodShape>, $ZodType<unknown>> | HITLTool<$ZodObject<$ZodShape>, $ZodType<unknown>> | UnifiedTool<$ZodObject<$ZodShape>, $ZodType<unknown>, $ZodType<unknown>>;
298
625
  /**
299
626
  * Config payload for an OpenRouter server-executed tool. Derived directly
300
627
  * from the SDK's request-tool union so new server tools flow through with
@@ -461,11 +788,30 @@ export declare function isMcpTool(tool: Tool): tool is McpBranded;
461
788
  */
462
789
  export declare function isClientTool(tool: Tool): tool is ClientTool;
463
790
  /**
464
- * Type guard to check if a tool has an execute function
791
+ * Type guard to check if a tool is a unified `run`-based tool
792
+ */
793
+ export declare function isUnifiedTool(tool: Tool): tool is UnifiedTool;
794
+ /**
795
+ * True when the tool can outlive its round: a unified tool with a
796
+ * non-'sync' lifecycle. Drives check-schema generation and placeholder
797
+ * wording.
798
+ */
799
+ export declare function isLongRunningTool(tool: Tool): boolean;
800
+ /**
801
+ * True when the tool is an agent tool (`tool.agent()`): a unified tool
802
+ * whose run drives a child conversation.
803
+ */
804
+ export declare function isAgentTool(tool: Tool): boolean;
805
+ /**
806
+ * Type guard to check if a tool has an execute function (regular or
807
+ * generator). Unified `run` tools are excluded — they have their own
808
+ * dispatch and this guard's predicate would misclassify them.
465
809
  */
466
810
  export declare function hasExecuteFunction(tool: Tool): tool is ToolWithExecute | ToolWithGenerator;
467
811
  /**
468
- * Type guard to check if a tool uses a generator (has eventSchema)
812
+ * Type guard to check if a tool uses a generator (has eventSchema).
813
+ * Unified tools may declare an `eventSchema` for run yields but are not
814
+ * legacy generator tools.
469
815
  */
470
816
  export declare function isGeneratorTool(tool: Tool): tool is ToolWithGenerator;
471
817
  /**
@@ -473,7 +819,7 @@ export declare function isGeneratorTool(tool: Tool): tool is ToolWithGenerator;
473
819
  */
474
820
  export declare function isRegularExecuteTool(tool: Tool): tool is ToolWithExecute;
475
821
  /**
476
- * Type guard to check if a tool is a manual tool (no execute and no onToolCalled)
822
+ * Type guard to check if a tool is a manual tool (no execute, no onToolCalled, no run)
477
823
  */
478
824
  export declare function isManualTool(tool: Tool): tool is ManualTool;
479
825
  /**
@@ -481,11 +827,12 @@ export declare function isManualTool(tool: Tool): tool is ManualTool;
481
827
  */
482
828
  export declare function isHITLTool(tool: Tool): tool is HITLTool;
483
829
  /**
484
- * Type guard: true if the tool can be auto-resolved within a turn — either
485
- * through a client execute/generator function or through a HITL onToolCalled hook.
486
- * Returns false for manual tools (which always pause) and server tools.
830
+ * Type guard: true if the tool can be auto-resolved within a turn — through a
831
+ * client execute/generator function, a HITL onToolCalled hook, or a unified
832
+ * `run` (which always produces at least a placeholder output). Returns false
833
+ * for manual tools (which always pause) and server tools.
487
834
  */
488
- export declare function isAutoResolvableTool(tool: Tool): tool is ToolWithExecute | ToolWithGenerator | HITLTool;
835
+ export declare function isAutoResolvableTool(tool: Tool): tool is ToolWithExecute | ToolWithGenerator | HITLTool | UnifiedTool;
489
836
  /**
490
837
  * Parsed tool call from API response
491
838
  * @template T - The tool type to infer argument types from
@@ -657,6 +1004,38 @@ export type ToolCallOutputEvent = {
657
1004
  output: models.FunctionCallOutputItem;
658
1005
  timestamp: number;
659
1006
  };
1007
+ /**
1008
+ * Emitted when an async tool call escapes the round: a background tool's
1009
+ * execute outlived its grace window, or a deferred tool's start returned a
1010
+ * task handle. The model has received a pending placeholder output.
1011
+ */
1012
+ export type ToolAsyncStartedEvent = {
1013
+ type: 'tool.async_started';
1014
+ toolCallId: string;
1015
+ toolName: string;
1016
+ taskId: string;
1017
+ mode: ToolTaskMode;
1018
+ /** The model-facing acknowledgement carried in the placeholder, if any. */
1019
+ ack?: unknown;
1020
+ timestamp: number;
1021
+ };
1022
+ /**
1023
+ * Emitted when an async tool task settles — completed, failed, cancelled,
1024
+ * timed out, or expired. `delivery` reports how (or whether) the outcome
1025
+ * reached the model: `'injected'` into this run's conversation,
1026
+ * `'pending_resume'` recorded on state for the next run, or `'dropped'`
1027
+ * (run ended under `onRunEnd: 'detach'`).
1028
+ */
1029
+ export type ToolAsyncSettledEvent<TResult = unknown> = {
1030
+ type: 'tool.async_settled';
1031
+ toolCallId: string;
1032
+ taskId: string;
1033
+ status: 'completed' | 'failed' | 'cancelled' | 'timed_out' | 'expired';
1034
+ result?: TResult;
1035
+ error?: string;
1036
+ delivery: 'injected' | 'pending_resume' | 'dropped';
1037
+ timestamp: number;
1038
+ };
660
1039
  /**
661
1040
  * Turn start event emitted at the beginning of each API turn
662
1041
  * Turn 0 is the initial request, subsequent turns follow tool execution
@@ -681,7 +1060,15 @@ export type TurnEndEvent = {
681
1060
  * @template TEvent - The event type from generator tools
682
1061
  * @template TResult - The result type from tool execution
683
1062
  */
684
- export type ResponseStreamEvent<TEvent = unknown, TResult = unknown> = StreamEvents | ToolPreliminaryResultEvent<TEvent> | ToolResultEvent<TResult, TEvent> | ToolCallOutputEvent | TurnStartEvent | TurnEndEvent;
1063
+ export type ResponseStreamEvent<TEvent = unknown, TResult = unknown> = StreamEvents | ToolPreliminaryResultEvent<TEvent> | ToolResultEvent<TResult, TEvent> | ToolCallOutputEvent | ToolAsyncStartedEvent | ToolAsyncSettledEvent<TResult> | TurnStartEvent | TurnEndEvent;
1064
+ /**
1065
+ * Type guard to check if an event is a tool async started event
1066
+ */
1067
+ export declare function isToolAsyncStartedEvent(event: ResponseStreamEvent): event is ToolAsyncStartedEvent;
1068
+ /**
1069
+ * Type guard to check if an event is a tool async settled event
1070
+ */
1071
+ export declare function isToolAsyncSettledEvent<TResult = unknown>(event: ResponseStreamEvent<unknown, TResult>): event is ToolAsyncSettledEvent<TResult>;
685
1072
  /**
686
1073
  * Type guard to check if an event is a tool preliminary result event
687
1074
  */
@@ -781,8 +1168,52 @@ export interface PartialResponse<TTools extends readonly Tool[] = readonly Tool[
781
1168
  * fn) tool calls are unresolved; the loop stopped so the caller can execute
782
1169
  * them client-side and continue. Distinct from `awaiting_hitl` — HITL tools
783
1170
  * have an `onToolCalled` hook; manual tools do not.
784
- */
785
- export type ConversationStatus = 'complete' | 'interrupted' | 'awaiting_approval' | 'awaiting_hitl' | 'awaiting_client_tools' | 'in_progress';
1171
+ * - `awaiting_async_tools`: one or more deferred tools (`tool.deferred`)
1172
+ * started a durable external task; the loop paused until the task is
1173
+ * resolved via the tool's `.resolve()` / `.fail()` / `.cancel()` methods
1174
+ * (or `resumeToolResults()`), possibly from a different process. Distinct
1175
+ * from `awaiting_client_tools` — the tool DID execute (its `start` ran and
1176
+ * a placeholder output was persisted); only its final result is pending.
1177
+ */
1178
+ export type ConversationStatus = 'complete' | 'interrupted' | 'awaiting_approval' | 'awaiting_hitl' | 'awaiting_client_tools' | 'awaiting_async_tools' | 'in_progress';
1179
+ export type { ToolTaskStatus } from './tool-task.js';
1180
+ /**
1181
+ * A pending (or settled) async tool task tracked on
1182
+ * {@link ConversationState.pendingAsyncTools}. One entry per background /
1183
+ * deferred call that produced a placeholder output.
1184
+ */
1185
+ export interface PendingAsyncTool {
1186
+ /** The originating `function_call`'s call id. */
1187
+ callId: string;
1188
+ /** Durable task id (deferred: caller-supplied; background/agent: generated). */
1189
+ taskId: string;
1190
+ /** The tool's name. */
1191
+ name: string;
1192
+ /** How the task escapes the round. */
1193
+ mode: ToolTaskMode;
1194
+ /** Current lifecycle status (MCP-Tasks-compatible vocabulary). */
1195
+ status: ToolTaskStatus;
1196
+ /** Unix ms when the task started. */
1197
+ startedAt: number;
1198
+ /** Unix ms after which the task is considered expired. */
1199
+ expiresAt?: number;
1200
+ /** Poll-interval hint surfaced to the model and external pollers. */
1201
+ pollAfterMs?: number;
1202
+ /**
1203
+ * Set on a background task left running when the run ended under
1204
+ * `onRunEnd: 'detach'` — its result will never be delivered.
1205
+ */
1206
+ orphaned?: boolean;
1207
+ /**
1208
+ * The most recent log entry, truncated (~200 chars) — the one piece of
1209
+ * progress that survives a process restart. Additive within
1210
+ * ConversationState version 1.
1211
+ */
1212
+ lastLog?: {
1213
+ at: number;
1214
+ text: string;
1215
+ };
1216
+ }
786
1217
  /**
787
1218
  * State for multi-turn conversations with persistence and approval gates
788
1219
  * @template TTools - The tools array type for proper type inference
@@ -803,6 +1234,16 @@ export interface ConversationState<TTools extends readonly Tool[] = readonly Too
803
1234
  messages: models.InputsUnion;
804
1235
  /** Previous response ID for chaining (OpenRouter server-side optimization) */
805
1236
  previousResponseId?: string;
1237
+ /**
1238
+ * RFC 8785 canonical key of the forced `toolChoice` most recently consumed
1239
+ * by the active logical run. Persisted across approval, HITL, client-tool,
1240
+ * and async-tool pauses so an unchanged forced choice stays relaxed after
1241
+ * resume while a newly resolved forced value can re-arm.
1242
+ *
1243
+ * Cleared after an unforced caller choice is successfully dispatched or the
1244
+ * run reaches a terminal exit.
1245
+ */
1246
+ consumedForcedToolChoiceKey?: string;
806
1247
  /** Tool calls awaiting human approval */
807
1248
  pendingToolCalls?: Array<ParsedToolCall<TTools[number]>>;
808
1249
  /** Tool results executed but not yet sent to the model */
@@ -811,6 +1252,25 @@ export interface ConversationState<TTools extends readonly Tool[] = readonly Too
811
1252
  partialResponse?: PartialResponse<TTools>;
812
1253
  /** Signal from a new request to interrupt this conversation */
813
1254
  interruptedBy?: string;
1255
+ /**
1256
+ * Doom-loop detector state (see the `doomLoop` option on `callModel`).
1257
+ * Bounded plain JSON, persisted so repetition streaks survive
1258
+ * serialize → resume. Absent when detection is off. Additive within
1259
+ * ConversationState version 1.
1260
+ */
1261
+ doomLoop?: DoomLoopSerializedState;
1262
+ /**
1263
+ * Async tool tasks (background / deferred) whose placeholder output was
1264
+ * sent to the model but whose real result has not been delivered yet.
1265
+ * Additive within ConversationState version 1.
1266
+ */
1267
+ pendingAsyncTools?: PendingAsyncTool[];
1268
+ /**
1269
+ * Call ids whose async result has already been delivered — the
1270
+ * at-most-once guard against replayed webhook resolutions. Additive
1271
+ * within ConversationState version 1.
1272
+ */
1273
+ settledAsyncCallIds?: string[];
814
1274
  /** Current status of the conversation */
815
1275
  status: ConversationStatus;
816
1276
  /** Creation timestamp (Unix ms) */
@@ -863,5 +1323,4 @@ export declare function toolHasApprovalConfigured(tool: Tool): boolean;
863
1323
  * Type guard to check if any tools in array have approval configured at runtime
864
1324
  */
865
1325
  export declare function hasApprovalRequiredTools(tools: readonly Tool[]): boolean;
866
- export {};
867
1326
  //# sourceMappingURL=tool-types.d.ts.map