@theokit/agents 10.0.0 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +324 -0
  2. package/LICENSE +2 -2
  3. package/README.md +6 -5
  4. package/dist/{agent-compiler-CIPQkehU.d.ts → agent-compiler-tetgj6zR.d.ts} +3 -172
  5. package/dist/{agent-handle-Dgi4ZGbg.d.ts → agent-handle-rEWdERuv.d.ts} +113 -2
  6. package/dist/auth.js +1 -1
  7. package/dist/{bridge-entry-emr2PSXC.d.ts → bridge-entry-DRAzQ7UA.d.ts} +32 -200
  8. package/dist/bridge.d.ts +7 -6
  9. package/dist/bridge.js +3 -3
  10. package/dist/{chunk-CKRM5Q2K.js → chunk-4EHZG6KN.js} +122 -16
  11. package/dist/chunk-4EHZG6KN.js.map +1 -0
  12. package/dist/{chunk-M6HMASZC.js → chunk-7PNUTDBQ.js} +109 -14
  13. package/dist/chunk-7PNUTDBQ.js.map +1 -0
  14. package/dist/{chunk-QXGSF6WX.js → chunk-D2EFYZBV.js} +1 -1
  15. package/dist/{chunk-QXGSF6WX.js.map → chunk-D2EFYZBV.js.map} +1 -1
  16. package/dist/{chunk-4VHCH6IZ.js → chunk-LLIERPF3.js} +14 -1
  17. package/dist/chunk-LLIERPF3.js.map +1 -0
  18. package/dist/{chunk-QJN2LLPF.js → chunk-T5MBTKA2.js} +275 -164
  19. package/dist/chunk-T5MBTKA2.js.map +1 -0
  20. package/dist/client-react.d.ts +24 -2
  21. package/dist/client-react.js +2 -1
  22. package/dist/client-react.js.map +1 -1
  23. package/dist/client.d.ts +99 -5
  24. package/dist/client.js +3 -1
  25. package/dist/config.d.ts +2 -2
  26. package/dist/config.js +2 -2
  27. package/dist/{define-agent-BO5QSjV8.d.ts → define-agent-BnH1MBxs.d.ts} +16 -1
  28. package/dist/{delegation-scoring-CDvtrYKd.d.ts → delegation-scoring-CQtF2Zaf.d.ts} +345 -11
  29. package/dist/hooks.js +2 -2
  30. package/dist/hooks.js.map +1 -1
  31. package/dist/index.d.ts +22 -9
  32. package/dist/index.js +8 -3
  33. package/dist/index.js.map +1 -1
  34. package/dist/mcp-health.d.ts +66 -1
  35. package/dist/mcp-health.js +32 -1
  36. package/dist/mcp-health.js.map +1 -1
  37. package/dist/pty.js.map +1 -1
  38. package/dist/session.d.ts +75 -1
  39. package/dist/session.js +43 -4
  40. package/dist/session.js.map +1 -1
  41. package/dist/testing.d.ts +3 -2
  42. package/dist/testing.js +1 -1
  43. package/dist/tools.d.ts +4 -2
  44. package/dist/tools.js +4 -4
  45. package/dist/tools.js.map +1 -1
  46. package/dist/types-C16Wuh9E.d.ts +173 -0
  47. package/package.json +23 -10
  48. package/dist/chunk-4VHCH6IZ.js.map +0 -1
  49. package/dist/chunk-CKRM5Q2K.js.map +0 -1
  50. package/dist/chunk-M6HMASZC.js.map +0 -1
  51. package/dist/chunk-QJN2LLPF.js.map +0 -1
@@ -2,7 +2,7 @@ import {
2
2
  AgentClient,
3
3
  HttpTransport,
4
4
  isAgentHandle
5
- } from "./chunk-M6HMASZC.js";
5
+ } from "./chunk-7PNUTDBQ.js";
6
6
  import {
7
7
  __name
8
8
  } from "./chunk-Z4QWC7IK.js";
@@ -41,6 +41,7 @@ function useAgent(binding, options = {}) {
41
41
  thread: state.thread,
42
42
  status: state.status,
43
43
  error: state.error,
44
+ pendingApprovals: state.pendingApprovals,
44
45
  send: client.send,
45
46
  abort: client.abort,
46
47
  reset: client.reset,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/use-agent.ts"],"mappings":";;;;;;;;;;AACA,SAASA,SAASC,QAAQC,4BAA4B;AAyE/C,SAASC,SACdC,SACAC,UAA2B,CAAC,GAAC;AAI7B,QAAMC,aAAaC,OAAOF,OAAAA;AAC1BC,aAAWE,UAAUH;AAIrB,MAAII;AACJ,MAAI,OAAOL,YAAY,SAAUK,OAAML;WAC9BM,cAAcN,OAAAA,EAAUK,OAAML,QAAQO;AAE/C,QAAMC,kBAAkBH,OAAOL;AAE/B,QAAMS,SAASC;IACb,MACE,IAAIC;MACFN,QAAQO,SACJ,IAAIC,cAAc;QAChBR;QACAS,SAAS,6BAAMZ,WAAWE,QAAQU,SAAzB;QACTC,OAAOb,WAAWE,QAAQW;MAC5B,CAAA,IACCf;;MAEL,MAAA;AACE,cAAMgB,MAAMd,WAAWE,QAAQa;AAC/B,eAAO,OAAOD,QAAQ,aAAaA,IAAAA,IAAQA;MAC7C;IAAA;;;IAIJ;MAACR;;EAAgB;AAGnB,QAAMU,QAAQC,qBAAqBV,OAAOW,WAAWX,OAAOY,aAAaZ,OAAOY,WAAW;AAE3F,SAAO;IACLC,UAAUJ,MAAMI;IAChBC,QAAQL,MAAMK;IACdC,QAAQN,MAAMM;IACdC,OAAOP,MAAMO;IACbC,MAAMjB,OAAOiB;IACbC,OAAOlB,OAAOkB;IACdC,OAAOnB,OAAOmB;IACdC,SAASpB,OAAOoB;IAChBC,WAAWrB,OAAOqB;EACpB;AACF;AAnDgB/B;","names":["useMemo","useRef","useSyncExternalStore","useAgent","binding","options","optionsRef","useRef","current","api","isAgentHandle","path","bindingIdentity","client","useMemo","AgentClient","undefined","HttpTransport","headers","fetch","ctx","context","state","useSyncExternalStore","subscribe","getSnapshot","messages","thread","status","error","send","abort","reset","approve","reconnect"]}
1
+ {"version":3,"sources":["../src/client/use-agent.ts"],"mappings":";;;;;;;;;;AACA,SAASA,SAASC,QAAQC,4BAA4B;AA+F/C,SAASC,SACdC,SACAC,UAA2B,CAAC,GAAC;AAI7B,QAAMC,aAAaC,OAAOF,OAAAA;AAC1BC,aAAWE,UAAUH;AAIrB,MAAII;AACJ,MAAI,OAAOL,YAAY,SAAUK,OAAML;WAC9BM,cAAcN,OAAAA,EAAUK,OAAML,QAAQO;AAE/C,QAAMC,kBAAkBH,OAAOL;AAE/B,QAAMS,SAASC;IACb,MACE,IAAIC;MACFN,QAAQO,SACJ,IAAIC,cAAc;QAChBR;QACAS,SAAS,6BAAMZ,WAAWE,QAAQU,SAAzB;QACTC,OAAOb,WAAWE,QAAQW;MAC5B,CAAA,IACCf;;MAEL,MAAA;AACE,cAAMgB,MAAMd,WAAWE,QAAQa;AAC/B,eAAO,OAAOD,QAAQ,aAAaA,IAAAA,IAAQA;MAC7C;IAAA;;;IAIJ;MAACR;;EAAgB;AAGnB,QAAMU,QAAQC,qBAAqBV,OAAOW,WAAWX,OAAOY,aAAaZ,OAAOY,WAAW;AAE3F,SAAO;IACLC,UAAUJ,MAAMI;IAChBC,QAAQL,MAAMK;IACdC,QAAQN,MAAMM;IACdC,OAAOP,MAAMO;IACbC,kBAAkBR,MAAMQ;IACxBC,MAAMlB,OAAOkB;IACbC,OAAOnB,OAAOmB;IACdC,OAAOpB,OAAOoB;IACdC,SAASrB,OAAOqB;IAChBC,WAAWtB,OAAOsB;EACpB;AACF;AApDgBhC;","names":["useMemo","useRef","useSyncExternalStore","useAgent","binding","options","optionsRef","useRef","current","api","isAgentHandle","path","bindingIdentity","client","useMemo","AgentClient","undefined","HttpTransport","headers","fetch","ctx","context","state","useSyncExternalStore","subscribe","getSnapshot","messages","thread","status","error","pendingApprovals","send","abort","reset","approve","reconnect"]}
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { b as AgentTransport, A as ApprovalDecision } from './agent-handle-Dgi4ZGbg.js';
2
- export { c as AgentClient, d as AgentClientOptions, e as AgentClientState, a as AgentHandle, f as ApprovalAbortedError, C as ChannelPushSource, g as ChannelTransport, h as ChannelTransportOptions, i as ChannelTurnHandlers, I as InProcessApprovalRequestLike, j as InProcessAwaitApproval, k as InProcessRunInput, l as InProcessRunner, m as InProcessTransport, n as InProcessTransportOptions, R as RequestContext, U as UseAgentStatus, o as agentHandle, p as isAgentHandle } from './agent-handle-Dgi4ZGbg.js';
1
+ import { b as AgentTransport, A as ApprovalDecision } from './agent-handle-rEWdERuv.js';
2
+ export { c as AgentClient, d as AgentClientOptions, e as AgentClientState, a as AgentHandle, f as AgentStreamInterruptedError, g as ApprovalAbortedError, C as ChannelPushSource, h as ChannelTransport, i as ChannelTransportOptions, j as ChannelTurnHandlers, I as InProcessApprovalRequestLike, k as InProcessAwaitApproval, l as InProcessRunInput, m as InProcessRunner, n as InProcessTransport, o as InProcessTransportOptions, P as PendingApproval, R as RequestContext, U as UseAgentStatus, p as agentHandle, q as isAgentHandle } from './agent-handle-rEWdERuv.js';
3
3
  import { WireTransport, WireChunk, WireMessage } from '@theokit/presenter/wire';
4
4
  import '@theokit/sdk/errors';
5
5
 
@@ -16,6 +16,48 @@ interface HttpTransportOptions {
16
16
  headers?: HeadersResolver;
17
17
  /** Override fetch (primarily for tests / non-browser hosts) — static; resolved once at construction. */
18
18
  fetch?: typeof fetch;
19
+ /**
20
+ * Where the server-minted run id lives between requests — the key `reconnectToStream` needs
21
+ * (usetheokit/theokit#387).
22
+ *
23
+ * Defaults to an in-memory cell, which is what this transport always did and what keeps a
24
+ * reconnect working within one page lifetime. That default is also why a RELOAD could not
25
+ * reconnect: the new page builds a fresh transport with an empty cell, so `reconnectToStream`
26
+ * returns `null` before it reaches the network — while the server still holds the run in its
27
+ * cache and would replay it.
28
+ *
29
+ * The medium is the CONSUMER's decision, not this package's. `sessionStorage` matches a run's
30
+ * lifetime better than `localStorage`, and either would be a client library writing to browser
31
+ * storage nobody asked it to write to, with privacy and SSR consequences. So the seam is injected
32
+ * and the package stores nothing it was not handed a place for:
33
+ *
34
+ * ```ts
35
+ * new HttpTransport({
36
+ * api: '/api/agents/support',
37
+ * runIdStore: {
38
+ * get: () => sessionStorage.getItem('theo.runId') ?? undefined,
39
+ * set: (id) => {
40
+ * if (id === undefined) sessionStorage.removeItem('theo.runId')
41
+ * else sessionStorage.setItem('theo.runId', id)
42
+ * },
43
+ * },
44
+ * })
45
+ * ```
46
+ *
47
+ * Deliberately NOT included: reconnecting automatically on load. This makes a cached run
48
+ * REACHABLE; reaching for it is a product decision nobody has asked for.
49
+ */
50
+ runIdStore?: RunIdStore;
51
+ }
52
+ /**
53
+ * Where the reconnect key is kept.
54
+ *
55
+ * Two methods rather than a storage object, so a consumer can back it with `sessionStorage`, a
56
+ * cookie, a router param, or a test double — the transport never learns which.
57
+ */
58
+ interface RunIdStore {
59
+ get: () => string | undefined;
60
+ set: (runId: string | undefined) => void;
19
61
  }
20
62
  /**
21
63
  * M41 (ADR-0050 D3) — `ChatTransport` over the web agent path.
@@ -68,13 +110,55 @@ type UIMessageChunk = WireChunk;
68
110
  * `onMessage` is invoked on every reconstruction step with the latest snapshot of the assistant
69
111
  * message, so a caller (the `useAgent` hook) can render streaming updates.
70
112
  */
71
- declare function consumeUIMessageStream(response: Response, onMessage: (message: UIMessage) => void): Promise<void>;
113
+ declare function consumeUIMessageStream(response: Response, onMessage: (message: UIMessage) => void): Promise<ChunkStreamOutcome>;
72
114
  /**
73
115
  * A UIMessageStream SSE `Response` → `ReadableStream<UIMessageChunk>`. This is precisely what a
74
116
  * `ChatTransport.sendMessages` returns, so `HttpTransport` builds on it directly. A body-less
75
117
  * response yields an empty stream.
76
118
  */
77
119
  declare function responseToChunkStream(response: Response): Promise<ReadableStream<UIMessageChunk>>;
120
+ /**
121
+ * How a chunk stream ENDED — theokit#384.
122
+ *
123
+ * ## Why this exists at all
124
+ *
125
+ * The reader used to return `void`, which left its caller exactly two outcomes to choose between:
126
+ * it threw, or it did not. A stream that simply STOPS — a socket closed mid-run — throws nothing,
127
+ * so `AgentClient` read "did not throw" as "the agent finished" and settled a truncated answer in
128
+ * `status: 'done'` with no error. The information needed to tell the two apart was on the wire and
129
+ * nothing carried it out of the reader.
130
+ *
131
+ * ## What counts as an ending, and what does not
132
+ *
133
+ * The stream's own terminal chunk, `finish`, is the marker — NOT the SSE `[DONE]` sentinel:
134
+ *
135
+ * - `finish` is emitted by `presentUIMessageStream` on EVERY framework path, including the error
136
+ * path, so it is the one terminator that exists on all three transports. `[DONE]` is written
137
+ * only by the durable SSE encoder, so an in-process or channel stream has none to look for.
138
+ * - `[DONE]` is also the WEAKER claim of the two. `durableUiMessageStreamResponse` flushes it from
139
+ * a `finally` even when the source aborted mid-run, so a run cut on the SERVER carries `[DONE]`
140
+ * and no `finish`. Keying on `finish` reports that case too; keying on `[DONE]` would miss it.
141
+ *
142
+ * `finish` also cannot lie in the other direction: nothing can truncate a run after its terminal
143
+ * chunk has been written, so a stream that carried one carried a complete turn even if the socket
144
+ * died a byte later.
145
+ */
146
+ interface ChunkStreamOutcome {
147
+ /**
148
+ * `true` iff the terminal `finish` chunk crossed before the stream ended.
149
+ *
150
+ * `false` means the producer stopped talking mid-run: a dropped connection, a killed server, a
151
+ * proxy timeout. It does NOT mean the run failed — a failure arrives as an `error` chunk, which
152
+ * the reader raises instead of returning.
153
+ */
154
+ readonly terminated: boolean;
155
+ /**
156
+ * How many chunks crossed. Diagnostic only, and it earns its place: it separates "the server
157
+ * accepted the request and then said nothing" (`0`) from "the answer was cut mid-sentence"
158
+ * (`n > 0`), which are different failures with different first suspects.
159
+ */
160
+ readonly chunksReceived: number;
161
+ }
78
162
  /**
79
163
  * Read a `ReadableStream<UIMessageChunk>` into reconstructed assistant messages. Shared by
80
164
  * {@link consumeUIMessageStream} (Response path) and the framework-agnostic `AgentClient` store
@@ -84,8 +168,18 @@ declare function responseToChunkStream(response: Response): Promise<ReadableStre
84
168
  * A provider failure (401/429/5xx) arrives as a `{ type: 'error', errorText }` chunk rather than a
85
169
  * thrown rejection — both the in-process runner and the SSE path emit it as data. The reader
86
170
  * rejects on it, so `AgentClient.#drive`'s existing catch surfaces it (`status='error'`).
171
+ *
172
+ * theokit#384 — it also RETURNS how the stream ended. See {@link ChunkStreamOutcome}.
173
+ *
174
+ * The observation happens in a `TransformStream` rather than inside `readMessageStream`, because a
175
+ * bare `finish` reconstructs to nothing: the reader yields a snapshot for it only when it carries
176
+ * `messageMetadata`, so the terminal chunk is invisible at the message level by design (measured
177
+ * against the ai-sdk oracle — see `read-message-stream.ts`). Watching the CHUNKS is the only place
178
+ * the terminator is observable without changing what the reader emits. The cost is one queue hop
179
+ * per chunk on a path that already spends 3.274 ms per emit deriving the timeline (M86) — real,
180
+ * and three orders of magnitude below the work it sits next to.
87
181
  */
88
- declare function consumeChunkStream(stream: ReadableStream<UIMessageChunk>, onMessage: (message: UIMessage) => void): Promise<void>;
182
+ declare function consumeChunkStream(stream: ReadableStream<UIMessageChunk>, onMessage: (message: UIMessage) => void): Promise<ChunkStreamOutcome>;
89
183
 
90
184
  /**
91
185
  * M41/M42 — extract the turn text from the last user message's text parts. Shared by the transports
@@ -94,4 +188,4 @@ declare function consumeChunkStream(stream: ReadableStream<UIMessageChunk>, onMe
94
188
  */
95
189
  declare function extractLastUserText(messages: readonly WireMessage[]): string;
96
190
 
97
- export { AgentTransport, ApprovalDecision, type HeadersResolver, HttpTransport, type HttpTransportOptions, consumeChunkStream, consumeUIMessageStream, extractLastUserText, responseToChunkStream };
191
+ export { AgentTransport, ApprovalDecision, type ChunkStreamOutcome, type HeadersResolver, HttpTransport, type HttpTransportOptions, consumeChunkStream, consumeUIMessageStream, extractLastUserText, responseToChunkStream };
package/dist/client.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  AgentClient,
3
+ AgentStreamInterruptedError,
3
4
  ApprovalAbortedError,
4
5
  ChannelTransport,
5
6
  HttpTransport,
@@ -10,10 +11,11 @@ import {
10
11
  extractLastUserText,
11
12
  isAgentHandle,
12
13
  responseToChunkStream
13
- } from "./chunk-M6HMASZC.js";
14
+ } from "./chunk-7PNUTDBQ.js";
14
15
  import "./chunk-Z4QWC7IK.js";
15
16
  export {
16
17
  AgentClient,
18
+ AgentStreamInterruptedError,
17
19
  ApprovalAbortedError,
18
20
  ChannelTransport,
19
21
  HttpTransport,
package/dist/config.d.ts CHANGED
@@ -191,7 +191,7 @@ declare class TrustStore {
191
191
  * draft called them synchronously and `trust()` returned before the bytes landed, so an immediate
192
192
  * `read()` saw an empty store. Same shape as the M71 pointer bug, and same cause — the SDK's
193
193
  * `.d.ts` does not declare these, so nothing at compile time says they return a Promise
194
- * (usetheodev/theokit-sdk#280).
194
+ * (usetheokit/theokit-sdk#280).
195
195
  */
196
196
  trust(record: TrustRecord): Promise<void>;
197
197
  /**
@@ -310,7 +310,7 @@ interface LoadInstructionTreeInput {
310
310
  * Which files to load. Defaults to the conventional two.
311
311
  *
312
312
  * A list matches basenames exactly; a predicate answers the question a list cannot. A rules
313
- * DIRECTORY `.claude/rules/`, `.cursor/rules/`, `.theokit/rules/` — holds files the caller
313
+ * DIRECTORY —, `.cursor/rules/`, `.theokit/rules/` — holds files the caller
314
314
  * cannot name in advance, because the user chooses the names. With only a list on offer, the
315
315
  * closest consumer wrote its own 112-line walk (budget, depth ceiling, cycle guard and all) to
316
316
  * ask `entry.endsWith('.md')`. The walk was ours; only the question was theirs.
package/dist/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ensureSecureDir
3
- } from "./chunk-QXGSF6WX.js";
3
+ } from "./chunk-D2EFYZBV.js";
4
4
  import {
5
5
  __name
6
6
  } from "./chunk-Z4QWC7IK.js";
@@ -154,7 +154,7 @@ var TrustStore = class {
154
154
  * draft called them synchronously and `trust()` returned before the bytes landed, so an immediate
155
155
  * `read()` saw an empty store. Same shape as the M71 pointer bug, and same cause — the SDK's
156
156
  * `.d.ts` does not declare these, so nothing at compile time says they return a Promise
157
- * (usetheodev/theokit-sdk#280).
157
+ * (usetheokit/theokit-sdk#280).
158
158
  */
159
159
  async trust(record) {
160
160
  ensureSecureDir(this.file);
@@ -1,6 +1,7 @@
1
1
  import { TrustPosture, SettingSource, CustomTool, MemorySettings } from '@theokit/sdk';
2
2
  import { z } from 'zod';
3
- import { R as ReasoningEffort, G as Guardrail, H as HumanInTheLoopOptions, S as SkillsSelection, M as McpServersMap, C as CompiledAgentOptions } from './agent-compiler-CIPQkehU.js';
3
+ import { G as Guardrail, S as SkillsSelection, C as CompiledAgentOptions } from './agent-compiler-tetgj6zR.js';
4
+ import { R as ReasoningEffort, H as HumanInTheLoopOptions, M as McpServersMap } from './types-C16Wuh9E.js';
4
5
  import { H as HookHandlers } from './hook-handlers-Cw2FsnE5.js';
5
6
  import { TheokitAgentError } from '@theokit/sdk/errors';
6
7
 
@@ -127,6 +128,20 @@ interface DefineAgentConfig<TInput extends z.ZodType = z.ZodType> {
127
128
  system?: string;
128
129
  /** Extended-thinking effort. */
129
130
  reasoningEffort?: ReasoningEffort;
131
+ /**
132
+ * theokit#363 — hard ceiling on the agent's tool-calling turns within ONE run. Reaching it ends
133
+ * the turn instead of letting the model keep calling tools; absent ⇒ the SDK's own ceiling (8).
134
+ *
135
+ * Named `maxIterations`, not `maxSteps`, because the concept already has exactly one name here —
136
+ * `@Agent({ maxIterations })`, `@MainLoop({ maxIterations })`, `AgentRunner.stream({ maxIterations })`,
137
+ * `delegate({ maxIterations })` — and one name in the SDK it lowers to (`SendOptions.maxIterations`).
138
+ * A second name would be the only place needing translation, and the translation is invisible where
139
+ * it costs most: the SDK rejects an invalid value with a message naming `SendOptions.maxIterations`,
140
+ * which an author who typed `.maxSteps()` has no way to connect to what they wrote. Familiarity
141
+ * argues for the ai-sdk spelling, but ai-sdk's own name is `stopWhen: stepCountIs(n)` — borrowing
142
+ * "steps" would buy recognition of a word, not of an API.
143
+ */
144
+ maxIterations?: number;
130
145
  /**
131
146
  * Pre-built tools. Accepts the `@theokit/sdk` `CustomTool` that `defineAgentTool`
132
147
  * (theokit/server) and every `@theokit/sdk-tools` factory return (issue #81) — they are
@@ -1,27 +1,350 @@
1
1
  import { PluginsSettings, ProviderRoutingSettings, AgentDefinition, BudgetTracker, CustomTool } from '@theokit/sdk';
2
2
  import { RetryOptions } from '@theokit/sdk/retry';
3
- import { C as CompiledAgentOptions, a as MainLoopMeta, b as CompiledTool } from './agent-compiler-CIPQkehU.js';
3
+ import { C as CompiledAgentOptions, a as CompiledTool } from './agent-compiler-tetgj6zR.js';
4
4
  import { TheokitAgentError } from '@theokit/sdk/errors';
5
5
  import { z } from 'zod';
6
+ import { b as MainLoopMeta } from './types-C16Wuh9E.js';
7
+ import { WireChunk } from '@theokit/presenter/wire';
6
8
  import { H as HookHandlers } from './hook-handlers-Cw2FsnE5.js';
7
9
 
8
10
  /**
9
- * SSE streaming handler Web Standard Response with ReadableStream.
11
+ * Typed discriminated union for agent SSE stream events.
10
12
  *
11
- * Per ADR D4: SSE is the v1 transport.
12
- * Per EC-2: uses ReadableStream with controller.enqueue() instead of res.write().
13
- * Works natively on Node, Bun, Deno, CF Workers.
13
+ * Every event has a `type` field for discrimination.
14
+ * Clients narrow via `if (event.type === 'text_delta') event.content`.
14
15
  */
16
+ /** Partial text content from the LLM. */
17
+ interface TextDeltaEvent {
18
+ type: 'text_delta';
19
+ content: string;
20
+ }
21
+ /** Agent started a tool call. */
22
+ interface ToolCallEvent {
23
+ type: 'tool_call';
24
+ callId: string;
25
+ toolName: string;
26
+ input: unknown;
27
+ }
28
+ /**
29
+ * Tool-call arguments streaming in incrementally (theokit-sdk#70). Emitted repeatedly as the
30
+ * model generates a tool call's args, BEFORE they are committed. The same `callId` correlates to
31
+ * the later `tool_call` (args committed) and `tool_result`. Consumers opt in to render tool-input
32
+ * progressively; those that don't simply ignore this variant (it never replaces `tool_call`).
33
+ */
34
+ interface PartialToolCallEvent {
35
+ type: 'partial_tool_call';
36
+ callId: string;
37
+ toolName: string;
38
+ input: unknown;
39
+ }
40
+ /** Tool execution completed. */
41
+ interface ToolResultEvent {
42
+ type: 'tool_result';
43
+ callId: string;
44
+ toolName: string;
45
+ output: string;
46
+ durationMs: number;
47
+ isError: boolean;
48
+ }
49
+ /** Extended thinking / reasoning (when model supports it). */
50
+ interface ThinkingEvent {
51
+ type: 'thinking';
52
+ content: string;
53
+ }
54
+ /** Agent loop iteration. */
55
+ interface IterationEvent {
56
+ type: 'iteration';
57
+ step: number;
58
+ totalSteps: number | null;
59
+ }
60
+ /** Human approval required before proceeding. */
61
+ interface ApprovalRequiredEvent {
62
+ type: 'approval_required';
63
+ callId: string;
64
+ toolName: string;
65
+ question: string;
66
+ input?: unknown;
67
+ callbackUrl: string;
68
+ timeoutMs: number;
69
+ /** M20 — JSON-schema descriptor of the custom payload the approver may attach (optional). */
70
+ payloadSchema?: Record<string, unknown>;
71
+ }
72
+ /**
73
+ * The run is blocked awaiting user input or approval — theokit#141.
74
+ *
75
+ * The SDK's low-fidelity pause signal (`SDKRequestMessage`), distinct from {@link
76
+ * ApprovalRequiredEvent}. The latter is the framework's own, produced by `createHitlPlugin`, and
77
+ * carries everything an approval UI needs to be actionable. This one carries only the request id,
78
+ * because that is all the SDK provides — and it matters most exactly where the plugin is absent
79
+ * (the ACP/serving path of theokit#139), which is where a dropped pause reads to the user as a
80
+ * hang with no explanation.
81
+ *
82
+ * A consumer that cannot resolve the request should still SHOW that the run is waiting. Silence is
83
+ * the one response that is always wrong.
84
+ */
85
+ interface InputRequestedEvent {
86
+ type: 'input_requested';
87
+ requestId: string;
88
+ }
89
+ /** Task-level milestone or summary — theokit#141. Both fields are optional at the source. */
90
+ interface TaskProgressEvent {
91
+ type: 'task_progress';
92
+ status?: string;
93
+ text?: string;
94
+ }
95
+ /**
96
+ * Live output from a shell command the SDK is running — theokit#141.
97
+ *
98
+ * `event` is passed through opaquely because the SDK types it as `Record<string, unknown>`. The
99
+ * layer deliberately does not interpret or render it: guessing a shape here would be a second,
100
+ * weaker oracle over a payload whose real contract lives upstream.
101
+ */
102
+ interface ShellOutputEvent {
103
+ type: 'shell_output';
104
+ event: Record<string, unknown>;
105
+ }
106
+ /** Agent encountered an error. */
107
+ interface ErrorEvent {
108
+ type: 'error';
109
+ code: string;
110
+ message: string;
111
+ retryable: boolean;
112
+ }
113
+ /**
114
+ * Why a run stopped short of finishing on its own — theokit#379.
115
+ *
116
+ * ## Why it is an enum and not a `truncated: boolean`
117
+ *
118
+ * The two members demand OPPOSITE reactions from the caller, and a boolean cannot carry the
119
+ * difference:
120
+ *
121
+ * - `'step_limit'` — the loop ran out of tool-calling turns while the model still wanted more.
122
+ * The work is unfinished but progressing; re-sending is the sane continuation.
123
+ * - `'no_progress'` — the doom-loop guard stopped the model repeating IDENTICAL tool calls. The
124
+ * SDK's own wording: "a controlled stop, NOT a truncation to re-send". Re-sending repeats the
125
+ * loop that was just cut.
126
+ *
127
+ * A caller that reads `truncated: true` and re-sends does the right thing in the first case and
128
+ * feeds the doom loop in the second.
129
+ *
130
+ * ## Why these spellings
131
+ *
132
+ * Both sides of the boundary had already agreed on them before this field existed: the framework's
133
+ * own `LoopFinishReason` (`../loop/loop-strategy.ts`) spells the outer-loop terminals `step_limit`
134
+ * and `no_progress`, and so does the SDK's continuation driver
135
+ * (`RunToCompletionResult.terminal: "done" | "step_limit" | "no_progress"`). Inventing a third
136
+ * vocabulary for the same two outcomes was the only way to get this wrong.
137
+ *
138
+ * ## Why there is no `'finished'` member
139
+ *
140
+ * A clean run carries NO `stopReason` at all — absence is the finished case. Stamping every
141
+ * ordinary turn with a new field would change what every existing consumer receives in order to
142
+ * describe the one case that was already correct. A consumer that has never heard of `stopReason`
143
+ * therefore keeps receiving byte-identical frames for every run that finishes on its own, and for a
144
+ * truncated one it receives one extra optional key it ignores — the same degradation the ai-sdk
145
+ * `finish` chunk already allows, since its `messageMetadata` is `unknown`.
146
+ */
147
+ type AgentStopReason = 'step_limit' | 'no_progress';
148
+ /** Agent completed with a final result. */
149
+ interface DoneEvent {
150
+ type: 'done';
151
+ result: string;
152
+ usage: {
153
+ inputTokens: number;
154
+ outputTokens: number;
155
+ totalTokens: number;
156
+ /** V4-O: SDK reasoning/cache token buckets (0 when the provider omits them). */
157
+ reasoningTokens?: number;
158
+ cacheReadTokens?: number;
159
+ cacheWriteTokens?: number;
160
+ };
161
+ durationMs: number;
162
+ /** Total cost in USD for this agent run (EC-2: added for budget tracking). */
163
+ cost?: number;
164
+ /**
165
+ * theokit#379 — why the run stopped, when it did NOT stop because the agent was done.
166
+ *
167
+ * ABSENT on a clean finish. Present only when the SDK reports that the run was cut: the terminal
168
+ * frame is a `done` either way (a reached ceiling is not an error — see `sdk-adapter.ts`'s
169
+ * `applyStepCeiling`), so without this field a cut run and a finished one are the same event.
170
+ *
171
+ * Read from `RunResult.stoppedByDoomLoop` / `RunResult.stoppedAtIterationLimit` at the adapter
172
+ * boundary. See {@link AgentStopReason} for why the caller needs to tell the two apart.
173
+ */
174
+ stopReason?: AgentStopReason;
175
+ /**
176
+ * The model this turn actually ran on — the EFFECTIVE id, not the declared one.
177
+ *
178
+ * It is resolved at the one place that knows: `createSdkAgentStream`, where
179
+ * `overrides.model ?? compiled.model ?? 'openai/gpt-4o-mini'` is decided. A consumer reading
180
+ * `CompiledAgentOptions.model` instead would be wrong twice — it misses a per-run override, and
181
+ * it reads `undefined` for an agent that declared no model and still ran one.
182
+ *
183
+ * Tokens are on this event already, and tokens alone convert to no cost: price is per model.
184
+ * This is the other half of that question, and the reason it travels rather than staying inside
185
+ * the adapter (usetheokit/theokit#368's fifth criterion).
186
+ *
187
+ * Optional so a producer that predates it — or one building a `done` from a source with no model
188
+ * to report — degrades to absence rather than to a fabricated id.
189
+ */
190
+ model?: string;
191
+ }
192
+ /**
193
+ * Per-turn usage the translator attaches to the ai-sdk `finish` chunk's `messageMetadata`, so it
194
+ * lands on the reconstructed assistant `UIMessage.metadata` on the client (via `readUIMessageStream`)
195
+ * with NO extra header/store wiring. It is the seam that lets a surface (a TUI status bar, a web
196
+ * cost meter) show real tokens/cost for the turn it just streamed. Mirrors `DoneEvent`'s usage/cost
197
+ * (the authoritative totals the SDK reports at turn end) plus the wall-clock `durationMs`.
198
+ */
199
+ interface AgentTurnMetadata {
200
+ usage: DoneEvent['usage'];
201
+ /** Total cost in USD for the turn (present iff the SDK reported it). */
202
+ cost?: number;
203
+ durationMs: number;
204
+ /**
205
+ * theokit#379 — mirrors {@link DoneEvent.stopReason} so a surface that only ever sees the wire
206
+ * (a web client reading `UIMessage.metadata`, the observability translator reading the `finish`
207
+ * chunk) can tell a cut turn from a finished one without a second transport. Absent on a clean
208
+ * finish, exactly as on `DoneEvent`.
209
+ */
210
+ stopReason?: AgentStopReason;
211
+ /**
212
+ * Mirrors {@link DoneEvent.model} — the model the turn ran on, carried to whoever only ever sees
213
+ * the wire. The observability translator is the caller that needs it: it reads the `finish`
214
+ * chunk and records the model beside the token counts, which is what makes a cost answerable
215
+ * from a trace at all. Absent when the producer reported none.
216
+ */
217
+ model?: string;
218
+ }
219
+ /** Agent run started. */
220
+ interface RunStartedEvent {
221
+ type: 'run_started';
222
+ runId: string;
223
+ agentName: string;
224
+ model?: string;
225
+ }
226
+ /** Artifact generation started (code, document, diagram). */
227
+ interface ArtifactStartEvent {
228
+ type: 'artifact_start';
229
+ artifactId: string;
230
+ mimeType: string;
231
+ filename?: string;
232
+ metadata?: Record<string, unknown>;
233
+ }
234
+ /** Artifact content chunk (streamable artifacts). */
235
+ interface ArtifactChunkEvent {
236
+ type: 'artifact_chunk';
237
+ artifactId: string;
238
+ chunk: string;
239
+ isLast: boolean;
240
+ }
241
+ /** Real-time state update from @Observable channels. */
242
+ interface StateUpdateEvent {
243
+ type: 'state_update';
244
+ channel: string;
245
+ data: unknown;
246
+ }
247
+ /** Checkpoint saved (resumable agents). */
248
+ interface CheckpointSavedEvent {
249
+ type: 'checkpoint_saved';
250
+ checkpointId: string;
251
+ step: number;
252
+ resumeToken: string;
253
+ }
254
+ /** File edit produced by a code assistant tool. */
255
+ interface FileEditEvent {
256
+ type: 'file_edit';
257
+ file: string;
258
+ format: 'search-replace' | 'unified-diff' | 'full-file' | 'line-range';
259
+ search?: string;
260
+ replace?: string;
261
+ content?: string;
262
+ diff?: string;
263
+ startLine?: number;
264
+ endLine?: number;
265
+ }
266
+ /** Discriminated union of all agent stream events. */
267
+ type AgentStreamEvent = RunStartedEvent | TextDeltaEvent | ToolCallEvent | PartialToolCallEvent | ToolResultEvent | ThinkingEvent | IterationEvent | ApprovalRequiredEvent | InputRequestedEvent | TaskProgressEvent | ShellOutputEvent | ArtifactStartEvent | ArtifactChunkEvent | StateUpdateEvent | CheckpointSavedEvent | FileEditEvent | ErrorEvent | DoneEvent;
268
+ /** Type guard helpers. */
269
+ declare function isTextDelta(e: AgentStreamEvent): e is TextDeltaEvent;
270
+ declare function isToolCall(e: AgentStreamEvent): e is ToolCallEvent;
271
+ declare function isPartialToolCall(e: AgentStreamEvent): e is PartialToolCallEvent;
272
+ declare function isToolResult(e: AgentStreamEvent): e is ToolResultEvent;
273
+ declare function isDone(e: AgentStreamEvent): e is DoneEvent;
274
+ declare function isError(e: AgentStreamEvent): e is ErrorEvent;
275
+ declare function isApprovalRequired(e: AgentStreamEvent): e is ApprovalRequiredEvent;
276
+
277
+ /**
278
+ * The failure, as chunks the protocol accepts — theokit#161 (B).
279
+ *
280
+ * ## The measured defect
281
+ *
282
+ * M95 put the `code` INSIDE the error chunk (`{type:'error', errorText, errorCode}`), for a good
283
+ * reason: without it a consumer that must DISTINGUISH the failure has only the message, and matching
284
+ * on error text is the heuristic this ecosystem already paid for once — M93 classified failures as
285
+ * transient by regex over the message and read `ECONNREFUSED ...:443` as definitive because the PORT
286
+ * matched its "4xx" pattern.
287
+ *
288
+ * But the `error` variant of ai's `uiMessageChunkSchema` is STRICT: any extra key invalidates it.
289
+ * Measured against `ai@7.0.14` — `{type:'error',errorText:'boom'}` validates; the same chunk with
290
+ * `errorCode` does NOT. So since M95 this path emitted a chunk outside the protocol it claims to
291
+ * speak, and a consumer that validates would reject the whole frame — losing the text along with it.
292
+ *
293
+ * Nobody saw it because the test written to catch exactly this stopped BEFORE validating: its
294
+ * precondition (`toContainEqual({type:'error',errorText:'boom'})`) went stale when `errorCode`
295
+ * appeared, `expect` threw, and the validation loop never ran.
296
+ *
297
+ * ## Why a data part, and not one of the easy exits
298
+ *
299
+ * Dropping the code returns the consumer to the text matching M95 removed. Embedding it in
300
+ * `errorText` is the same thing under another name. The protocol already has the right place — a
301
+ * data part — and this file already uses one for `data-checkpoint`. Measured: it validates.
302
+ *
303
+ * `transient: true` because an error code is turn diagnostics, not message content: the SDK does not
304
+ * persist it in history, which is exactly what we want.
305
+ *
306
+ * The data part comes BEFORE the error chunk deliberately: a sequential consumer already holds the
307
+ * code when the failure arrives. In the other order it would have to handle the error first and only
308
+ * then learn which one it was.
309
+ */
310
+ /**
311
+ * What the browser is told a failure was.
312
+ *
313
+ * Masked by default (usetheokit/theokit#390): the server's raw text — a driver's message, an HTTP
314
+ * client's, a filesystem call's — reached the client verbatim, and `ai@7` on the same protocol
315
+ * masks by default for the reason its own comment gives, "prevent leaking server error details to
316
+ * the client by default".
317
+ *
318
+ * The full text is NOT lost: it reaches the server's logs and the `agent.run` span, and it reaches
319
+ * this hook. What stops is it reaching the browser unless a host decides otherwise.
320
+ */
321
+ type MaskError = (error: {
322
+ message: string;
323
+ code?: string;
324
+ }) => string;
325
+ declare function presentUIMessageStream(events: AsyncIterable<AgentStreamEvent>, opts: {
326
+ textId: string;
327
+ onError?: MaskError;
328
+ }): AsyncGenerator<WireChunk, void, unknown>;
329
+
15
330
  /** Minimal event shape matching SDK's SDKMessage discriminated union. */
16
331
  interface StreamEvent {
17
332
  type: string;
18
333
  [key: string]: unknown;
19
334
  }
20
335
  /**
21
- * Create a Web Standard Response that streams SSE events.
22
- * Each event becomes: `event: {type}\ndata: {json}\n\n`
336
+ * Create a Web Standard Response that streams the agent's turn as UIMessage wire chunks.
337
+ *
338
+ * The `event:` line carries the chunk's own type. It is informational — `parseWireStream` reads
339
+ * only `data:` lines, per WHATWG SSE — and kept because an `EventSource` consumer can dispatch on
340
+ * it.
341
+ *
342
+ * @param eventStream - the framework's own run events
343
+ * @param opts.onError - what a consumer is told a failure was; masked by default (#390)
23
344
  */
24
- declare function streamAgentResponse(eventStream: AsyncIterable<StreamEvent>): Response;
345
+ declare function streamAgentResponse(eventStream: AsyncIterable<StreamEvent>, opts?: {
346
+ onError?: Parameters<typeof presentUIMessageStream>[1]['onError'];
347
+ }): Response;
25
348
 
26
349
  /**
27
350
  * LoopStrategy — the per-round terminal-decision contract that gives runtime to
@@ -160,6 +483,10 @@ declare class DelegationBudgetExceededError extends TheokitAgentError {
160
483
  * @deprecated Use {@link DelegationBudgetExceededError}. The alias is kept for one major so anyone
161
484
  * catching by the old name is not broken; it is the **same** class, not a copy — `instanceof` still
162
485
  * holds in both directions, and a referential-identity test (`toBe`) pins that.
486
+ *
487
+ * knip reports the value/type pair as a duplicate export, which is exactly what a deprecation
488
+ * alias is; `rules.duplicates` is "warn" for that reason. Removing it is the breaking change it
489
+ * exists to avoid.
163
490
  */
164
491
  declare const BudgetExceededError: typeof DelegationBudgetExceededError;
165
492
  /** @deprecated Use {@link DelegationBudgetExceededError}. */
@@ -316,8 +643,15 @@ interface DelegateOptions {
316
643
  * this option exists to remove.
317
644
  */
318
645
  parentHooks?: HookHandlers;
319
- /** LLM API key (inherited from parent). */
320
- apiKey?: string;
646
+ /**
647
+ * LLM API key (inherited from parent).
648
+ *
649
+ * `null` says the provider takes NO credential — a model on the developer's own machine
650
+ * (usetheokit/theokit#423). It is a distinct value from `''` on purpose: an empty string is what
651
+ * an unset environment variable produces, and treating the two alike is how a typo becomes an
652
+ * unauthenticated run. `undefined` still means the caller supplied nothing and is still refused.
653
+ */
654
+ apiKey?: string | null;
321
655
  /** Session ID override (default: crypto.randomUUID for isolation). */
322
656
  sessionId?: string;
323
657
  /** Cancellation — aborts stop the reflective loop from re-entering. */
@@ -466,4 +800,4 @@ declare function delegateWithScoring(subAgent: DelegationTarget, message: string
466
800
  feedbackTemplate?: (message: string, feedback: string) => string;
467
801
  }): Promise<ScoredDelegation>;
468
802
 
469
- export { streamAgentResponse as A, type BackgroundDelegation as B, type DelegationTarget as D, type LoopStrategy as L, type ReflectionStrategy as R, type StreamEvent as S, type DelegateOptions as a, type RoundStreamFactory as b, type DelegationResult as c, BudgetExceededError as d, DEFAULT_MAX_ITERATIONS as e, type DelegateFn as f, DelegationBudgetExceededError as g, DelegationError as h, type DelegationPort as i, type LoopFinishReason as j, type LoopOutcome as k, type LoopStrategyConfig as l, type ReflectionContext as m, type ReflectionResult as n, type ReflectionStrategyConfig as o, type ScoreVerdict as p, type ScoredDelegation as q, type Scorer as r, delegate as s, delegateBackground as t, delegateWithScoring as u, ladderReflectionStrategy as v, loopStrategyConfigSchema as w, noopReflectionStrategy as x, reflectionStrategyConfigSchema as y, resolveLoopStrategy as z };
803
+ export { noopReflectionStrategy as $, type AgentStopReason as A, type BackgroundDelegation as B, type CheckpointSavedEvent as C, type DelegationTarget as D, type ErrorEvent as E, type FileEditEvent as F, type ThinkingEvent as G, type ToolCallEvent as H, type IterationEvent as I, type ToolResultEvent as J, delegate as K, type LoopStrategy as L, delegateBackground as M, delegateWithScoring as N, isApprovalRequired as O, type PartialToolCallEvent as P, isDone as Q, type ReflectionStrategy as R, type StreamEvent as S, type TextDeltaEvent as T, isError as U, isPartialToolCall as V, isTextDelta as W, isToolCall as X, isToolResult as Y, ladderReflectionStrategy as Z, loopStrategyConfigSchema as _, type DelegateOptions as a, presentUIMessageStream as a0, reflectionStrategyConfigSchema as a1, resolveLoopStrategy as a2, streamAgentResponse as a3, type MaskError as a4, type RoundStreamFactory as b, type DelegationResult as c, type AgentStreamEvent as d, type AgentTurnMetadata as e, type ApprovalRequiredEvent as f, type ArtifactChunkEvent as g, type ArtifactStartEvent as h, BudgetExceededError as i, DEFAULT_MAX_ITERATIONS as j, type DelegateFn as k, DelegationBudgetExceededError as l, DelegationError as m, type DelegationPort as n, type DoneEvent as o, type LoopFinishReason as p, type LoopOutcome as q, type LoopStrategyConfig as r, type ReflectionContext as s, type ReflectionResult as t, type ReflectionStrategyConfig as u, type RunStartedEvent as v, type ScoreVerdict as w, type ScoredDelegation as x, type Scorer as y, type StateUpdateEvent as z };