@lunora/agent 1.0.0-alpha.1 → 1.0.0-alpha.100

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 (92) hide show
  1. package/README.md +2 -0
  2. package/dist/channels.d.mts +33 -21
  3. package/dist/channels.d.ts +33 -21
  4. package/dist/channels.mjs +1 -181
  5. package/dist/component.d.mts +61 -56
  6. package/dist/component.d.ts +61 -56
  7. package/dist/component.mjs +2 -407
  8. package/dist/inbound.d.mts +23 -16
  9. package/dist/inbound.d.ts +23 -16
  10. package/dist/inbound.mjs +1 -32
  11. package/dist/index.d.mts +542 -409
  12. package/dist/index.d.ts +542 -409
  13. package/dist/index.mjs +1 -18
  14. package/dist/naming.d.mts +23 -10
  15. package/dist/naming.d.ts +23 -10
  16. package/dist/naming.mjs +1 -8
  17. package/dist/packem_shared/AGENT_MODULE-M4D1EejI.mjs +1 -0
  18. package/dist/packem_shared/VoiceSessionDO-D238XrlM.mjs +1 -0
  19. package/dist/packem_shared/adaptMcpResult-CVlq-_TO.mjs +2 -0
  20. package/dist/packem_shared/agent-loop-CE_Q6bk9.mjs +3 -0
  21. package/dist/packem_shared/agentAsTool-CgZb6ycK.mjs +1 -0
  22. package/dist/packem_shared/base64-5eyBfWO3.mjs +1 -0
  23. package/dist/packem_shared/braintrustTelemetry-Byne8uPK.mjs +1 -0
  24. package/dist/packem_shared/branch-marker-boZ00zmk.mjs +1 -0
  25. package/dist/packem_shared/buildModelMessages-Y4tGo1T9.mjs +5 -0
  26. package/dist/packem_shared/codeTool-BQb-K3my.mjs +1 -0
  27. package/dist/packem_shared/collectAgenticMemoryTools-BeMWT2qt.mjs +1 -0
  28. package/dist/packem_shared/combineTelemetry-DgE9W9G8.mjs +1 -0
  29. package/dist/packem_shared/common-CJSjtsfv.mjs +1 -0
  30. package/dist/packem_shared/compileAgentWorkflow-DVHi7NYD.mjs +1 -0
  31. package/dist/packem_shared/component-shared-G8ngerkU.mjs +1 -0
  32. package/dist/packem_shared/consoleTelemetry-BZY3Y-Ly.mjs +1 -0
  33. package/dist/packem_shared/createAgentContext-BvU9LP43.mjs +1 -0
  34. package/dist/packem_shared/createAgentGenerate-BOILDUZj.mjs +3 -0
  35. package/dist/packem_shared/createDispatchRunner-BxtGxAQi-DXXP7SxZ.mjs +1 -0
  36. package/dist/packem_shared/defineAgent-KgH-gwYg.mjs +3 -0
  37. package/dist/packem_shared/defineSkill-DGGvDWNq.mjs +1 -0
  38. package/dist/packem_shared/fnv1a-BNN96GYb.mjs +1 -0
  39. package/dist/packem_shared/functionTool-D3CP4f8-.mjs +1 -0
  40. package/dist/packem_shared/in-flight-calls-GH1A_1P3.mjs +1 -0
  41. package/dist/packem_shared/normalizeEntityName-k3fjicAG.mjs +2 -0
  42. package/dist/packem_shared/otlpTelemetry-ByqrB-72.mjs +1 -0
  43. package/dist/packem_shared/positive-integer-ztHqqpBk.mjs +1 -0
  44. package/dist/packem_shared/runAgentLoop-Drn1NcCc.mjs +1 -0
  45. package/dist/packem_shared/runVoiceTurn-BtcmyaBo.mjs +1 -0
  46. package/dist/packem_shared/sandboxComponent-BtOkUYpY.mjs +5 -0
  47. package/dist/packem_shared/sentryTelemetry-BHeydiAB.mjs +1 -0
  48. package/dist/packem_shared/tool-output-Cmk3JWMG.mjs +1 -0
  49. package/dist/packem_shared/types.d-B8WZl1rU.d.mts +1347 -0
  50. package/dist/packem_shared/types.d-B8WZl1rU.d.ts +1347 -0
  51. package/dist/packem_shared/voice-turn-BRj_it54.mjs +1 -0
  52. package/dist/reply.d.mts +40 -0
  53. package/dist/reply.d.ts +40 -0
  54. package/dist/reply.mjs +1 -0
  55. package/dist/sandbox.d.mts +130 -110
  56. package/dist/sandbox.d.ts +130 -110
  57. package/dist/sandbox.mjs +1 -109
  58. package/dist/skill-markdown.d.mts +36 -0
  59. package/dist/skill-markdown.d.ts +36 -0
  60. package/dist/skill-markdown.mjs +1 -0
  61. package/dist/telemetry/index.d.mts +319 -96
  62. package/dist/telemetry/index.d.ts +319 -96
  63. package/dist/telemetry/index.mjs +1 -4
  64. package/package.json +18 -9
  65. package/dist/packem_shared/AGENT_MODULE-Dnt_-AAT.mjs +0 -24
  66. package/dist/packem_shared/VoiceSessionDO-DLoXsHGF.mjs +0 -297
  67. package/dist/packem_shared/adaptMcpResult-wtNMvLoP.mjs +0 -65
  68. package/dist/packem_shared/agentAsTool-Dt8NlU6k.mjs +0 -94
  69. package/dist/packem_shared/base64-BVwtgRJV.mjs +0 -18
  70. package/dist/packem_shared/braintrustTelemetry-wuGDErob.mjs +0 -47
  71. package/dist/packem_shared/buildModelMessages-BWFigaoo.mjs +0 -69
  72. package/dist/packem_shared/codeTool-CjgJOC9t.mjs +0 -122
  73. package/dist/packem_shared/collectAgenticMemoryTools-QrzpV-WX.mjs +0 -97
  74. package/dist/packem_shared/combineTelemetry-DCyaaWAI.mjs +0 -43
  75. package/dist/packem_shared/common-DAeFCot5.mjs +0 -61
  76. package/dist/packem_shared/compileAgentWorkflow-BxJjHgtD.mjs +0 -55
  77. package/dist/packem_shared/consoleTelemetry-z2MiP1jt.mjs +0 -93
  78. package/dist/packem_shared/createAgentContext-4xJGXNR4.mjs +0 -50
  79. package/dist/packem_shared/createAgentGenerate-BQv9YJ01.mjs +0 -192
  80. package/dist/packem_shared/createDispatchRunner-DSbp_dph-ZHTtxy3f.mjs +0 -69
  81. package/dist/packem_shared/defineAgent-D6maSbVc.mjs +0 -148
  82. package/dist/packem_shared/defineSkill-Ctf_S-rz.mjs +0 -22
  83. package/dist/packem_shared/functionTool-D6lCa2jB.mjs +0 -20
  84. package/dist/packem_shared/graph-component-aoUwO-f0.mjs +0 -216
  85. package/dist/packem_shared/memory-D4FPcBsX.mjs +0 -12
  86. package/dist/packem_shared/normalizeEntityName-CyEEWFkR.mjs +0 -3
  87. package/dist/packem_shared/runAgentLoop-Dhg4ZNvw.mjs +0 -493
  88. package/dist/packem_shared/runVoiceTurn-LnqLvCRR.mjs +0 -211
  89. package/dist/packem_shared/sandboxComponent-DR3pTwBL.mjs +0 -194
  90. package/dist/packem_shared/sentryTelemetry-CgqFJyLO.mjs +0 -36
  91. package/dist/packem_shared/types.d-BWG0uUtX.d.mts +0 -1015
  92. package/dist/packem_shared/types.d-BWG0uUtX.d.ts +0 -1015
@@ -0,0 +1,1347 @@
1
+ import { InboundEmail } from '@lunora/mail/inbound';
2
+ import { FlexibleSchema, LanguageModel, ModelMessage, ToolSet, ToolChoice, ToolCallRepairFunction, StopCondition, TelemetryOptions } from 'ai';
3
+ /**
4
+ * Structural mirror of the Lunora function reference (`{ __lunoraRef }`).
5
+ * Declared locally so the loop can mint references to the agent runtime
6
+ * functions by path without importing `@lunora/dispatch`.
7
+ * @experimental
8
+ */
9
+ interface AgentFunctionReference {
10
+ __lunoraRef: string;
11
+ }
12
+ /**
13
+ * `ctx.run`-shaped dispatcher the loop uses to call Lunora functions.
14
+ * @experimental
15
+ */
16
+ type AgentRunFunction = (reference: AgentFunctionReference, args?: Record<string, unknown>) => Promise<unknown>;
17
+ /**
18
+ * Structural subset of the Cloudflare Workflows durable-step API the loop needs.
19
+ * @experimental
20
+ */
21
+ interface AgentStepLike {
22
+ do: <T>(name: string, callback: () => Promise<T>) => Promise<T>;
23
+ /**
24
+ * Durably hibernate until an external event of `type` arrives, then return
25
+ * its payload. Used for human-in-the-loop approvals: a run pauses on
26
+ * `approval:<toolCallId>` until a client resolves it. Like `do`, a resolved
27
+ * wait is memoized — a replay returns the recorded decision without pausing
28
+ * again. Signature mirrors `@lunora/workflow`'s `WorkflowStepLike`.
29
+ */
30
+ waitForEvent: <T = unknown>(name: string, options: {
31
+ timeout?: number | string;
32
+ type: string;
33
+ }) => Promise<{
34
+ payload: Readonly<T>;
35
+ type: string;
36
+ }>;
37
+ }
38
+ /**
39
+ * The context handed to an agent tool's `execute`. Beyond the model-provided
40
+ * input, a tool sees who it runs for (`threadKey`), how to call back into the
41
+ * app (`run` — dispatches Lunora queries/mutations/actions), and its
42
+ * `idempotencyKey`.
43
+ *
44
+ * The `idempotencyKey` is the deterministic durable-step name
45
+ * (`tool:<name>:<toolCallId>`, further suffixed per script step for a
46
+ * `codeTool`). A COMPLETED tool step is never re-run on a workflow replay
47
+ * (native step memoization) — but a step that FAILS mid-body is retried
48
+ * at-least-once, so a side-effecting tool (charge a card, send a mail) must
49
+ * dedupe on this key itself. `functionTool` forwards it to the dispatched
50
+ * function as `args.idempotencyKey` (pinned after the model input, so it
51
+ * can't be overridden) — a function that wants to dedupe declares
52
+ * `idempotencyKey: v.optional(v.string())` in its own args and checks it; a
53
+ * function that ignores it is unaffected (an undeclared arg field is dropped,
54
+ * not rejected).
55
+ * @experimental
56
+ */
57
+ interface AgentToolContext {
58
+ /**
59
+ * How many sub-agent delegations deep this run already is — 0 for a run a
60
+ * user started, one more per `agent.asTool` hop. The loop copies it off
61
+ * {@link AgentRunInput.depth}; `agent.asTool` reads it to refuse spawning a
62
+ * child past the delegation-depth bound (see `as-tool.ts`). Absent is 0.
63
+ */
64
+ depth?: number;
65
+ /** The Worker environment bindings. */
66
+ env: Record<string, unknown>;
67
+ /**
68
+ * Read the thread's synced state — dispatches the public owner-gated
69
+ * `agents:agentState` query through {@link AgentToolContext.run} (the same
70
+ * admin-dispatch path the loop reads history with), so it inherits the
71
+ * thread's identity/owner gate. Returns `undefined` before any state was
72
+ * seeded (`defineAgent({ initialState })`) or written.
73
+ *
74
+ * NOT replay-stable: on an at-least-once step retry this reflects whatever a
75
+ * prior attempt already wrote via {@link AgentToolContext.setState} (a real
76
+ * committed write, not rolled back), so a value *derived* from it is not
77
+ * safe to pass straight back to `setState` — see its doc for the
78
+ * read-modify-write hazard and the `idempotencyKey` dedupe fix.
79
+ */
80
+ getState: () => Promise<Record<string, unknown> | undefined>;
81
+ /** Deterministic idempotency key — the tool's durable-step name. */
82
+ idempotencyKey: string;
83
+ /**
84
+ * Verified owner of the thread this run belongs to (the loop copies it off
85
+ * {@link AgentRunInput.owner}); `undefined` on an anonymous/single-tenant
86
+ * thread. `agent.asTool` forwards it to the CHILD run, so a sub-agent thread
87
+ * under an owned parent inherits the same RLS scope instead of being created
88
+ * ownerless — which left it readable by anyone who knew its key.
89
+ */
90
+ owner?: string;
91
+ /**
92
+ * Emit an EPHEMERAL progress event for this tool call on the agent's live
93
+ * channel — the same live-only sink the streamed token deltas ride. NOT
94
+ * persisted and NEVER replayed: it fires only while `execute` runs inside the
95
+ * tool's memoized durable step, so a COMPLETED step is served from the memo on
96
+ * replay without re-emitting. A safe no-op when the runtime wired no live sink
97
+ * (the durable path's default), exactly as `streamingText` stays empty then.
98
+ * Surfaced on the client by `useAgentToolEvents`, correlated to this call by
99
+ * {@link AgentToolContext.toolCallId}. `data` must be JSON-serializable.
100
+ */
101
+ reportProgress: (data: unknown) => void;
102
+ /** Dispatch a Lunora function (the workflow `ctx.run`). */
103
+ run: AgentRunFunction;
104
+ /**
105
+ * Replace the thread's synced state — dispatches the internal
106
+ * `agents:agentSetState` mutation (absolute REPLACE of the whole object) and
107
+ * broadcasts to every `useAgentState` subscriber over the existing reactive
108
+ * channel. Runs inside the tool's memoized durable step, so a COMPLETED step
109
+ * is served from the memo on replay without re-dispatching.
110
+ *
111
+ * REPLAY-SAFETY CONTRACT — the *value* you pass must be REPLAY-STABLE: a
112
+ * constant or derived purely from the replay-stable tool `input` (never
113
+ * `Date.now()`/`Math.random()`). A step that FAILS mid-body is retried
114
+ * at-least-once, and the retry re-runs the whole `execute` against state a
115
+ * prior attempt may already have written (the dispatch is a real committed
116
+ * mutation, not rolled back). Re-applying a replay-stable value is a no-op,
117
+ * so the absolute set converges. A value derived from
118
+ * {@link AgentToolContext.getState} is NOT replay-stable — a naive
119
+ * read-modify-write (`setState({ count: (await getState()).count + 1 })`)
120
+ * DOUBLE-ADVANCES on a retry because the retry re-reads the already-written
121
+ * value. For a read-modify-write, make the tool idempotent on
122
+ * {@link AgentToolContext.idempotencyKey}: record the key in the state and
123
+ * skip the write when it is already present.
124
+ */
125
+ setState: (state: Record<string, unknown>) => Promise<void>;
126
+ /**
127
+ * The durable-step handle (`step.do`/`waitForEvent`). ALWAYS present:
128
+ * `agent-loop.ts` threads it into every tool's context unconditionally, and
129
+ * it is required rather than optional so a missing handle is a compile
130
+ * error instead of a silent durability downgrade. `codeTool` uses it to give
131
+ * each script step its OWN nested durable boundary — see `code-tool.ts` — so
132
+ * a failure at script step 3 retries only step 3, not steps 1–2's
133
+ * already-committed side effects. Cloudflare Workflows supports a `step.do`
134
+ * nested inside another `step.do`'s callback (the codeTool call's own
135
+ * enclosing step). Most tools never touch this directly; a test driving
136
+ * `execute`/`runToolScript` by hand passes a pass-through double.
137
+ */
138
+ step: AgentStepLike;
139
+ /** The thread this tool call belongs to. */
140
+ threadKey: string;
141
+ /** The provider-issued tool-call id. */
142
+ toolCallId: string;
143
+ }
144
+ /**
145
+ * The view of {@link AgentToolContext} handed to a `needsApproval` gate
146
+ * function — every field except `setState`, `step`, and `reportProgress`.
147
+ *
148
+ * `setState` is dropped because a gate that mutates thread state is a side
149
+ * effect inside a decision predicate, and `reportProgress` because emitting a
150
+ * live event is the same thing in observable form — the decision is what the
151
+ * loop reports, not the deciding. `step` goes because the loop already runs the
152
+ * gate inside a durable step of its own, so a gate has no business opening
153
+ * another.
154
+ *
155
+ * `getState` and `run` stay: reads are legitimate gate inputs (gate on the
156
+ * caller's plan tier, on a spend total), and the gate resolves inside its own
157
+ * durable step, so they are replay-safe there. Note this makes the type a
158
+ * NARROWING, not a proof of purity — `run` takes any
159
+ * {@link AgentFunctionReference}, so a gate can still dispatch a mutation
160
+ * before approval. Nothing in the type system distinguishes a query reference
161
+ * from a mutation one; keeping the gate side-effect-free is the author's.
162
+ * @experimental
163
+ */
164
+ type AgentApprovalContext = Omit<AgentToolContext, "reportProgress" | "setState" | "step">;
165
+ /**
166
+ * An agent tool. Unlike a raw AI SDK tool, `execute` is NOT handed to the
167
+ * model call — the loop runs it itself inside a named durable step so a
168
+ * completed call never re-runs on replay, and passes the
169
+ * {@link AgentToolContext} alongside the input.
170
+ * @experimental
171
+ */
172
+ interface AgentToolDefinition<Input = unknown, Output = unknown> {
173
+ /** What the tool does — shown to the model. */
174
+ description: string;
175
+ /** Run the tool. The returned value is persisted as the tool message content. */
176
+ execute: (input: Input, context: AgentToolContext) => Promise<Output> | Output;
177
+ /** The input schema shown to the model (a zod schema or `jsonSchema(...)`). */
178
+ inputSchema: FlexibleSchema<Input>;
179
+ /** Runtime brand. */
180
+ isLunoraAgentTool: true;
181
+ /**
182
+ * Gate the tool behind a human approval (mirrors the AI SDK's
183
+ * `needsApproval`). When it resolves truthy the durable run PAUSES — the
184
+ * thread moves to `"awaiting_input"` and the workflow hibernates on
185
+ * `approval:<toolCallId>` — until a client calls `agents:agentResolveApproval`.
186
+ * On approve the tool runs exactly as normal; on reject it is skipped and a
187
+ * tool result explaining the rejection is persisted so the next turn recovers.
188
+ * A boolean gates statically; a function gates per input. Default: `false`
189
+ * (unchanged behavior).
190
+ *
191
+ * The boolean/`undefined` forms are compile-time constants re-derived
192
+ * identically on every replay — no durable step. The FUNCTION form runs
193
+ * inside its OWN durable step (`tool:approval-gate:<toolCallId>`, distinct
194
+ * from the tool's own step), so it now runs exactly once per call, not once
195
+ * per replay. It must still be otherwise pure: deterministic given its
196
+ * inputs (no `Date.now()`/`Math.random()`) and free of side effects — the
197
+ * context it receives is {@link AgentApprovalContext}, which has neither
198
+ * `setState` nor `reportProgress`; state writes and progress events belong
199
+ * only in `execute`, inside the tool's own memoized step. It still holds
200
+ * `run`, which the type cannot narrow to reads — see
201
+ * {@link AgentApprovalContext}.
202
+ */
203
+ needsApproval?: ((input: Input, context: AgentApprovalContext) => boolean | Promise<boolean>) | boolean;
204
+ }
205
+ /**
206
+ * Author-supplied tool config (see {@link AgentToolDefinition}).
207
+ * @experimental
208
+ */
209
+ type AgentToolConfig<Input = unknown, Output = unknown> = Omit<AgentToolDefinition<Input, Output>, "isLunoraAgentTool">;
210
+ /**
211
+ * Generic-erased tool for tool maps. `Input` sits in both a covariant
212
+ * (`inputSchema`) and a contravariant (`execute`) position, so no single
213
+ * non-`any` instantiation admits every concrete tool — the same reason the AI
214
+ * SDK's `ToolSet` erases its generics.
215
+ * @experimental
216
+ */
217
+ type AnyAgentTool = AgentToolDefinition<any, any>;
218
+ /**
219
+ * The model an agent runs on: a Workers AI model id (resolved via `env.AI`),
220
+ * a prebuilt AI SDK {@link LanguageModel}, or a thunk building one from the
221
+ * Worker env (for providers that need API keys off `env`).
222
+ * @experimental
223
+ */
224
+ type AgentModelInput = LanguageModel | ((env: Record<string, unknown>) => LanguageModel);
225
+ /**
226
+ * Memory wiring: the path (or reference) of an app **action** taking
227
+ * `{ query: string }` and returning `@lunora/ai/rag`'s `RetrieveResult`
228
+ * (`{ context, chunks, sources }`) — typically three lines over
229
+ * `defineRag(...)(ctx).retrieve`. Dispatching to a real action (instead of
230
+ * embedding vector plumbing here) keeps retrieval inside a fully wired ctx:
231
+ * codegen-resolved vector bindings, RLS, observability.
232
+ *
233
+ * Two retrieval MODES. `"inject"` (default) runs the action as a durable step at
234
+ * turn start and injects `.context` as one system message (one-shot top-k).
235
+ * `"agentic"` skips auto-injection; the source instead mints a `searchMemory`
236
+ * tool the MODEL calls mid-reasoning (Recursive-LM / "read what you need") — each
237
+ * call a memoized durable step, so multi-hop retrieval is crash-safe for free.
238
+ * @experimental
239
+ */
240
+ interface AgentMemoryOptions {
241
+ /**
242
+ * Episodic-tier options — read only when {@link AgentMemoryOptions.kind} is
243
+ * `"episodic"`. `recall` bounds how many recent episodes are injected per run
244
+ * (default 5, capped at 20); `extractionModel` overrides the (optionally
245
+ * cheaper) model that summarizes each run into an episode (defaults to the
246
+ * agent's own model).
247
+ */
248
+ episodic?: {
249
+ /** Model for the run-end episode-summary step (defaults to the agent's model). */
250
+ extractionModel?: AgentModelInput;
251
+ /** Max recent episodes recalled per run (default 5, max 20). */
252
+ recall?: number;
253
+ };
254
+ /**
255
+ * Graph-tier bounds and extraction model — read only when
256
+ * {@link AgentMemoryOptions.kind} is `"graph"`. `depth`/`maxSeeds`/`fanOut`/
257
+ * `maxNodes` bound the run-time BFS traversal; `extractionModel` overrides the
258
+ * (optionally cheaper) model used for the run-end entity/relation extraction
259
+ * step (defaults to the agent's own model).
260
+ */
261
+ graph?: {
262
+ /** Max BFS hops from a seed entity (default 2). */
263
+ depth?: number;
264
+ /** Model for the run-end extraction step (defaults to the agent's model). */
265
+ extractionModel?: AgentModelInput;
266
+ /** Max edges expanded per visited node (default 8). */
267
+ fanOut?: number;
268
+ /** Max entities visited across the whole traversal (default 32). */
269
+ maxNodes?: number;
270
+ /** Max seed entities matched from the query (default 4). */
271
+ maxSeeds?: number;
272
+ };
273
+ /**
274
+ * `"semantic"` (default) is vector RAG over {@link AgentMemoryOptions.source}.
275
+ * `"graph"` traverses the owner-scoped entity/relation graph (auto-extracted
276
+ * on write, keyed by the thread's `owner`) via the built-in traverse function
277
+ * and ignores `source`. `"episodic"` recalls a recency-ordered timeline of the
278
+ * owner's past runs (each summarized at run end); it also ignores `source`.
279
+ * See {@link AgentMemoryOptions}.
280
+ */
281
+ kind?: "episodic" | "graph" | "semantic";
282
+ /**
283
+ * `"inject"` (default) auto-injects one top-k context system message per
284
+ * run; `"agentic"` skips injection and mints a `searchMemory` tool the model
285
+ * drives itself. Applies to `"semantic"` kind only. See
286
+ * {@link AgentMemoryOptions}.
287
+ */
288
+ mode?: "agentic" | "inject";
289
+ /**
290
+ * Agentic-only. An optional fetch-by-id **action** `{ id: string } -> string`
291
+ * that mints a companion `readMemory` tool so the model can pull a full
292
+ * document after `searchMemory` surfaces its id (typically a ~3-line action
293
+ * mapping `${sourceId}#${n}` → text). Ignored in `"inject"` mode.
294
+ */
295
+ read?: AgentFunctionReference | string;
296
+ /**
297
+ * Agentic-only. Per-result snippet truncation (chars) applied to each
298
+ * `searchMemory` hit for token economy. Default 240. Ignored in `"inject"`
299
+ * mode.
300
+ */
301
+ snippetChars?: number;
302
+ /**
303
+ * The memory action: a function path (`"rag:searchDocs"`) or reference.
304
+ * Required for `"semantic"` kind (enforced at `defineAgent`); ignored — and
305
+ * therefore optional — for `"graph"` kind, which dispatches the built-in
306
+ * traverse function instead.
307
+ */
308
+ source?: AgentFunctionReference | string;
309
+ /** Retrieval depth forwarded to the action as `topK`. */
310
+ topK?: number;
311
+ }
312
+ /**
313
+ * One keyed memory-retrieval source the loop dispatches per run. `defineAgent`
314
+ * folds them onto {@link AgentDefinition.memorySources}: the default source
315
+ * (`key: "default"`) from {@link AgentConfig.memory}, then one per skill that
316
+ * carries `knowledge` (keyed by the skill's name). The key names the durable
317
+ * step — the default source keeps the historic `"memory:retrieve"`, a skill
318
+ * source uses `"memory:retrieve:<key>"` — so replay stays deterministic.
319
+ * @experimental
320
+ */
321
+ interface AgentMemorySource extends AgentMemoryOptions {
322
+ /** Stable source key: `"default"` for `memory`, else the contributing skill's name. */
323
+ key: string;
324
+ }
325
+ /**
326
+ * Cumulative or per-turn token usage — AI SDK `LanguageModelUsage` field names.
327
+ * @experimental
328
+ */
329
+ interface AgentUsage {
330
+ /** Prompt (input) tokens. */
331
+ inputTokens?: number;
332
+ /** Completion (output) tokens. */
333
+ outputTokens?: number;
334
+ /** Input + output tokens. */
335
+ totalTokens?: number;
336
+ }
337
+ /**
338
+ * Context handed to a dynamic {@link AgentConfig.instructions} function.
339
+ * @experimental
340
+ */
341
+ interface AgentInstructionsContext {
342
+ /** The Worker environment bindings. */
343
+ env: Record<string, unknown>;
344
+ /** The user message that started this run. */
345
+ input: string;
346
+ /** The thread this run belongs to. */
347
+ threadKey: string;
348
+ }
349
+ /**
350
+ * Author-supplied config for {@link SkillDefinition} — a reusable bundle of
351
+ * expertise (an instruction fragment, tools, and retrieval knowledge) an agent
352
+ * composes in via `defineAgent({ skills: [...] })`. Reuse-first: `tools` carry
353
+ * the SAME {@link AnyAgentTool} shape agents already use
354
+ * (`functionTool`/`mcpTools`/`agentAsTool`), and `knowledge` reuses the
355
+ * {@link AgentMemoryOptions} retrieval verbatim.
356
+ * @experimental
357
+ */
358
+ interface SkillConfig {
359
+ /**
360
+ * An instruction fragment merged into the agent's system prompt — a static
361
+ * string or a thunk over the run context (same shape as
362
+ * {@link AgentConfig.instructions}). Fragments compose in order: the agent's
363
+ * own instructions first, then each skill's in `skills` array order.
364
+ */
365
+ instructions?: string | ((context: AgentInstructionsContext) => string);
366
+ /**
367
+ * Retrieval-augmented knowledge for this skill — see
368
+ * {@link AgentMemoryOptions}. Retrieved as its own durable step at run start
369
+ * (keyed by the skill `name`) and injected alongside the agent's `memory`.
370
+ */
371
+ knowledge?: AgentMemoryOptions;
372
+ /**
373
+ * The skill's identifier — namespaces this skill's `knowledge` memory source
374
+ * (the durable step `memory:retrieve:<name>`). Must be identifier-shaped.
375
+ */
376
+ name: string;
377
+ /**
378
+ * Tools this skill contributes, merged into the agent's FLAT tool namespace.
379
+ * A name collision with the agent's own tools (or another skill's) is an
380
+ * error at `defineAgent` — the agent owns the model-facing namespace.
381
+ */
382
+ tools?: Record<string, AnyAgentTool>;
383
+ }
384
+ /**
385
+ * The code-side parts of a markdown-authored skill — everything `SKILL.md`
386
+ * cannot carry. `name` and `instructions` come from the file itself.
387
+ * @experimental
388
+ */
389
+ type SkillMarkdownExtras = Omit<SkillConfig, "instructions" | "name">;
390
+ /**
391
+ * A `defineSkill` result — config plus the brand the agent merge checks.
392
+ * @experimental
393
+ */
394
+ interface SkillDefinition extends SkillConfig {
395
+ /** Runtime brand check (see `isSkillDefinition`). */
396
+ readonly isLunoraSkill: true;
397
+ }
398
+ /**
399
+ * One prior turn, as {@link AgentConfig.prepareStep} and `stopWhen` observe it.
400
+ * @experimental
401
+ */
402
+ interface AgentStepInfo {
403
+ /** The assistant text of the turn. */
404
+ text: string;
405
+ /** The tool calls the turn issued (AI SDK `StepResult.toolCalls` shape). */
406
+ toolCalls: ReadonlyArray<{
407
+ input: unknown;
408
+ toolCallId: string;
409
+ toolName: string;
410
+ }>;
411
+ /** The turn's token usage, when the model reported it. */
412
+ usage?: AgentUsage;
413
+ }
414
+ /**
415
+ * The turn summary handed to {@link AgentConfig.onStepFinish}.
416
+ * @experimental
417
+ */
418
+ interface AgentStepFinishInfo {
419
+ /** The assistant text produced this turn. */
420
+ text: string;
421
+ /** The tool calls issued this turn (empty on the final answer). */
422
+ toolCalls: ReadonlyArray<AgentToolCall>;
423
+ /** The zero-based turn index. */
424
+ turn: number;
425
+ /** The turn's token usage, when the model reported it. */
426
+ usage?: AgentUsage;
427
+ }
428
+ /**
429
+ * Called after each LLM turn with that turn's text, tool calls, and usage. Runs
430
+ * inside a named durable step (`agent:step-finish:<turn>`) so it fires exactly
431
+ * once per turn even across a workflow replay.
432
+ * @experimental
433
+ */
434
+ type AgentOnStepFinish = (info: AgentStepFinishInfo) => Promise<void> | void;
435
+ /**
436
+ * What {@link AgentConfig.onReply} is called with when a triggered run reaches
437
+ * its final answer.
438
+ * @experimental
439
+ */
440
+ interface AgentReplyInfo {
441
+ /** The worker `env` — where an outbound credential (a bot token, a mailer binding) lives. */
442
+ env: Record<string, unknown>;
443
+ /** Where to reply, captured by the mapper that started this run. */
444
+ replyRef: AgentReplyRef;
445
+ /** The run's final answer. */
446
+ result: AgentRunResult;
447
+ /** The thread this run belongs to. */
448
+ threadKey: string;
449
+ }
450
+ /**
451
+ * Send a triggered run's answer back where it came from — the outbound half of
452
+ * an `onEmail` / `onInbound` trigger.
453
+ *
454
+ * Called once, automatically, when a run started with a `replyRef` produces a
455
+ * final answer; a run without one (an ordinary in-app `ctx.agents.<name>.run`)
456
+ * never calls it. It runs inside a named durable step, so a transient failure
457
+ * is retried and a workflow replay does not send the answer twice.
458
+ *
459
+ * Delivery is the app's call, because only the app has the credential: email
460
+ * has a home already (`replyToEmail(mailer, ref, body)` from
461
+ * `@lunora/agent/reply`), while Slack/GitHub/Discord bot tokens have no
462
+ * framework-owned store yet — read them from `info.env` and make the provider
463
+ * call.
464
+ * @experimental
465
+ */
466
+ type AgentOnReply = (info: AgentReplyInfo) => Promise<void> | void;
467
+ /**
468
+ * The input {@link AgentConfig.prepareStep} sees before a turn runs.
469
+ * @experimental
470
+ */
471
+ interface AgentPrepareStepInput {
472
+ /** The messages assembled for this turn (instructions + memory + history). */
473
+ messages: ReadonlyArray<ModelMessage>;
474
+ /** The zero-based index of the turn about to run. */
475
+ stepNumber: number;
476
+ /** The turns already completed this run. */
477
+ steps: ReadonlyArray<AgentStepInfo>;
478
+ }
479
+ /**
480
+ * Per-turn overrides {@link AgentConfig.prepareStep} may return. A returned
481
+ * `messages` array **replaces** the assembled history for that turn — the seam
482
+ * where history compaction lives.
483
+ * @experimental
484
+ */
485
+ interface AgentPrepareStepResult {
486
+ /** Restrict the tools exposed to the model this turn (by name). */
487
+ activeTools?: ReadonlyArray<string>;
488
+ /** Replace the assembled messages for this turn (history compaction). */
489
+ messages?: ReadonlyArray<ModelMessage>;
490
+ /** Swap the model for this turn. */
491
+ model?: AgentModelInput;
492
+ /** Prepend a system message for this turn. */
493
+ system?: string;
494
+ /** Override the tool-choice strategy for this turn. */
495
+ toolChoice?: ToolChoice<ToolSet>;
496
+ }
497
+ /**
498
+ * Adjust the next turn before it runs — mirrors AI SDK's `prepareStep`. Invoked
499
+ * inside the turn's durable step so its effect is memoized on replay.
500
+ * @experimental
501
+ */
502
+ type AgentPrepareStep = (input: AgentPrepareStepInput) => AgentPrepareStepResult | Promise<AgentPrepareStepResult | undefined> | undefined;
503
+ /**
504
+ * The subset of {@link AgentRunInput} an {@link AgentEmailMapper} returns to
505
+ * start a run from an inbound email.
506
+ * @experimental
507
+ */
508
+ /**
509
+ * Where a triggered run should send its answer back to — captured by the
510
+ * mapper, from fields the verified inbound payload already carries, and carried
511
+ * through the run so {@link AgentConfig.onReply} can answer in the same place
512
+ * the question was asked.
513
+ *
514
+ * A discriminated union rather than an opaque blob because each channel threads
515
+ * differently: email by RFC 5322 `In-Reply-To`/`References`, Slack by
516
+ * `thread_ts`, GitHub by issue number, Discord by channel (plus the triggering
517
+ * message for a true reply).
518
+ * @experimental
519
+ */
520
+ type AgentReplyRef = {
521
+ channel: "discord";
522
+ channelId: string;
523
+ messageId?: string;
524
+ } | {
525
+ channel: "email";
526
+ from: string;
527
+ messageId: string;
528
+ references?: string;
529
+ to: string[];
530
+ } | {
531
+ channel: "github";
532
+ commentId?: number;
533
+ issueNumber: number;
534
+ owner: string;
535
+ repo: string;
536
+ } | {
537
+ channel: "slack";
538
+ channelId: string;
539
+ threadTs: string;
540
+ };
541
+ interface AgentEmailRun {
542
+ /** The user message that starts (or continues) the thread — the model's prompt. */
543
+ input: string;
544
+ /**
545
+ * Verified owner of the thread (its RLS scope). SECURITY: inbound `from` is
546
+ * spoofable and the run dispatches RLS-bypassed — derive this from a verified
547
+ * signal (a DKIM-checked address, a mapped account), never blindly from
548
+ * `email.from`.
549
+ */
550
+ owner?: string;
551
+ /**
552
+ * Where to send the answer — see {@link AgentReplyRef}. Populate it from the
553
+ * event the mapper already has (`captureEmailReplyRef(email)` does it for
554
+ * email) and the run calls {@link AgentConfig.onReply} with it once the
555
+ * final answer is ready. Omit it and the run simply never replies.
556
+ */
557
+ replyRef?: AgentReplyRef;
558
+ /** The thread key — reuse to continue a conversation (e.g. a ticket id parsed from the subject). */
559
+ threadKey: string;
560
+ /** Optional thread title, set on first creation. */
561
+ title?: string;
562
+ }
563
+ /**
564
+ * Map an inbound email into an agent run, or `null`/`undefined` to DROP it (a
565
+ * failed DKIM/SPF/DMARC check, or a message not addressed to this agent). Wired
566
+ * by codegen onto the worker's top-level `email()` handler when set — see
567
+ * `@lunora/agent/inbound`.
568
+ *
569
+ * SECURITY: the parsed `from`/`subject`/body are attacker-controlled and the run
570
+ * is dispatched with RLS bypassed. Gate on `email.authentication` (DKIM/SPF/DMARC
571
+ * verdicts) here before returning a run, and treat every returned field as
572
+ * untrusted input.
573
+ * @experimental
574
+ */
575
+ type AgentEmailMapper = (email: InboundEmail) => AgentEmailRun | null | Promise<AgentEmailRun | null | undefined> | undefined;
576
+ /** The inbound webhook channels an agent can be triggered from — see `@lunora/agent/channels`. */
577
+ type AgentInboundChannelKind = "discord" | "github" | "slack";
578
+ /** The run-input an inbound-channel mapper returns — the same shape as {@link AgentEmailRun}. */
579
+ type AgentChannelRun = AgentEmailRun;
580
+ /**
581
+ * The verified, parsed webhook event handed to an {@link AgentInboundChannel.map} mapper.
582
+ * @experimental
583
+ */
584
+ interface InboundChannelEvent {
585
+ /** Which channel delivered it. */
586
+ channel: AgentInboundChannelKind;
587
+ /** The raw request headers. */
588
+ headers: Headers;
589
+ /** Parse the body as JSON (throws on malformed JSON). */
590
+ json: () => unknown;
591
+ /** The raw (verified) request body. */
592
+ rawBody: string;
593
+ }
594
+ /**
595
+ * Map a VERIFIED inbound channel event into an agent run, or `null`/`undefined`
596
+ * to DECLINE it. Runs only AFTER the channel signature check passes.
597
+ *
598
+ * SECURITY: the payload is attacker-controlled and the run dispatches RLS
599
+ * bypassed. Derive the run `owner` from the verified channel identity (the
600
+ * workspace/installation the signing secret belongs to), never from an arbitrary
601
+ * payload field, and treat every returned field as untrusted input.
602
+ */
603
+ type AgentChannelMapper = (event: InboundChannelEvent) => AgentChannelRun | null | Promise<AgentChannelRun | null | undefined> | undefined;
604
+ /**
605
+ * Trigger an agent from a verified inbound webhook. Codegen wires the developer's
606
+ * chosen HTTP route to `dispatchAgentChannel(...)` (from `@lunora/agent/channels`),
607
+ * which verifies the channel signature over the raw body before calling `map`.
608
+ */
609
+ interface AgentInboundChannel {
610
+ /** Which channel this agent listens on (selects the signature scheme). */
611
+ channel: AgentInboundChannelKind;
612
+ /** Map a verified event to a run (or `null` to decline). */
613
+ map: AgentChannelMapper;
614
+ /**
615
+ * The verification credential, from `env`: an env-var NAME (string) or an
616
+ * `(env) => value` resolver. Slack → signing secret; GitHub → webhook secret;
617
+ * Discord → the application's Ed25519 public key (hex).
618
+ */
619
+ secret: string | ((env: Record<string, unknown>) => string | undefined);
620
+ }
621
+ /**
622
+ * `AgentConfig` is part of the experimental `@lunora/agent` API and may change without a major version bump.
623
+ * @experimental
624
+ */
625
+ interface AgentConfig {
626
+ /** Restrict the tools the model may call, by name. Default: all tools. */
627
+ activeTools?: ReadonlyArray<string>;
628
+ /**
629
+ * How long a human-in-the-loop tool approval may stay pending before the
630
+ * run stops waiting — a Cloudflare Workflows duration: milliseconds, or a
631
+ * `"<n> <unit>"` string like `"3 days"` (the unit set is the host's, so a
632
+ * typo is a compile error). Default `"3 days"`.
633
+ *
634
+ * On timeout the call is treated as REJECTED (the run records why and
635
+ * continues down the normal rejection path), so a run whose approver never
636
+ * answers ends instead of hibernating forever.
637
+ *
638
+ * CLAMPED to one week. A longer wait would outlive the thread's
639
+ * abandoned-run horizon, letting a new run reclaim the thread while the
640
+ * approval is still pending — which is the exact failure this timeout
641
+ * exists to prevent, so it cannot be configured back into existence.
642
+ */
643
+ approvalTimeout?: `${number} ${"day" | "hour" | "minute" | "month" | "second" | "week" | "year"}${"s" | ""}` | number;
644
+ /**
645
+ * Automatic thread-history compaction. When the persisted history exceeds
646
+ * `maxMessages`, the loop summarizes the older messages (all but the most
647
+ * recent `keepRecent`, default `ceil(maxMessages / 2)`) into one system-message
648
+ * brief and prompts the model with that brief plus the recent tail — keeping
649
+ * the context bounded as a conversation grows. The summary is produced inside
650
+ * the turn's memoized durable step (replay-safe) by `model` (a cheaper model
651
+ * may be set, else the agent's). Absent (the default) disables compaction —
652
+ * the full history is sent every turn, exactly as before. A manual
653
+ * {@link AgentConfig.prepareStep} still runs after and can override further.
654
+ */
655
+ compaction?: {
656
+ /** How many most-recent messages to keep verbatim (default `ceil(maxMessages / 2)`). */
657
+ keepRecent?: number;
658
+ /** Compact once history exceeds this many messages. */
659
+ maxMessages: number;
660
+ /** Model for the summarization step (defaults to the agent's model). */
661
+ model?: AgentModelInput;
662
+ };
663
+ /**
664
+ * Seed the thread's synced state — a static, JSON-serializable object set on
665
+ * the thread row at creation only (first writer wins, like `owner`/`title`),
666
+ * so a `useAgentState` client sees it immediately. Keep it DETERMINISTIC (no
667
+ * `Date.now()`/`Math.random()`); it is written once by the durable bootstrap.
668
+ * Runtime-only config — invisible to codegen (agent-free and agent-ful
669
+ * `_generated/*` output is unchanged by its presence).
670
+ */
671
+ initialState?: Record<string, unknown>;
672
+ /**
673
+ * System prompt prepended to every model call — a static string or a thunk
674
+ * derived from the run context (dynamic instructions).
675
+ */
676
+ instructions?: string | ((context: AgentInstructionsContext) => string);
677
+ /** Cap the tokens generated per turn (AI SDK `maxOutputTokens`). */
678
+ maxOutputTokens?: number;
679
+ /**
680
+ * Cost/step cap: maximum LLM turns per run. Default 8. Composes with
681
+ * {@link AgentConfig.stopWhen} — the loop ends when EITHER triggers.
682
+ */
683
+ maxTurns?: number;
684
+ /** Retrieval-augmented memory — see {@link AgentMemoryOptions}. */
685
+ memory?: AgentMemoryOptions;
686
+ /** The model — see {@link AgentModelInput}. */
687
+ model: AgentModelInput;
688
+ /**
689
+ * Optional override for the deployed workflow name (`wrangler.jsonc`
690
+ * `workflows[].name`). Defaults to `agent-<kebab-cased export name>`. Does
691
+ * NOT change the binding name, which is always derived from the export
692
+ * name (`support` → `AGENT_SUPPORT`).
693
+ */
694
+ name?: string;
695
+ /**
696
+ * Policy when a run starts on a thread that already has a DIFFERENT run in
697
+ * flight (the thread's `status` is `"running"` under another workflow
698
+ * instance) — the guard that stops two runs from interleaving messages on
699
+ * the shared per-thread seq counter:
700
+ *
701
+ * - `"reject"` (default) — fail the new run fast with a `CONFLICT` error.
702
+ * - `"replace"` — terminate the in-flight instance and take the thread over.
703
+ * - `"queue"` — park the new run behind the one in flight (FIFO, up to five deep) and hibernate it until the thread is handed over.
704
+ *
705
+ * Each parked run is a live workflow instance waiting on an event, so the
706
+ * queue's depth cap is a real resource bound: past it, a start is rejected
707
+ * exactly as `"reject"` would. A `"replace"` arriving later supersedes the
708
+ * run in FLIGHT, not the queue — parked runs still take their turn after it.
709
+ *
710
+ * A dispatch with no instance id (the inbound-email / inbound-channel
711
+ * paths) cannot be parked, because nothing later can tell it apart from
712
+ * another such dispatch to wake it; `"queue"` rejects those.
713
+ *
714
+ * A workflow REPLAY re-enters the bootstrap under the SAME instance id and
715
+ * is never a concurrent run (the guard compares the stored instance id).
716
+ */
717
+ onConcurrentRun?: "queue" | "reject" | "replace";
718
+ /**
719
+ * Map an inbound email into an agent run — see {@link AgentEmailMapper}.
720
+ * When set, codegen wires this agent onto the worker's top-level `email()`
721
+ * handler (via `@lunora/agent/inbound`) so a received message starts a
722
+ * durable run. Return `null`/`undefined` to drop the message.
723
+ */
724
+ onEmail?: AgentEmailMapper;
725
+ /**
726
+ * Trigger this agent from a verified inbound webhook (Slack / GitHub /
727
+ * Discord) — see {@link AgentInboundChannel}. Mount `dispatchAgentChannel(...)`
728
+ * (from `@lunora/agent/channels`) on an HTTP route; it verifies the channel
729
+ * signature over the raw body before calling `map`.
730
+ */
731
+ onInbound?: AgentInboundChannel;
732
+ /**
733
+ * Called with the final answer when the run was triggered from a channel
734
+ * that gave it a `replyRef` — see {@link AgentOnReply}. This is how an
735
+ * inbound-triggered agent answers where it was asked.
736
+ */
737
+ onReply?: AgentOnReply;
738
+ /** Called after each LLM turn — see {@link AgentOnStepFinish}. */
739
+ onStepFinish?: AgentOnStepFinish;
740
+ /**
741
+ * Structured final answer: a zod schema or `jsonSchema(...)`. When set the
742
+ * loop runs the model with AI SDK `Output.object({ schema })` and returns
743
+ * the parsed object in {@link AgentRunResult.output}.
744
+ */
745
+ output?: FlexibleSchema<unknown>;
746
+ /** Adjust the next turn before it runs — see {@link AgentPrepareStep}. */
747
+ prepareStep?: AgentPrepareStep;
748
+ /**
749
+ * Opt this agent into being STARTED over the public RPC boundary — i.e. via
750
+ * the auto-registered `agents:agentRun` mutation an HTTP-only client (e.g.
751
+ * the `@lunora/mcp` server) calls. Default `false`: an agent is startable
752
+ * only from server-side app code (`ctx.agents.<name>.run(...)`), so declaring
753
+ * an agent does NOT expose it to arbitrary RPC callers. Fail-closed — the run
754
+ * mutation refuses an agent that has not opted in, regardless of any MCP-side
755
+ * `allowAgents` configuration. A started thread is still owner-scoped to
756
+ * `ctx.auth.userId`. Deploy configuration codegen reads STATICALLY (a boolean
757
+ * literal), so it must be inline in the `defineAgent({ ... })` object literal.
758
+ */
759
+ publicRun?: boolean;
760
+ /**
761
+ * Repair a malformed tool call the model emits (AI SDK
762
+ * `experimental_repairToolCall`): given the failing `{ toolCall, error, tools,
763
+ * inputSchema, messages, system }`, return a corrected tool call or `null` to
764
+ * give up. Runs inside the model turn, so keep it deterministic (it re-runs on
765
+ * a replay). Unset (the default) leaves the loop's existing
766
+ * unknown-tool-name recovery as the only repair path.
767
+ */
768
+ repairToolCall?: ToolCallRepairFunction<ToolSet>;
769
+ /**
770
+ * Reusable {@link SkillDefinition}s to compose in — each contributes an
771
+ * instruction fragment, tools (merged into the flat namespace; collisions
772
+ * throw), and retrieval `knowledge` (its own keyed memory source). Folded at
773
+ * declaration time; the tool namespace and memory sources on the returned
774
+ * {@link AgentDefinition} already reflect the merge.
775
+ */
776
+ skills?: ReadonlyArray<SkillDefinition>;
777
+ /**
778
+ * Extra loop-stop conditions (AI SDK `StopCondition`s). Composes with
779
+ * {@link AgentConfig.maxTurns} — the loop ends when EITHER triggers.
780
+ */
781
+ stopWhen?: ReadonlyArray<StopCondition<ToolSet>> | StopCondition<ToolSet>;
782
+ /** Passed to `generateText` as its stable `telemetry` option. */
783
+ telemetry?: TelemetryOptions;
784
+ /** Sampling temperature forwarded to the model. */
785
+ temperature?: number;
786
+ /** Tool-choice strategy (AI SDK `ToolChoice`). Default: `"auto"`. */
787
+ toolChoice?: ToolChoice<ToolSet>;
788
+ /** The tools the model may call, by name. */
789
+ tools?: Record<string, AnyAgentTool>;
790
+ /**
791
+ * Opt into a real-time voice session — see {@link AgentVoiceConfig}. Its mere
792
+ * presence tells codegen to emit the `VOICE_...` hibernatable-WebSocket DO
793
+ * (`VoiceSessionDO` subclass) and the `api.agents.{name}Voice` client
794
+ * reference; an agent without it is byte-identical to before. Runtime config
795
+ * for the in-DO pipeline (models/voice) rides the same object.
796
+ */
797
+ voice?: AgentVoiceConfig;
798
+ }
799
+ /**
800
+ * Opt an agent into a real-time VOICE session — a dedicated hibernatable-
801
+ * WebSocket Durable Object (`VoiceSessionDO`) that runs a per-turn
802
+ * STT→LLM→TTS pipeline IN-DO and SHARES the agent's existing thread tables
803
+ * (`agent_threads`/`agent_messages`) via the runtime dispatch seam. Presence of
804
+ * this block is what codegen keys on to emit the `VOICE_...` DO class + the
805
+ * `api.agents.{name}Voice` client reference; agents without it are byte-
806
+ * identical.
807
+ *
808
+ * v1 slice: conversational turns only. In-DO voice turns are NOT replay-durable
809
+ * and get NO Workflow tool-loop — tool calls are deferred. All fields are
810
+ * optional; the defaults target Workers AI (`@cf/openai/whisper-large-v3-turbo`
811
+ * for STT, `@cf/deepgram/aura-1` for TTS).
812
+ * @experimental
813
+ */
814
+ interface AgentVoiceConfig {
815
+ /**
816
+ * Server→client audio container. `"mp3"` (default) matches the TTS model's
817
+ * native stream; carried to the client so it decodes the returned frames
818
+ * with the right codec.
819
+ */
820
+ audioFormat?: "mp3" | "wav";
821
+ /**
822
+ * Spoken on connect before the first user turn — a fixed greeting synthesized
823
+ * through the TTS model. Omit for a silent-until-spoken-to session.
824
+ *
825
+ * Synthesized once per THREAD, not once per socket: the greeting's persisted
826
+ * row is keyed per thread, and a reconnect onto a thread that already exists
827
+ * gets the `ready` frame without paying for the same line again.
828
+ */
829
+ greeting?: string;
830
+ /**
831
+ * Cap on how many turns one voice socket may run before it is closed with
832
+ * code `4002`. Every turn is a full LLM generation plus sentence-by-sentence
833
+ * TTS — billed and persisted — on a hibernatable socket that can live for
834
+ * days, and the one-turn-in-flight guard throttles nothing. Defaults to 100;
835
+ * a client that hits the cap reconnects for a fresh budget.
836
+ */
837
+ maxTurns?: number;
838
+ /**
839
+ * TTS voice/speaker id forwarded to the TTS model (e.g. a Deepgram Aura voice
840
+ * like `"aura-asteria-en"`). Model-specific; omitted when unset so the model
841
+ * uses its own default voice.
842
+ */
843
+ speaker?: string;
844
+ /**
845
+ * Speech-to-text model id (a Workers AI id, resolved via `env.AI`). Defaults
846
+ * to `@cf/openai/whisper-large-v3-turbo` — batch per-utterance transcription
847
+ * (the client marks utterance boundaries; continuous STT is deferred).
848
+ */
849
+ stt?: string;
850
+ /**
851
+ * Text-to-speech model id (a Workers AI id, resolved via `env.AI`). Defaults
852
+ * to `@cf/deepgram/aura-1` — streamed MP3 synthesized sentence-by-sentence
853
+ * from the LLM's token stream.
854
+ */
855
+ tts?: string;
856
+ }
857
+ /**
858
+ * The input the parent model provides when delegating to a sub-agent tool.
859
+ * @experimental
860
+ */
861
+ interface AgentSubToolInput {
862
+ /** The task or question to hand to the sub-agent. */
863
+ prompt: string;
864
+ }
865
+ /**
866
+ * Options for {@link AgentDefinition.asTool} (`agent.asTool(...)`).
867
+ * @experimental
868
+ */
869
+ interface AgentAsToolOptions {
870
+ /** What the sub-agent does — shown to the parent's model (it decides from it). */
871
+ description: string;
872
+ /** Cap on child-run status polls before giving up — a positive integer. Default 600 (with the 500 ms default interval, a five-minute budget). */
873
+ maxPolls?: number;
874
+ /**
875
+ * The child agent's export name — selects its `AGENT_<NAME>` Workflow
876
+ * binding (e.g. `"researcher"` → `AGENT_RESEARCHER`). The model-facing tool
877
+ * name is the KEY assigned in the parent's `tools` map, not this.
878
+ */
879
+ name: string;
880
+ /** Delay (ms) between child-run status polls. Default 500. */
881
+ pollIntervalMs?: number;
882
+ /**
883
+ * Test seam replacing the between-poll wait. Production uses a real timer;
884
+ * tests inject an immediate resolve so polling runs without wall-clock delay.
885
+ */
886
+ wait?: (ms: number) => Promise<void>;
887
+ }
888
+ /**
889
+ * A `defineAgent` result — config plus the brand codegen discovers.
890
+ * @experimental
891
+ */
892
+ interface AgentDefinition extends AgentConfig {
893
+ /**
894
+ * Adapt this agent into a tool a PARENT agent can call: the returned tool's
895
+ * `execute` starts a durable child run on the named agent's Workflow binding
896
+ * and returns its final answer. Mirrors `@lunora/ai/rag`'s `asTool()`. The
897
+ * child run is correlated by a replay-stable `threadKey` derived from the
898
+ * parent's `toolCallId`, so a retried step reuses the same sub-run.
899
+ */
900
+ asTool: (options: AgentAsToolOptions) => AgentToolDefinition<AgentSubToolInput, string>;
901
+ /** Runtime brand check (see `isAgentDefinition`). */
902
+ readonly isLunoraAgent: true;
903
+ /**
904
+ * The keyed memory sources the loop dispatches per run — the merge of
905
+ * {@link AgentConfig.memory} (as `key: "default"`) and each skill's
906
+ * `knowledge`. Populated by `defineAgent`; `memory` stays on the config for
907
+ * back-compat and direct authoring. When absent the loop falls back to
908
+ * `memory` alone (preserving the historic `"memory:retrieve"` step).
909
+ */
910
+ memorySources?: ReadonlyArray<AgentMemorySource>;
911
+ }
912
+ /**
913
+ * What `agentEnsureThread` reports back to the loop.
914
+ *
915
+ * A discriminated union rather than a bag of optional booleans: the four
916
+ * outcomes are mutually exclusive, and the data each carries only exists for its
917
+ * own case. `queued` has a position, `replaced` has the instance it took the
918
+ * thread from, and the other two have nothing — encoding that as five
919
+ * independent optional fields made every reader re-derive which combination was
920
+ * legal.
921
+ * @experimental
922
+ */
923
+ type EnsureThreadOutcome = {
924
+ outcome: "continued" | "created";
925
+ } | {
926
+ outcome: "queued";
927
+ position: number;
928
+ } | {
929
+ outcome: "replaced";
930
+ priorInstanceId: string;
931
+ };
932
+ /**
933
+ * Params of one agent run (the compiled workflow's payload).
934
+ * @experimental
935
+ */
936
+ interface AgentRunInput {
937
+ /**
938
+ * Sub-agent delegation depth. A run a user starts omits it (0); each
939
+ * `agent.asTool` hop stamps its child one deeper, and the tool refuses to
940
+ * delegate past the bound — `maxTurns` bounds one level's turns, this bounds
941
+ * the TREE (every hop mints a distinct child `threadKey`, so the per-thread
942
+ * run-queue cap never applies across them).
943
+ */
944
+ depth?: number;
945
+ /** The user message that starts (or continues) the thread. */
946
+ input: string;
947
+ /**
948
+ * Verified identity of the thread owner — pass `ctx.auth.userId` from the
949
+ * mutation/action starting the run. An owned thread's public queries
950
+ * (`agents:agentThread` / `agents:agentMessages`) only answer for this
951
+ * identity; omitting it leaves the thread readable by anyone who knows the
952
+ * key (only appropriate for single-tenant/anonymous apps). Immutable after
953
+ * the first run.
954
+ */
955
+ owner?: string;
956
+ /** Where a triggered run sends its answer — see {@link AgentReplyRef}. */
957
+ replyRef?: AgentReplyRef;
958
+ /** The thread key — reuse to continue a conversation. */
959
+ threadKey: string;
960
+ /** Optional thread title, set on first creation. */
961
+ title?: string;
962
+ }
963
+ /**
964
+ * Output of one agent run (the compiled workflow's return value).
965
+ * @experimental
966
+ */
967
+ interface AgentRunResult {
968
+ /** The parsed structured answer, when {@link AgentConfig.output} is set. */
969
+ output?: unknown;
970
+ /**
971
+ * Why the run ended: a final answer, a `stopWhen` condition, or the
972
+ * `maxTurns` cap.
973
+ */
974
+ stopped: "final" | "maxTurns" | "stopCondition";
975
+ /** The final assistant text (absent when stopped by `maxTurns`). */
976
+ text?: string;
977
+ /** LLM turns consumed. */
978
+ turns: number;
979
+ /** Cumulative token usage across the run's turns, when the model reported it. */
980
+ usage?: AgentUsage;
981
+ }
982
+ /**
983
+ * Function paths of the agent runtime functions (the `agentComponent()`
984
+ * functions the app re-exports from `lunora/agents.ts`, so codegen registers
985
+ * them under the `agents:` namespace).
986
+ * @experimental
987
+ */
988
+ interface AgentFunctionPaths {
989
+ appendMessage: string;
990
+ /**
991
+ * The internal `agents:agentCompleteRun` mutation the loop dispatches at the
992
+ * end of a run: it writes the terminal status AND hands the thread to the
993
+ * next queued run in the same mutation.
994
+ */
995
+ completeRun: string;
996
+ /**
997
+ * The internal `agents:agentDeleteMessage` mutation the loop dispatches to
998
+ * retire the HITL approval marker once the decision has landed.
999
+ */
1000
+ deleteMessage: string;
1001
+ ensureThread: string;
1002
+ /** The internal `agents:agentEpisodeRecall` query the loop dispatches for an episodic-kind read. */
1003
+ episodeRecall: string;
1004
+ /** The internal `agents:agentEpisodeUpsert` mutation the loop dispatches on run-end episode extraction. */
1005
+ episodeUpsert: string;
1006
+ /** The internal `agents:agentGraphTraverse` query the loop dispatches for a graph-kind read. */
1007
+ graphTraverse: string;
1008
+ /** The internal `agents:agentGraphUpsert` mutation the loop dispatches on run-end graph extraction. */
1009
+ graphUpsert: string;
1010
+ listMessages: string;
1011
+ patchThread: string;
1012
+ /**
1013
+ * The public `agents:agentRun` mutation an HTTP-only client (e.g. the
1014
+ * `@lunora/mcp` server) calls to START a durable run — owner-scoped to
1015
+ * `ctx.auth.userId`, deterministic (the caller supplies `threadKey`).
1016
+ */
1017
+ run: string;
1018
+ /** The internal `agents:agentSetState` mutation the loop dispatches for `setState`. */
1019
+ setState: string;
1020
+ /** The public owner-gated `agents:agentState` query (`getState` + `useAgentState`). */
1021
+ state: string;
1022
+ }
1023
+ /**
1024
+ * Approval lifecycle marker on a message: `"awaiting_approval"` on the
1025
+ * placeholder written while a run pauses on a gated tool, then `"approved"` /
1026
+ * `"rejected"` on the tool result once a client resolves it. Absent on ordinary
1027
+ * messages. `"awaiting_approval"` rows are filtered out of the model prompt.
1028
+ * @experimental
1029
+ */
1030
+ type AgentMessageStatus = "approved" | "awaiting_approval" | "rejected";
1031
+ /**
1032
+ * One persisted thread message, as the loop reads it back.
1033
+ * @experimental
1034
+ */
1035
+ interface AgentMessageRow {
1036
+ content: string;
1037
+ role: "assistant" | "system" | "tool" | "user";
1038
+ seq: number;
1039
+ /** Approval lifecycle marker — see {@link AgentMessageStatus}. */
1040
+ status?: AgentMessageStatus;
1041
+ toolCallId?: string;
1042
+ toolCalls?: ReadonlyArray<AgentToolCall>;
1043
+ toolName?: string;
1044
+ }
1045
+ /**
1046
+ * One model-issued tool call.
1047
+ * @experimental
1048
+ */
1049
+ interface AgentToolCall {
1050
+ id: string;
1051
+ input: unknown;
1052
+ /**
1053
+ * Why this call was REJECTED before it could run: the model's arguments
1054
+ * failed the tool's input schema, or did not parse as JSON. Present only on
1055
+ * a rejected call — the loop records it as a recoverable tool result and
1056
+ * never executes the tool, because the `input` above is the raw value the
1057
+ * provider sent, not a validated one.
1058
+ */
1059
+ invalid?: string;
1060
+ name: string;
1061
+ }
1062
+ /**
1063
+ * Normalized result of one LLM turn (the `generate` seam's return value).
1064
+ * @experimental
1065
+ */
1066
+ interface AgentGenerateResult {
1067
+ /** The parsed structured answer, when {@link AgentConfig.output} is set. */
1068
+ output?: unknown;
1069
+ text: string;
1070
+ toolCalls: ReadonlyArray<AgentToolCall>;
1071
+ /** Token usage the model reported for this turn. */
1072
+ usage?: AgentUsage;
1073
+ }
1074
+ /**
1075
+ * Options passed to the {@link AgentGenerate} seam for one LLM turn.
1076
+ * @experimental
1077
+ */
1078
+ interface AgentGenerateOptions {
1079
+ /** Restrict the tools exposed to the model this turn (by name). */
1080
+ activeTools?: ReadonlyArray<string>;
1081
+ /** The assembled conversation for this turn. */
1082
+ messages: ReadonlyArray<unknown>;
1083
+ /** A per-turn model override (from {@link AgentConfig.prepareStep}). */
1084
+ model?: LanguageModel;
1085
+ /**
1086
+ * Abort the in-flight turn (the streaming seam forwards it to `streamText`'s
1087
+ * `abortSignal`). On abort the streaming seam returns the text streamed so
1088
+ * far rather than rejecting, so a barge-in can persist the spoken prefix.
1089
+ * The durable (non-streaming) loop never sets it.
1090
+ */
1091
+ signal?: AbortSignal;
1092
+ /** A per-turn tool-choice override. */
1093
+ toolChoice?: ToolChoice<ToolSet>;
1094
+ }
1095
+ /**
1096
+ * The LLM-turn seam: given the assembled conversation, return the model's
1097
+ * decision. Production wires AI SDK `generateText`; tests inject a script.
1098
+ * @experimental
1099
+ */
1100
+ type AgentGenerate = (options: AgentGenerateOptions) => Promise<AgentGenerateResult>;
1101
+ /**
1102
+ * The entities and relations extracted from one run's exchange, upserted into
1103
+ * the owner-scoped graph. Endpoint names in `relations` reference entities by
1104
+ * name (normalized on write); `confidence` (0..1) seeds an edge's weight.
1105
+ */
1106
+ interface AgentGraphExtraction {
1107
+ entities: ReadonlyArray<{
1108
+ name: string;
1109
+ type?: string;
1110
+ }>;
1111
+ relations: ReadonlyArray<{
1112
+ confidence?: number;
1113
+ dst: string;
1114
+ label: string;
1115
+ src: string;
1116
+ }>;
1117
+ }
1118
+ /**
1119
+ * The run-end graph-extraction seam: given the run's exchange (user input +
1120
+ * final answer) and the model to run it on, return the extracted entities and
1121
+ * relations. Production wires AI SDK `generateObject` over a fixed schema
1122
+ * (`createGraphExtract`); the durable loop calls it inside a memoized
1123
+ * `memory:extract` step so the model never re-runs on replay. Absent (the
1124
+ * default) disables extraction, so an agent with no graph memory — and every
1125
+ * unit test that doesn't opt in — is byte-identical.
1126
+ */
1127
+ type AgentGraphExtract = (input: {
1128
+ /** The run's final assistant answer. */
1129
+ assistantText: string;
1130
+ /** The Worker env, for resolving a Workers AI model id. */
1131
+ env: Record<string, unknown>;
1132
+ /** The extraction model (the source's `extractionModel`, else the agent's). */
1133
+ model: AgentModelInput;
1134
+ /** The user message that started the run. */
1135
+ userInput: string;
1136
+ }) => Promise<AgentGraphExtraction>;
1137
+ /**
1138
+ * The history-compaction seam: given the OLDER model messages to condense (and
1139
+ * the model to run it on), return a summary brief. Production wires AI SDK
1140
+ * `generateText` (`createCompact`); the loop calls it INSIDE the turn's memoized
1141
+ * `llm:turn:N` step so the summarization is replay-safe. Absent (the default)
1142
+ * disables compaction, so an agent with no `compaction` config — and every unit
1143
+ * test that doesn't opt in — is byte-identical.
1144
+ */
1145
+ type AgentCompact = (input: {
1146
+ /** The Worker env, for resolving a Workers AI model id. */
1147
+ env: Record<string, unknown>;
1148
+ /** The older conversation messages to summarize (already assembled). */
1149
+ messages: ModelMessage[];
1150
+ /** The compaction model (the config's `model`, else the agent's). */
1151
+ model: AgentModelInput;
1152
+ }) => Promise<string>;
1153
+ /** The one-line summary an episodic-memory run records for later recency recall. */
1154
+ interface AgentEpisodeExtraction {
1155
+ summary: string;
1156
+ }
1157
+ /**
1158
+ * The run-end episode-extraction seam: given the run's exchange and the model to
1159
+ * run it on, return a one/two-sentence summary. Production wires AI SDK
1160
+ * `generateText` (`createEpisodeExtract`); the durable loop calls it inside a
1161
+ * memoized `memory:episode` step so the model never re-runs on replay. Absent
1162
+ * (the default) disables extraction, so an agent with no episodic memory — and
1163
+ * every unit test that doesn't opt in — is byte-identical.
1164
+ */
1165
+ type AgentEpisodeExtract = (input: {
1166
+ /** The run's final assistant answer. */
1167
+ assistantText: string;
1168
+ /** The Worker env, for resolving a Workers AI model id. */
1169
+ env: Record<string, unknown>;
1170
+ /** The extraction model (the source's `extractionModel`, else the agent's). */
1171
+ model: AgentModelInput;
1172
+ /** The user message that started the run. */
1173
+ userInput: string;
1174
+ }) => Promise<AgentEpisodeExtraction>;
1175
+ /**
1176
+ * A live token delta produced while a turn streams. Ephemeral — deltas are
1177
+ * pushed to the sink as the model generates and are NEVER replayed (the
1178
+ * persisted assistant message is the single source of truth). Keyed by
1179
+ * `threadKey` + the zero-based `turn` so a client can correlate a delta to the
1180
+ * in-flight turn.
1181
+ * @experimental
1182
+ */
1183
+ interface AgentTokenDelta {
1184
+ /**
1185
+ * Discriminates the token arm of {@link AgentLiveEvent}. Optional and left
1186
+ * unset on the wire (a token is the default) so the existing emit site and
1187
+ * its structural test assertions stay byte-identical; narrow the union with
1188
+ * `event.kind === "progress"` to isolate the {@link AgentProgressEvent} arm.
1189
+ */
1190
+ kind?: "token";
1191
+ /** The incremental text chunk the model just produced. */
1192
+ text: string;
1193
+ /** The thread this delta belongs to. */
1194
+ threadKey: string;
1195
+ /** The zero-based index of the turn producing the delta. */
1196
+ turn: number;
1197
+ }
1198
+ /**
1199
+ * A live, ephemeral progress event a tool emits via
1200
+ * {@link AgentToolContext.reportProgress} while its durable step runs. Rides the
1201
+ * SAME live-only sink as {@link AgentTokenDelta} — NEVER persisted, NEVER
1202
+ * replayed. Correlated to the in-flight tool call (and its persisted tool row)
1203
+ * by `toolCallId` rather than a turn index, since a single turn can fan out many
1204
+ * tool calls.
1205
+ * @experimental
1206
+ */
1207
+ interface AgentProgressEvent {
1208
+ /** The arbitrary, JSON-serializable payload the tool reported. */
1209
+ data: unknown;
1210
+ /** Discriminates the progress arm of {@link AgentLiveEvent}. */
1211
+ kind: "progress";
1212
+ /** The thread this event belongs to. */
1213
+ threadKey: string;
1214
+ /** The tool call this progress belongs to (correlates with the persisted tool row). */
1215
+ toolCallId: string;
1216
+ }
1217
+ /**
1218
+ * A single event on the agent's live-only channel: either a streamed token
1219
+ * {@link AgentTokenDelta} (turn-keyed) or a tool {@link AgentProgressEvent}
1220
+ * (`toolCallId`-keyed). Both are ephemeral and never replayed — the persisted
1221
+ * thread messages remain the single source of truth. Discriminate on `kind`
1222
+ * (`"progress"` for the progress arm; token deltas leave it unset).
1223
+ * @experimental
1224
+ */
1225
+ type AgentLiveEvent = AgentProgressEvent | AgentTokenDelta;
1226
+ /**
1227
+ * A live-only sink for the agent's ephemeral channel — streamed token deltas
1228
+ * ({@link AgentTokenDelta}) and tool progress events
1229
+ * ({@link AgentProgressEvent}). The runtime provides it (teeing to the existing
1230
+ * stream transport); tests capture it. Invoked ONLY on the first execution of a
1231
+ * turn's / tool's durable step — a workflow replay serves the memoized result
1232
+ * without re-running the body, so no event is re-emitted.
1233
+ *
1234
+ * At-least-once caveat: if a turn's step *fails mid-stream* (before it commits)
1235
+ * the workflow retries the not-yet-memoized step and re-tees that turn's deltas
1236
+ * from scratch. This is the standard durable-step retry contract, not a replay
1237
+ * of a completed turn. Consumers should therefore reset/dedupe accumulated text
1238
+ * per `threadKey`+`turn` boundary so a step retry cannot visually double-append;
1239
+ * the persisted assistant message remains the single source of truth.
1240
+ * @experimental
1241
+ */
1242
+ type AgentTokenSink = (event: AgentLiveEvent) => void;
1243
+ /**
1244
+ * The streaming LLM-turn seam: like {@link AgentGenerate} but tees each text
1245
+ * delta to `onDelta` as the model produces it, then resolves the SAME
1246
+ * {@link AgentGenerateResult} the non-streaming seam returns — so the value the
1247
+ * durable `llm:turn:N` step memoizes (and persists) is identical whether the
1248
+ * turn streamed or not. Production wires AI SDK `streamText`; tests inject a
1249
+ * script. Deltas are live-only — a workflow replay never re-invokes the seam.
1250
+ * @experimental
1251
+ */
1252
+ type AgentStreamGenerate = (options: AgentGenerateOptions, onDelta: (text: string) => void) => Promise<AgentGenerateResult>;
1253
+ /**
1254
+ * Spec entry codegen emits per agent: `{ binding: "AGENT_SUPPORT", exportName: "support" }`.
1255
+ * @experimental
1256
+ */
1257
+ interface AgentBindingSpec {
1258
+ binding: string;
1259
+ exportName: string;
1260
+ /**
1261
+ * Whether the app author opted this agent into public run-starts (via
1262
+ * `defineAgent({ publicRun: true })`) — codegen emits it only when `true`.
1263
+ * Carried onto the {@link AgentHandle} so the public `agents:agentRun`
1264
+ * mutation gates on it fail-closed: an agent without it can be started only
1265
+ * from server-side app code, never by an external RPC client.
1266
+ */
1267
+ publicRun?: boolean;
1268
+ }
1269
+ /**
1270
+ * The lifecycle status stored on an agent thread: `"running"` while a run is in
1271
+ * flight, `"idle"` after it finishes (or stops on a condition), `"error"` on a
1272
+ * terminal failure, `"cancelled"` when a run was terminated via
1273
+ * {@link AgentHandle.cancel}, and `"awaiting_input"` while the run is paused on
1274
+ * a human-in-the-loop tool approval. Mirrored by the `status` `v.union` in
1275
+ * `component.ts`.
1276
+ * @experimental
1277
+ */
1278
+ type AgentThreadStatus = "awaiting_input" | "cancelled" | "error" | "idle" | "running";
1279
+ /**
1280
+ * Structural subset of a Cloudflare Workflow instance the producer surface needs.
1281
+ * @experimental
1282
+ */
1283
+ interface AgentWorkflowInstanceLike {
1284
+ /** Deliver an external event to the running instance (resumes a `waitForEvent`). */
1285
+ sendEvent: (event: {
1286
+ payload: unknown;
1287
+ type: string;
1288
+ }) => Promise<void>;
1289
+ status: () => Promise<unknown>;
1290
+ terminate: () => Promise<void>;
1291
+ }
1292
+ /**
1293
+ * Structural subset of a Cloudflare Workflow binding the producer surface needs.
1294
+ * @experimental
1295
+ */
1296
+ interface AgentWorkflowBindingLike {
1297
+ create: (options?: {
1298
+ id?: string;
1299
+ params?: unknown;
1300
+ }) => Promise<{
1301
+ id: string;
1302
+ }>;
1303
+ get: (id: string) => Promise<AgentWorkflowInstanceLike>;
1304
+ }
1305
+ /**
1306
+ * A started agent run (a workflow instance).
1307
+ * @experimental
1308
+ */
1309
+ interface AgentRunHandle {
1310
+ /** The workflow instance id. */
1311
+ id: string;
1312
+ }
1313
+ /**
1314
+ * The `ctx.agents.<name>` producer handle.
1315
+ * @experimental
1316
+ */
1317
+ interface AgentHandle {
1318
+ /**
1319
+ * Cancel a run by its workflow instance id: terminate the instance and mark
1320
+ * its thread `"cancelled"`. Safe to call on an already-finished run.
1321
+ */
1322
+ cancel: (id: string) => Promise<void>;
1323
+ /**
1324
+ * Whether this agent may be started over the PUBLIC `agents:agentRun`
1325
+ * mutation (the `defineAgent({ publicRun: true })` opt-in, carried from the
1326
+ * codegen wiring spec). `false` by default — the run mutation refuses it
1327
+ * fail-closed, so declaring an agent never exposes it to arbitrary RPC
1328
+ * callers. Does not affect the server-side `run(...)` path below.
1329
+ */
1330
+ publicRun: boolean;
1331
+ /** Start a durable agent run for a thread. */
1332
+ run: (input: AgentRunInput, options?: {
1333
+ id?: string;
1334
+ }) => Promise<AgentRunHandle>;
1335
+ /**
1336
+ * Deliver an external event to a run by its workflow instance id — the path
1337
+ * `agents:agentResolveApproval` uses to resume a run paused on a
1338
+ * human-in-the-loop tool approval.
1339
+ */
1340
+ sendEvent: (id: string, event: {
1341
+ payload: unknown;
1342
+ type: string;
1343
+ }) => Promise<void>;
1344
+ /** Read a run's workflow status by instance id. */
1345
+ status: (id: string) => Promise<unknown>;
1346
+ }
1347
+ export { AgentVoiceConfig as $, AgentDefinition as A, AgentApprovalContext as B, AgentEmailMapper as C, AgentEmailRun as D, AgentGenerateOptions as E, AgentGenerateResult as F, AgentInstructionsContext as G, AgentLiveEvent as H, InboundChannelEvent as I, AgentMemoryOptions as J, AgentMemorySource as K, AgentMessageStatus as L, AgentOnReply as M, AgentOnStepFinish as N, AgentPrepareStep as O, AgentPrepareStepInput as P, AgentPrepareStepResult as Q, AgentProgressEvent as R, SkillMarkdownExtras as S, AgentReplyInfo as T, AgentRunHandle as U, AgentStepFinishInfo as V, AgentStepInfo as W, AgentThreadStatus as X, AgentTokenDelta as Y, AgentToolCall as Z, AgentUsage as _, AgentReplyRef as a, AgentWorkflowBindingLike as a0, AgentWorkflowInstanceLike as a1, EnsureThreadOutcome as a2, AgentToolContext as b, AgentToolDefinition as c, SkillDefinition as d, AgentCompact as e, AgentEpisodeExtract as f, AgentGraphExtract as g, AgentGenerate as h, AgentTokenSink as i, AgentRunInput as j, AgentFunctionPaths as k, AgentRunFunction as l, AgentStepLike as m, AgentStreamGenerate as n, AgentRunResult as o, AgentMessageRow as p, AgentConfig as q, AnyAgentTool as r, AgentAsToolOptions as s, AgentSubToolInput as t, AgentBindingSpec as u, AgentHandle as v, AgentToolConfig as w, AgentFunctionReference as x, AgentModelInput as y, SkillConfig as z };