@theokit/agents 14.5.1 → 15.0.1

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 (43) hide show
  1. package/CHANGELOG.md +137 -0
  2. package/README.md +54 -19
  3. package/dist/{agent-compiler-D7-Xd6Zh.d.ts → agent-compiler-DMQcLHIs.d.ts} +224 -6
  4. package/dist/auth.d.ts +19 -26
  5. package/dist/auth.js +2 -2
  6. package/dist/auth.js.map +1 -1
  7. package/dist/{bridge-entry-kh9JllQT.d.ts → bridge-entry-BWVce48e.d.ts} +92 -13
  8. package/dist/bridge.d.ts +4 -5
  9. package/dist/bridge.js +6 -4
  10. package/dist/{chunk-D2EFYZBV.js → chunk-4ULFP7GK.js} +2 -3
  11. package/dist/chunk-4ULFP7GK.js.map +1 -0
  12. package/dist/{chunk-DLBGAMHP.js → chunk-7YASPLQE.js} +34 -7
  13. package/dist/chunk-7YASPLQE.js.map +1 -0
  14. package/dist/{chunk-44IHBFL6.js → chunk-CV4YROFI.js} +99 -34
  15. package/dist/chunk-CV4YROFI.js.map +1 -0
  16. package/dist/{chunk-5WS66AW7.js → chunk-NIQJ4GZX.js} +7 -1
  17. package/dist/chunk-NIQJ4GZX.js.map +1 -0
  18. package/dist/{chunk-U72XTMYB.js → chunk-Q65ZIFNK.js} +1 -1
  19. package/dist/chunk-Q65ZIFNK.js.map +1 -0
  20. package/dist/config.d.ts +57 -7
  21. package/dist/config.js +48 -7
  22. package/dist/config.js.map +1 -1
  23. package/dist/{delegation-scoring-CH9Mr9NF.d.ts → delegation-scoring-CSe_C-iX.d.ts} +2 -8
  24. package/dist/hooks.js +1 -1
  25. package/dist/index.d.ts +22 -15
  26. package/dist/index.js +16 -10
  27. package/dist/index.js.map +1 -1
  28. package/dist/mcp-health.d.ts +1 -1
  29. package/dist/session.js +1 -1
  30. package/dist/testing.d.ts +3 -4
  31. package/dist/testing.js +1 -1
  32. package/dist/tools.d.ts +13 -7
  33. package/dist/tools.js +3 -3
  34. package/dist/tools.js.map +1 -1
  35. package/dist/{types-C16Wuh9E.d.ts → types-CxI2x6mU.d.ts} +38 -10
  36. package/docs/capability-map.md +1191 -0
  37. package/package.json +4 -3
  38. package/dist/chunk-44IHBFL6.js.map +0 -1
  39. package/dist/chunk-5WS66AW7.js.map +0 -1
  40. package/dist/chunk-D2EFYZBV.js.map +0 -1
  41. package/dist/chunk-DLBGAMHP.js.map +0 -1
  42. package/dist/chunk-U72XTMYB.js.map +0 -1
  43. package/dist/define-agent-y3fsG91f.d.ts +0 -188
package/CHANGELOG.md CHANGED
@@ -1,5 +1,142 @@
1
1
  # @theokit/agents
2
2
 
3
+ ## 15.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 80509ca: Docblock-only: the three-target parity rule is cited at the path this repository versions.
8
+
9
+ The comment in `in-process-turn.ts` pointed at the kit-relative form of that path <!-- rule-citation-ok: naming the stale form here is what the entry is about -->, which resolves
10
+ nowhere for anyone who clones — `.claude/` is gitignored. The document it means is
11
+ `docs/program/three-target-parity.md`, tracked and 102 lines. No behaviour changes and no export
12
+ moves; the patch exists because the changeset gate reads a changed publishable package and cannot
13
+ tell a comment from a contract, which is the right default for it to have.
14
+
15
+ ## 15.0.0
16
+
17
+ ### Major Changes
18
+
19
+ - d4c00bb: **Breaking:** `CheckpointOptions` is narrowed to `{ resumeSignal?: boolean }`. `storage`, `strategy`,
20
+ `maxCheckpoints` and `ttl` are removed, along with the `CheckpointStorage` and `CheckpointStrategy`
21
+ types.
22
+
23
+ The option read as a durable-checkpoint configuration and was a signalling flag: four fields
24
+ declared, exactly one ever read, as an `=== 'filesystem'` equality deciding whether a
25
+ `checkpoint_saved` event is emitted. `'drizzle'` and `'redis'` were indistinguishable from
26
+ `'memory'` in every code path.
27
+
28
+ The warning that pushed authors toward `'filesystem'` is gone. It said that value "selects the SDK's
29
+ durable conversation store"; the SDK persists every session to its transcript regardless, so it
30
+ selected nothing — and on a pod with no volume it named the one storage that is unreachable.
31
+
32
+ **Migration:** delete the removed fields; they configured nothing. `storage: 'filesystem'` becomes
33
+ `resumeSignal: true` if you want the `checkpoint_saved` event. Resume itself is a property of the
34
+ SDK's session transcript and needs no option.
35
+
36
+ ### Minor Changes
37
+
38
+ - c3d677d: An SDK that cannot read a foreign configuration root is now refused with a typed
39
+ `CompatRootUnsupportedError`, instead of warned about.
40
+
41
+ `compatSources` landed in `@theokit/sdk@5.0.0`. Below it the option is accepted and ignored, so every
42
+ `.claude/` surface is unavailable while the package resolves, compiles and runs. A `console.warn`
43
+ stood there, and its own text named the condition that kept it a warning: "Until this package's floor
44
+ can name a stable 5.x". The floor is `^5.3.0`, so the only way to reach that branch is an override —
45
+ and an override that silently disables every foreign surface is what a refusal is for.
46
+
47
+ Separate from `CompatImportUnsupportedError`, which is about narrowing a root the SDK can already
48
+ read (5.4.0). Reusing it would name the wrong version and send the reader to the wrong upgrade.
49
+
50
+ - fb80f7d: `createPermissionsPlugin` now requires a gate for the `ask` verdict, and `AgentBuilder` gained
51
+ `.subagents()`.
52
+
53
+ The gate is mandatory rather than optional because its absence had a silent, catastrophic default:
54
+ `PermissionEngine` answers `ask` for a tool no rule matches, and the SDK turns that into a hard block
55
+ when nothing answers it. Wiring the engine without a gate made every tool an operator had not
56
+ enumerated stop working, the moment they wrote any `permissions` block at all.
57
+
58
+ `.subagents()` supplies subagent definitions by name, reaching `AgentOptions.agents`. It is distinct
59
+ from `settingSources`, which discovers them: the framework reads `.claude/agents/<name>.md` and takes
60
+ the prompt from the file, so a caller that enriched a definition — `applySubagentMemory` folding in a
61
+ `MEMORY.md` is the measured case — had nowhere to put the result and it was silently discarded.
62
+
63
+ - b81957c: `FOREIGN_KEY_DECISIONS` records what this package does with each `.claude/settings.json` key its
64
+ diagnostics name, and why — `honoured` or `refused`, each with a reason about this product rather
65
+ than about the reference.
66
+
67
+ `model` and `cleanupPeriodDays` join the settings schema as honoured keys. Reporting a key as "not
68
+ implemented here" says the code does not act on it; it never says whether that is a refusal or an
69
+ omission, and an author reading it cannot tell whether to stop writing the key or to wait for it.
70
+
71
+ ### Patch Changes
72
+
73
+ - 4aa6602: Documents that a subagent's `memory:` declaration needs `@theokit/sdk@5.9.0`, and pins it
74
+ with a test.
75
+
76
+ `applySubagentMemory` shipped in 14.5.0 reading a declaration the SDK parses off subagent
77
+ frontmatter. Before 5.9.0 that parse REFUSES the key — measured against the published
78
+ tarballs, with a control that declares no `memory:` and loads on both:
79
+
80
+ 5.3.0 ConfigurationError: Subagent note-taker.md: unknown frontmatter field "memory"
81
+ (accepted: name, description, model, tools, reasoning_effort, mcp, sandbox)
82
+ 5.9.0 loads both; `memory: "project"` survives
83
+
84
+ The failure was never silent. What misleads is where it points: naming the field reads as a
85
+ typo, so the line an author meant to write gets deleted instead of the SDK upgraded.
86
+
87
+ **The range is unchanged at `^5.3.0`, and there is no runtime guard.** Both were tried and
88
+ both were refused by gates that were right. Raising the floor is refused by
89
+ `the-declared-sdk-range-delivers-what-the-code-assumes.test.ts`, which holds that a gap
90
+ announcing itself is guarded rather than closed by the range — closing it strands every
91
+ consumer, including those who never write `memory:`. A guard is refused by the bundle
92
+ budget: wiring one into `listSubagentNames` cost **161 bytes** of a root barrel with 72 of
93
+ headroom, and the cost is the COUPLING rather than the code — four different bodies produced
94
+ byte-identical bundles, and a dedicated module was worse at 40 403 because it duplicated
95
+ `createRequire`.
96
+
97
+ So the requirement is documented where someone hits it, and a test fails if it stops being
98
+ true. `applySubagentMemory` itself needs no particular SDK: it reads `node:fs`, takes an
99
+ object, and works on any version — the requirement belongs to the parser.
100
+
101
+ - f6a5526: Two rows of the foreign-surfaces table were false, and a test now checks the class they
102
+ belong to.
103
+
104
+ `agent-memory/` read _"Measured 2026-09-15: no consumer does yet"_ while `apps/theocode`
105
+ imported `applySubagentMemory` and applied it in `delegation/role-discovery.ts:84-85`. The
106
+ measurement was true when taken — the consumer could not call it, because the function was
107
+ not in the version it pinned, and its own source says so: _"the one line that wires it does
108
+ not compile here yet"_. The integration was written, tested, and left disconnected, waiting
109
+ on a publish. Nothing about either side changed; the consumer moved into this repository and
110
+ the import resolved.
111
+
112
+ `themes/*.json` read _"nobody reads it, in any of the three packages"_ and never said which
113
+ three. Naming them took one edit and the sentence failed immediately: `apps/theocode`
114
+ resolves `~/.claude/themes/` in `tui/src/theme/custom-theme.ts`, with three reads in that one
115
+ file. It is the same shape as `keybindings.json` — out of scope HERE, read by the consumer —
116
+ which this section had explicitly denied.
117
+
118
+ **A claim nobody can check is not a weaker claim; it is a claim that has never been tested.**
119
+
120
+ `an-absence-is-a-decision-or-it-is-a-gap.test.ts` now confronts any row asserting an absence
121
+ of consumption with the consumer's own source, and fails naming both. It reads TABLE ROWS
122
+ rather than the file — the first version matched the prose explaining the old claim, which is
123
+ the mistake that file already records one block below. Positive controls on both rows: each
124
+ restored claim fails the test with the reason.
125
+
126
+ - 3d73fd2: Every text-bearing channel a client receives now reflects the declared output guards.
127
+
128
+ `DoneEvent.result` was not moderated: with a redactor declared, one turn delivered `text_delta ->
129
+ "here: [R]"` and `done.result -> "here: sk-abc123"`, so a client rendering the terminal frame got the
130
+ secret the guard existed to remove. It is now rebuilt from the round's own moderated deltas — which is
131
+ exact rather than approximate, because the frame carries the visible text of its own round.
132
+
133
+ `task_progress.text` — the fourth channel — takes an ordinary third `moderateOutputStream` pass on
134
+ both the runner and the served path. It is not a mirror of anything: it carries text the model writes
135
+ through `task-tools`, and a milestone naming a secret is the same disclosure as a delta naming it.
136
+
137
+ - Updated dependencies [54d87cf]
138
+ - @theokit/presenter@0.10.0
139
+
3
140
  ## 14.5.1
4
141
 
5
142
  ### Patch Changes
package/README.md CHANGED
@@ -98,10 +98,10 @@ configuration that had no effect.
98
98
  | `skills/`, `agents/`, `commands/`, `plugins/` | read when the dialect is declared |
99
99
  | `.mcp.json` | read; a field this runtime does not carry is reported |
100
100
  | `output-styles/*.md` | read, selected by `settings.json` |
101
- | `agent-memory/` | **resolvable** — the SDK carries the declaration, `applySubagentMemory` applies it, and the host decides whether to call it. Measured 2026-09-15: no consumer does yet |
101
+ | `agent-memory/` | **read** — `applySubagentMemory` applies a subagent's `memory:` declaration, and `apps/theocode` calls it (`apps/theocode/packages/agent/src/delegation/role-discovery.ts`). The host still decides: nothing here calls it for you |
102
102
  | `workflows/*.js` | **refused**, and reported. Every other surface is data; a workflow is code, and executing JavaScript found under a caller-supplied directory is a decision that belongs to you |
103
103
  | `keybindings.json` | **out of scope HERE** — read by the consumer, see below |
104
- | `themes/*.json` | **out of scope** — nobody reads it, in any of the three packages |
104
+ | `themes/*.json` | **out of scope HERE** — read by the consumer, like `keybindings.json`. `apps/theocode` resolves `~/.claude/themes/` in `apps/theocode/packages/tui/src/theme/custom-theme.ts`; this package and `@theokit/tui` read neither |
105
105
  | `~/.claude.json` — OAuth state, UI toggles | **out of scope** — a CLI's own state |
106
106
  | `~/.claude.json` — personal-scope MCP servers | **read** — `loadPersonalMcpServers` |
107
107
 
@@ -124,8 +124,10 @@ in `terminal-io/keybindings.ts`, against a format it measured against the publis
124
124
  Naming the toolkit as the owner sent a reader to the package that does the least with it — and the
125
125
  row said `out of scope` without saying out of scope FOR WHOM, which is the half that misleads.
126
126
 
127
- `themes/*.json` is read by nobody, in any of the three packages. That is a genuine gap rather than a
128
- delegation, and it is stated here rather than implied by an ownership that does not exist.
127
+ `themes/*.json` is read by the CONSUMER, exactly as `keybindings.json` is: `apps/theocode` resolves
128
+ `~/.claude/themes/` in `apps/theocode/packages/tui/src/theme/custom-theme.ts` and selects one through `/theme custom:<slug>`.
129
+ This package reads it in 0 files and so does `@theokit/tui` — measured 2026-09-17 against the
130
+ installed 0.80.0.
129
131
 
130
132
  **`~/.claude.json` is two things under one name, and the split is the point.** Its OAuth state and UI
131
133
  toggles are a CLI's own state — that file is written by a specific program about its own session, and
@@ -138,19 +140,32 @@ and only that key, and returns empty on a malformed file rather than throwing, b
138
140
  in a shared home file must not break every project on the machine.
139
141
 
140
142
  Both MCP readers are called by the host, not from inside this package — `loadMcpJson` has no internal
141
- caller either, and a library with no `main()` cannot have one. That is why both rows say **read** while
142
- `agent-memory/` says **resolvable**: the difference is not who calls, it is whether the chain is intact.
143
- A `memory:` declaration was DROPPED by `@theokit/sdk` before any reader could see it; nothing drops an
144
- MCP server.
143
+ caller either, and a library with no `main()` cannot have one. That is why both rows say **read**: the
144
+ criterion is not who calls, it is whether the chain is intact. Nothing drops an MCP server.
145
+
146
+ `agent-memory/` said **resolvable** under the same criterion, and for a real reason — a `memory:`
147
+ declaration was DROPPED by `@theokit/sdk` before any reader could see it, so the applier could be called
148
+ and still govern nothing. **That is no longer true**, which is why its row says `read` now. The SDK
149
+ carries the declaration, `packages/agents/tests/integration/the-sdk-version-that-parses-memory.test.ts`
150
+ pins the half that was dangerous, and `apps/theocode/packages/agent/src/delegation/role-discovery.ts:84-85`
151
+ is the join. The sentence above outlived the fix for two days, which is the shape this whole section
152
+ exists to catch: the table was corrected and the paragraph explaining it was not.
145
153
 
146
154
  The registry entry that prompted this counts four decisions across three files, because
147
155
  `~/.claude.json` is split. That is the count, stated so nobody goes looking for a fourth file.
148
156
 
149
157
  ### `themes/*.json`
150
158
 
151
- Read by none of the three packages, measured 2026-09-15. After `keybindings.json` turned out to be
152
- read by the consumer rather than by the package it was attributed to, the obvious next move was to
153
- treat this as the same finding. It is not.
159
+ Read by the consumer, measured 2026-09-17. After `keybindings.json` turned out to be read by
160
+ `apps/theocode` rather than by the package it was attributed to, the obvious next move was to treat
161
+ this as the same finding — and it **is** the same finding, which this document denied until the
162
+ three packages were named.
163
+
164
+ The denial is worth keeping, because it shows what an unfalsifiable sentence costs. This section
165
+ read *"nobody reads it, in any of the three packages"* and never said which three. A reader could
166
+ not check a set they could not enumerate, so nobody did; naming them took one edit, and the sentence
167
+ was false the moment it became checkable — `apps/theocode` resolves `~/.claude/themes/` in
168
+ `apps/theocode/packages/tui/src/theme/custom-theme.ts`, with three reads in that one file.
154
169
 
155
170
  This package has no colour at all — a grep for `color`, `chalk` or `theme` across its source returns
156
171
  nothing, the one apparent hit being `ansi` inside `stateTransitionHistory`. It produces text and tool
@@ -207,15 +222,28 @@ and not applied, so nobody believes it took effect.
207
222
  **200 lines, capped at 25KB**, of its `MEMORY.md` — both caps apply, and truncation is reported
208
223
  rather than silent.
209
224
 
210
- **The host calls it. Nothing here calls it for you, and `memory:` in subagent frontmatter does not
211
- reach it.** Measured 2026-09-15: `resolveAgentMemory` has no caller in this repository outside its
212
- own tests, and `@theokit/sdk` — which is the package that loads `.claude/agents/*.md`, and does not
213
- depend on this one — lists `memory` among the fields it refuses, so a file declaring it is skipped
214
- with a diagnostic rather than granted a directory.
225
+ **The host calls it. Nothing here calls it for you** — and that sentence is the whole contract: this
226
+ package exposes the reader, an application wires it.
227
+
228
+ One does. `apps/theocode` imports `applySubagentMemory` from `@theokit/agents/config` and applies it
229
+ to both role sets in `apps/theocode/packages/agent/src/delegation/role-discovery.ts:84`; that reaches `resolveAgentMemory`
230
+ through `agent-memory.ts:231`. So the chain runs end to end, and this document says **read** rather
231
+ than **resolvable**.
232
+
233
+ It said the opposite until 2026-09-16, and the reason is worth keeping because it is the failure
234
+ mode this table exists to prevent. The measurement behind *"no consumer does yet"* was true when
235
+ taken: the consumer could not call it. Its own source says why — *"`applySubagentMemory` … is NOT in
236
+ the published 14.4.0 this project pins, so the one line that wires it does not compile here yet"*.
237
+ The integration was written, tested, and left disconnected, waiting on a publish.
238
+
239
+ What changed is not the code on either side. `apps/theocode` joined this repository, `@theokit/agents`
240
+ resolves through the workspace, and the line compiled. **A capability can be published, correct, and
241
+ unreachable** — and a document that reports the reachable state as the real one is reporting a
242
+ delivery problem as a design decision.
215
243
 
216
- Saying "read" in the table above, as this document did until that measurement, put this surface in
217
- the same column as `CLAUDE.md` and implied a wiring that does not exist. The reader is real and the
218
- three roots below are real; what an author supplies is the call.
244
+ `@theokit/sdk` before 5.9.0 refuses `memory:` in frontmatter entirely, naming the field rather than
245
+ the version — `agent-memory.ts` carries that measurement, and
246
+ `the-sdk-version-that-parses-memory.test.ts` pins it.
219
247
 
220
248
  | `memory:` | Root | Who can see it |
221
249
  |---|---|---|
@@ -270,6 +298,13 @@ which is a worse failure than never having documented it.
270
298
  Those are the SDK's, and a PR that adds one here is rejected on sight.
271
299
  - It does **not** depend on the `theokit` web framework. The dependency runs the other way.
272
300
  - Web Standards over Node APIs inside `src/` — `Request`/`Response`, `fetch`, `crypto.randomUUID`.
301
+ Two `node:crypto` imports remain and both are measured rather than overlooked: `createHash` in
302
+ `packages/agents/src/hooks/hook-fingerprint.ts`, because the Web equivalent is async and this
303
+ function is not, and `randomBytes` in `packages/agents/src/hooks/hook-spec.ts`, where
304
+ `getRandomValues` would buy conformance at the cost of a hand-written hex conversion. A third
305
+ was removed on 2026-09-17: `randomUUID` had no such defence — the global is the API this line
306
+ names, and three other files in the package were already using it.
307
+ File reads keep `node:fs`: there is no Web Standard to prefer.
273
308
  Node APIs live in adapters.
274
309
 
275
310
  ## Who decides policy
@@ -1,8 +1,10 @@
1
- import { InlineSkill, SystemPromptResolver, SessionStore, PermissionGate, MemorySettings, SkillsSettings, ContextSettings, TelemetrySettings } from '@theokit/sdk';
2
- import { AgentDefinition } from '@theokit/sdk/subagents-loader';
1
+ import { InlineSkill, CustomTool, MemorySettings, TelemetrySettings, SystemPromptResolver, SessionStore, PermissionGate, SkillsSettings, ContextSettings } from '@theokit/sdk';
2
+ import { AgentDefinition as AgentDefinition$1 } from '@theokit/sdk/subagents-loader';
3
3
  import { TheokitAgentError } from '@theokit/sdk/errors';
4
- import { R as ReasoningEffort, a as MemoryOptions, P as ProjectContextOptions, M as McpServersMap, H as HumanInTheLoopOptions, C as CheckpointOptions, T as ToolOptions, A as ApprovalOptions, B as BudgetOptions } from './types-C16Wuh9E.js';
5
- import { G as GatedSettingSource, a as GatedCompatSource } from './setting-sources-gate-BJJiKq33.js';
4
+ import { R as ReasoningEffort, H as HumanInTheLoopOptions, M as McpServersMap, a as MemoryOptions, P as ProjectContextOptions, C as CheckpointOptions, T as ToolOptions, A as ApprovalOptions, B as BudgetOptions } from './types-CxI2x6mU.js';
5
+ import { z } from 'zod';
6
+ import { S as SettingSourcesSelection, G as GatedSettingSource, a as GatedCompatSource } from './setting-sources-gate-BJJiKq33.js';
7
+ import { H as HookHandlers } from './hook-handlers-Cw2FsnE5.js';
6
8
 
7
9
  /**
8
10
  * M9 (theokit-ai-first) — guardrail contract + typed errors.
@@ -277,6 +279,213 @@ declare class CompatImportUnsupportedError extends TheokitAgentError {
277
279
  constructor(version: string | undefined);
278
280
  }
279
281
 
282
+ /**
283
+ * M2 (theokit-ai-first) — `defineAgent`, the zero-config imperative agent surface.
284
+ *
285
+ * ADR-B1: `defineAgent({...})` (default-exported from a top-level `agents/<name>.ts`) is
286
+ * the canonical zero-config surface; the `@Agent` class decorator stays the advanced/DI
287
+ * surface. Both compile to {@link CompiledAgentOptions} and run through the same SDK
288
+ * runtime (`createSdkAgentStream`) — one runtime, two syntaxes.
289
+ *
290
+ * This module is PURE metadata (sdk-runtime.md / G2): `defineAgent` describes an agent, it
291
+ * NEVER calls an LLM. It imports only `zod` (types) + the compiler shape — no `theokit`
292
+ * core, preserving the agents → (nothing) dependency direction (G1).
293
+ */
294
+
295
+ /**
296
+ * One subagent, as `AgentOptions.agents` in the SDK takes it.
297
+ *
298
+ * Declared here rather than imported so the framework states its own contract: the SDK's
299
+ * `AgentDefinition` carries fields this bridge does not thread, and re-exporting it would promise
300
+ * every one of them.
301
+ */
302
+ interface SubagentAgentDefinition {
303
+ /** What the model reads when choosing this subagent. */
304
+ readonly description: string;
305
+ /** The instructions it runs with — the field that carries an applied memory. */
306
+ readonly prompt: string;
307
+ /** Tool names it may call. Absent means the framework decides, as it does for a discovered one. */
308
+ readonly tools?: readonly string[];
309
+ }
310
+ /**
311
+ * Brand tag for a `defineAgent` value. `Symbol.for` (global registry, not `Symbol()`) so
312
+ * the brand survives duplicate module instances (dual-package / bundling) — the scanner's
313
+ * brand-check then works regardless of which copy created the definition.
314
+ */
315
+ declare const AGENT_BRAND: unique symbol;
316
+ /** Config accepted by {@link defineAgent}. */
317
+ interface DefineAgentConfig<TInput extends z.ZodType = z.ZodType> {
318
+ /** Zod schema for the request body — lifted into the typed client (M2, {@link InferAgentInput}). */
319
+ input?: TInput;
320
+ /** Model id (e.g. `claude-sonnet-4-6`). Falls back to the SDK default when omitted. */
321
+ model?: string;
322
+ /** Static system prompt. */
323
+ system?: string;
324
+ /** Extended-thinking effort. */
325
+ reasoningEffort?: ReasoningEffort;
326
+ /**
327
+ * theokit#363 — hard ceiling on the agent's tool-calling turns within ONE run. Reaching it ends
328
+ * the turn instead of letting the model keep calling tools; absent ⇒ the SDK's own ceiling (8).
329
+ *
330
+ * Named `maxIterations`, not `maxSteps`, because the concept already has exactly one name here —
331
+ * `@Agent({ maxIterations })`, `@MainLoop({ maxIterations })`, `AgentRunner.stream({ maxIterations })`,
332
+ * `delegate({ maxIterations })` — and one name in the SDK it lowers to (`SendOptions.maxIterations`).
333
+ * A second name would be the only place needing translation, and the translation is invisible where
334
+ * it costs most: the SDK rejects an invalid value with a message naming `SendOptions.maxIterations`,
335
+ * which an author who typed `.maxSteps()` has no way to connect to what they wrote. Familiarity
336
+ * argues for the ai-sdk spelling, but ai-sdk's own name is `stopWhen: stepCountIs(n)` — borrowing
337
+ * "steps" would buy recognition of a word, not of an API.
338
+ */
339
+ maxIterations?: number;
340
+ /**
341
+ * Pre-built tools. Accepts the `@theokit/sdk` `CustomTool` that `defineAgentTool`
342
+ * (theokit/server) and every `@theokit/sdk-tools` factory return (issue #81) — they are
343
+ * normalized to the internal {@link CompiledTool} shape at compile time.
344
+ */
345
+ tools?: readonly CustomTool[];
346
+ /**
347
+ * M7 — run-context: an opaque, per-agent object forwarded to every tool handler's
348
+ * `ctx.context` at run time (injected by the theokit adapter's tool wrapper). Set shared config
349
+ * (e.g. `{ projectRoot }`) ONCE at the agent level instead of baking it into each tool
350
+ * factory. Distinct from `@Agent`'s context-window `context`.
351
+ */
352
+ context?: Record<string, unknown>;
353
+ /**
354
+ * M9 — guardrails: input/output guards applied at the framework boundary (ADR-0040 § D2).
355
+ * Input guards run on the user message before the SDK runtime; a `block` fails the run fast.
356
+ * Built-ins live in `@theokit/agents` (`promptInjectionDetector`, `piiDetector`, `costGuard`,
357
+ * `unicodeNormalizer`, `outputModeration`).
358
+ */
359
+ guardrails?: readonly Guardrail[];
360
+ /**
361
+ * M14 — HITL approvals keyed by tool name. Each gated tool pauses the run and emits an
362
+ * `approval_required` event until approved (reuses the same `compiled.hitl` wiring the `@Agent`
363
+ * + `@HumanInTheLoop` path produces). A key that does not match a declared tool fails fast at
364
+ * compile time.
365
+ */
366
+ approvals?: Record<string, HumanInTheLoopOptions>;
367
+ /**
368
+ * M13 — skills selection: a static list (compiled straight to the SDK `skills.enabled`) OR a
369
+ * per-request resolver `(ctx) => string[]` (carried on `compiled.skillsResolver`, resolved by the
370
+ * request path against the run-context). Absent ⇒ the SDK enables every discovered skill.
371
+ */
372
+ skills?: SkillsSelection;
373
+ /**
374
+ * Subagents this agent carries, registered by name — `AgentOptions.agents` in the SDK.
375
+ *
376
+ * #825 — distinct from what `settingSources` discovers, and that distinction is the whole reason it
377
+ * exists. The framework reads `.claude/agents/<name>.md` itself and takes the prompt from the FILE,
378
+ * so a caller that enriches a definition — `applySubagentMemory` folding in a `MEMORY.md` is the
379
+ * measured case — had nowhere to put the result. It computed the enriched copy and the framework
380
+ * used the file's, with nothing reporting a difference.
381
+ *
382
+ * A name declared here and also found on disk is the same subagent; the definition given here is
383
+ * the one that runs, because it is the one an author decided rather than the one a loader guessed.
384
+ */
385
+ subagents?: Record<string, SubagentAgentDefinition>;
386
+ /**
387
+ * theokit-file-based-config — opt into `.theokit/` file-based config (skills, subagents, hooks,
388
+ * MCP, context, cron). The SDK discovers config from these roots under the app's `cwd`:
389
+ * `project` = `<cwd>/.theokit/`, `user` = `~/.theokit/`. Absent ⇒ inline (code) config only.
390
+ *
391
+ * SECURITY (M68): `project` reads `.theokit/hooks.json`, which **executes shell**, so it requires
392
+ * a `TrustPosture` rather than a string. This field used to take `readonly SettingSource[]`, and
393
+ * its own JSDoc justified the risk as *"opt-in because `.theokit/` is the app's own repo (informed
394
+ * consent)"*. That premise holds for a web app whose `cwd` is its own deploy; it does not hold for
395
+ * an agent whose `cwd` is a repository the user just cloned, where `.theokit/` is
396
+ * attacker-controlled content.
397
+ *
398
+ * `user` stays a plain boolean — `~/.theokit/` is the operator's own machine. Omitting a root is
399
+ * not enabling it. The SDK owns discovery + execution (G2 / ADR-0040); theokit resolves the
400
+ * selection through `resolveSettingSources` and wires the result into
401
+ * `Agent.create({ local.settingSources })`.
402
+ */
403
+ settingSources?: SettingSourcesSelection;
404
+ /**
405
+ * #686 — decide whether a hook declared in a config root is spawned at all, BEFORE it runs.
406
+ *
407
+ * Covers any root, including a foreign dialect imported through `settingSources.claudeCode`.
408
+ * Requires `@theokit/sdk >= 5.4.0`: declaring it against an older SDK is REFUSED at assembly
409
+ * rather than forwarded, because a gate that silently does not gate is worse than none.
410
+ */
411
+ hookApproval?: HookApprovalGate;
412
+ /**
413
+ * B-054 — `MEMORY.md` here is NOT the Claude Code CLI's auto-memory file. This is the durable
414
+ * subsystem below: a SQLite+FTS5 store under `.theokit/memory/`, with `memory_search`/`memory_get`
415
+ * tools and no index cap. The CLI's lives under its own home (`CLAUDE_CONFIG_DIR` or `~/.claude`),
416
+ * is capped at 200 lines / 25 KB on read, and is swept on `cleanupPeriodDays` — none of which is
417
+ * implemented here. The SDK READS that directory for interop and does not write to it.
418
+ *
419
+ * Same filename, different directory, different semantics. Stated at both ends because a checklist
420
+ * that greps for `MEMORY.md` finds one and concludes the other exists.
421
+ *
422
+ * M49 — durable memory (the SDK's `.theokit/memory/` subsystem: `Remember:` capture, MEMORY.md
423
+ * store, auto-injected `<memory>` block, `memory_search`/`memory_get` tools). The shape is the
424
+ * SDK's own `MemorySettings` — the canonical runtime contract. Projected into
425
+ * `Agent.create({ memory })` by `assembleM8CreateOptions`.
426
+ */
427
+ memory?: MemorySettings;
428
+ /**
429
+ * B-072 — OpenTelemetry for this agent. Forwarded verbatim to `Agent.create({ telemetry })`,
430
+ * where the SDK emits spans for `agent.send`, `llm.call`, `tool.call` and `memory.search`.
431
+ *
432
+ * `{ enabled: true }` is the minimal opt-in. `@opentelemetry/api` is an OPTIONAL peer of the SDK:
433
+ * without it the whole thing is a silent no-op, which is the usual reason a run reports no spans.
434
+ */
435
+ telemetry?: TelemetrySettings;
436
+ /**
437
+ * Code `Plugin` objects forwarded to `Agent.create({ plugins })` — EXTENSION units (tools,
438
+ * commands, model providers, memory adapters). For lifecycle interception use {@link hooks}.
439
+ */
440
+ /**
441
+ * Code plugins — `{ name, register }`. NOT the Claude Code filesystem-bundle form, which is
442
+ * declared by living in a `plugins/` directory rather than by being passed here (B-055).
443
+ *
444
+ * `readonly unknown[]` is what let the wrong shape through silently.
445
+ */
446
+ plugins?: readonly CodePlugin[];
447
+ /**
448
+ * Lifecycle hooks keyed by `HookName` (`pre_tool_call` may veto via `{ block, message }`). Set by
449
+ * the builder's `hooks()`; converted into a code plugin at `build()` and never reaching the SDK
450
+ * under this name — the plugin is the TRANSPORT, this is the contract callers write against.
451
+ */
452
+ hooks?: HookHandlers | Readonly<Record<string, unknown>>;
453
+ /**
454
+ * MCP servers available to the agent — the builder-chain equivalent of the `@MCP` class
455
+ * decorator. Each key is a server name; the value is the server configuration. Forwarded
456
+ * unchanged to `Agent.create({ mcpServers })` (the SDK owns MCP execution). Absent ⇒ no MCP.
457
+ */
458
+ mcpServers?: McpServersMap;
459
+ }
460
+ /**
461
+ * A branded agent definition — the value {@link defineAgent} returns.
462
+ *
463
+ * `TTools` (M8) is a phantom type parameter carrying the tool-name union: the `AgentBuilder.create()` builder
464
+ * threads its accumulated literal tool names here (`.build()` returns `AgentDefinition<TInput,
465
+ * 'a' | 'b'>`), so the generated client (`.theokit/agents.d.ts`) can expose them via
466
+ * {@link InferAgentToolNames}. `defineAgent` leaves it `string` (its tools array carries no literal
467
+ * names). Never present at runtime.
468
+ */
469
+ type AgentDefinition<TInput extends z.ZodType = z.ZodType, TTools extends string = string> = DefineAgentConfig<TInput> & {
470
+ readonly [AGENT_BRAND]: true;
471
+ readonly __toolNames?: TTools;
472
+ };
473
+ /** Infer the request type of an agent definition from its `input` Zod schema. */
474
+ type InferAgentInput<T> = T extends AgentDefinition<infer S> ? (S extends z.ZodType ? z.infer<S> : never) : never;
475
+ /**
476
+ * Infer the tool-name union of an agent definition (M8). Yields the literal union for agents built
477
+ * with the `AgentBuilder.create()` builder (`'read_file' | 'count_lines'`), or `string` for `defineAgent` agents
478
+ * whose tools array carries no literal names.
479
+ */
480
+ type InferAgentToolNames<T> = T extends AgentDefinition<z.ZodType, infer N> ? N : never;
481
+ /** Brand-check: is `value` a {@link defineAgent} result? */
482
+ declare function isAgentDefinition(value: unknown): value is AgentDefinition;
483
+ /**
484
+ * Lower a definition to the SDK-ready {@link CompiledAgentOptions} — the same shape
485
+ * `compileAgent` (decorator path) produces, so both surfaces converge on one runtime.
486
+ */
487
+ declare function compileAgentDefinition(def: AgentDefinition): CompiledAgentOptions;
488
+
280
489
  /**
281
490
  * Agent compiler — transforms decorator metadata into SDK calls.
282
491
  *
@@ -352,7 +561,7 @@ declare function compileTools(toolboxes: ToolboxWalkResult[], toolboxInstances:
352
561
  * The local type was referenced in exactly two places, both of them its own declaration and the
353
562
  * field that held it, so adopting the SDK shape removed a mismatch rather than migrating users.
354
563
  */
355
- type CompiledSubAgent = AgentDefinition;
564
+ type CompiledSubAgent = AgentDefinition$1;
356
565
  /** Compiled agent options ready for SDK Agent.create(). */
357
566
  interface CompiledAgentOptions {
358
567
  model?: string;
@@ -372,6 +581,15 @@ interface CompiledAgentOptions {
372
581
  * (merged with `cwd`, decoupled from inline skills). Absent ⇒ inline (code) config only.
373
582
  */
374
583
  settingSources?: readonly GatedSettingSource[];
584
+ /**
585
+ * #825 — subagents the AUTHOR declared, carried through to `AgentOptions.agents`.
586
+ *
587
+ * Beside `settingSources` on purpose: that field says which roots the SDK may DISCOVER subagents
588
+ * in, and this one says which subagents exist with which instructions. A caller that applied a
589
+ * `MEMORY.md` to a definition has only this field to put the result in — through the other, the
590
+ * SDK re-reads the file and the enriched copy is lost.
591
+ */
592
+ subagents?: Readonly<Record<string, SubagentAgentDefinition>>;
375
593
  /**
376
594
  * Foreign configuration dialects, already authorised (usetheokit/theokit#634).
377
595
  *
@@ -477,4 +695,4 @@ interface CompiledAgentOptions {
477
695
  skillsResolver?: SkillsSelection;
478
696
  }
479
697
 
480
- export { type CodePlugin as C, type Guardrail as G, type HookApprovalGate as H, MalformedGuardrailResultError as M, type SkillsSelection as S, type ToolWalkResult as T, UnreadableTextPayloadError as U, type CompiledAgentOptions as a, type CompiledTool as b, CompatImportUnsupportedError as c, CostBudgetExceededError as d, type GuardrailAction as e, GuardrailError as f, type GuardrailPhase as g, type GuardrailResult as h, GuardrailViolationError as i, type HookApprovalRequest as j, HookGateUnsupportedError as k, type SkillsRequestContext as l, type ToolboxWalkResult as m, compileTools as n, resolveEnabledSkills as r };
698
+ export { type AgentDefinition as A, type CompiledAgentOptions as C, type DefineAgentConfig as D, type Guardrail as G, type HookApprovalGate as H, type InferAgentInput as I, MalformedGuardrailResultError as M, type SkillsRequestContext as S, type ToolWalkResult as T, UnreadableTextPayloadError as U, type CompiledTool as a, AGENT_BRAND as b, CompatImportUnsupportedError as c, CostBudgetExceededError as d, type GuardrailAction as e, GuardrailError as f, type GuardrailPhase as g, type GuardrailResult as h, GuardrailViolationError as i, type HookApprovalRequest as j, HookGateUnsupportedError as k, type InferAgentToolNames as l, type SkillsSelection as m, type ToolboxWalkResult as n, compileAgentDefinition as o, compileTools as p, isAgentDefinition as q, resolveEnabledSkills as r, type SubagentAgentDefinition as s, type CodePlugin as t };
package/dist/auth.d.ts CHANGED
@@ -71,27 +71,22 @@ declare class AuthProvider {
71
71
  * round-trip that stores nothing. The `AuthProvider` docblock instructed exactly that:
72
72
  * *"the caller persists them via `AuthProvider.persist`"*.
73
73
  *
74
- * ## The design came from measuring three peers, and it refuted the original proposal
75
- *
76
- * - **`codex`** — `codex-rs/login/src/device_code_auth.rs:234` has `run_device_code_login`, which
77
- * returns `()`: **nothing** comes out for the caller to persist, and the two granular halves stay
78
- * public. `loginWithDevice` copies that shape.
79
- * - **`opencode`** — every provider is an object with `methods: [{ label, type, authorize }]`, and
80
- * three providers written by different authors converge on **3 labelled methods** each. The label
81
- * is what the UI shows: it turns a protocol choice into a choice between readable phrases.
82
- * - **REJECTED — a `kind` discriminant.** None of the three discriminates protocol by field. The
83
- * measurement that closes the case: in `opencode`, Codex's browser and headless methods carry the
84
- * **same** `type: 'oauth'` — so `type` classifies the **kind of credential**, not the protocol. A
85
- * `kind` with internal dispatch would be a `switch`, exactly the defect this milestone removes
86
- * from the consumer. Here, each method points at **its own** function.
74
+ * ## The shape, and the discriminant that was rejected
75
+ *
76
+ * `loginWithDevice` returns `()` — nothing comes out for the caller to persist, and the two granular
77
+ * halves stay public. A provider is an object with `methods: [{ label, type, authorize }]`, and the
78
+ * label is what the UI shows: it turns a protocol choice into a choice between readable phrases.
79
+ *
80
+ * **REJECTED — a `kind` discriminant.** `type` classifies the **kind of credential**, not the
81
+ * protocol: a browser method and a headless one can both be `type: 'oauth'`. A `kind` with internal
82
+ * dispatch would be a `switch`, exactly the defect this milestone removes from the consumer. Here,
83
+ * each method points at **its own** function.
87
84
  *
88
85
  * ## Why the public identity lives HERE
89
86
  *
90
- * `codex` exports `CLIENT_ID` from the crate that implements the flow (`login/src/lib.rs:32`) and
91
- * the CLI **imports** it; `opencode` declares it inside the plugin. The two arrived at the same
92
- * place independently — and with the same value the consumer had copied. As long as it lived in the
93
- * consumer, every project wanting Codex would copy four public constants: a DRY violation across the
94
- * boundary, with two owners of the same fact.
87
+ * The client identity belongs beside the flow that uses it rather than in the consumer, so a surface
88
+ * cannot hold a stale copy of it.
89
+ *
95
90
  */
96
91
  /**
97
92
  * A labelled way of obtaining a credential within a provider.
@@ -128,10 +123,9 @@ interface DeviceAuthProvider {
128
123
  readonly methods: readonly AuthMethod[];
129
124
  }
130
125
  /**
131
- * Environment override for `clientId` — adopted from `codex`, which exports `CLIENT_ID` **and**
132
- * `CLIENT_ID_OVERRIDE_ENV_VAR` (`login/src/lib.rs:32-33`). It dissolves the false dilemma between a
133
- * fixed constant (inflexible) and a mandatory parameter (which hands the copy back to the consumer):
134
- * a default in the package, an escape for whoever needs one.
126
+ * Environment override for `clientId`. It dissolves the false dilemma between a fixed constant
127
+ * (inflexible) and a mandatory parameter (which hands the copy back to the consumer): a default in
128
+ * the package, an escape for whoever needs one.
135
129
  */
136
130
  declare const CODEX_CLIENT_ID_ENV_VAR = "THEOKIT_CODEX_CLIENT_ID";
137
131
  /**
@@ -157,10 +151,9 @@ interface LoginWithDeviceOptions {
157
151
  * Authorizes **and** persists, in one call. Returns where the credential landed and the account it
158
152
  * was attributed to — **never** token material.
159
153
  *
160
- * The shape comes from `run_device_code_login` (`codex`), which returns `()`: if nothing comes out
161
- * for the caller, there is no step it can forget. The two halves stay public on `AuthProvider`
162
- * (`deviceLogin` / `persist`) for whoever needs the granularity — the same choice `codex` makes by
163
- * keeping `request_device_code` and `complete_device_code_login` public alongside the facade.
154
+ * Nothing comes out for the caller, so there is no step it can forget. The two halves stay public on
155
+ * `AuthProvider` (`deviceLogin` / `persist`) for whoever needs the granularity, alongside this
156
+ * facade.
164
157
  *
165
158
  * It delegates verbatim: `method.authorize` runs the flow and `AuthProvider.persist` writes. Copying
166
159
  * the sequence instead of calling it would create a second oracle over the same fact, and two oracles
package/dist/auth.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  readSecureJson,
3
3
  writeSecureJson
4
- } from "./chunk-D2EFYZBV.js";
4
+ } from "./chunk-4ULFP7GK.js";
5
5
  import {
6
6
  debugLog
7
7
  } from "./chunk-M5J3Q6YC.js";
8
8
  import {
9
9
  ConfigurationError
10
- } from "./chunk-U72XTMYB.js";
10
+ } from "./chunk-Q65ZIFNK.js";
11
11
  import {
12
12
  currentOperatorPolicy
13
13
  } from "./chunk-2SP5BB6U.js";