@lunora/agent 0.0.0 → 1.0.0-alpha.10

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