@letrquan/book 0.2.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 (103) hide show
  1. package/CHANGELOG.md +2504 -0
  2. package/LICENSE +123 -0
  3. package/README.md +1383 -0
  4. package/dist/app-MOXXWEOY.js +18945 -0
  5. package/dist/app-MOXXWEOY.js.map +1 -0
  6. package/dist/chunk-24AXE6SP.js +12 -0
  7. package/dist/chunk-24AXE6SP.js.map +1 -0
  8. package/dist/chunk-3U5IBM24.js +213 -0
  9. package/dist/chunk-3U5IBM24.js.map +1 -0
  10. package/dist/chunk-574C6MQR.js +743 -0
  11. package/dist/chunk-574C6MQR.js.map +1 -0
  12. package/dist/chunk-5GDZ22YP.js +449 -0
  13. package/dist/chunk-5GDZ22YP.js.map +1 -0
  14. package/dist/chunk-5KLVH3PY.js +167 -0
  15. package/dist/chunk-5KLVH3PY.js.map +1 -0
  16. package/dist/chunk-5RDRAO4B.js +92 -0
  17. package/dist/chunk-5RDRAO4B.js.map +1 -0
  18. package/dist/chunk-7N4J557C.js +10 -0
  19. package/dist/chunk-7N4J557C.js.map +1 -0
  20. package/dist/chunk-A7JLBK2Y.js +3506 -0
  21. package/dist/chunk-A7JLBK2Y.js.map +1 -0
  22. package/dist/chunk-ANSJPYGI.js +84 -0
  23. package/dist/chunk-ANSJPYGI.js.map +1 -0
  24. package/dist/chunk-BTZZUCLI.js +264 -0
  25. package/dist/chunk-BTZZUCLI.js.map +1 -0
  26. package/dist/chunk-C7D4NJ2D.js +217 -0
  27. package/dist/chunk-C7D4NJ2D.js.map +1 -0
  28. package/dist/chunk-CAGOB2N4.js +208 -0
  29. package/dist/chunk-CAGOB2N4.js.map +1 -0
  30. package/dist/chunk-DGMQEI6Y.js +98 -0
  31. package/dist/chunk-DGMQEI6Y.js.map +1 -0
  32. package/dist/chunk-E75IPST5.js +289 -0
  33. package/dist/chunk-E75IPST5.js.map +1 -0
  34. package/dist/chunk-EYGD6X2T.js +180 -0
  35. package/dist/chunk-EYGD6X2T.js.map +1 -0
  36. package/dist/chunk-GTVB4HEM.js +765 -0
  37. package/dist/chunk-GTVB4HEM.js.map +1 -0
  38. package/dist/chunk-J7MIZ5CA.js +1907 -0
  39. package/dist/chunk-J7MIZ5CA.js.map +1 -0
  40. package/dist/chunk-KWHQ2XXL.js +626 -0
  41. package/dist/chunk-KWHQ2XXL.js.map +1 -0
  42. package/dist/chunk-L6LK2QIQ.js +657 -0
  43. package/dist/chunk-L6LK2QIQ.js.map +1 -0
  44. package/dist/chunk-MAQXCUR4.js +1007 -0
  45. package/dist/chunk-MAQXCUR4.js.map +1 -0
  46. package/dist/chunk-S4XL7HOM.js +402 -0
  47. package/dist/chunk-S4XL7HOM.js.map +1 -0
  48. package/dist/chunk-URLQAXTR.js +60 -0
  49. package/dist/chunk-URLQAXTR.js.map +1 -0
  50. package/dist/chunk-V4BOYR52.js +3504 -0
  51. package/dist/chunk-V4BOYR52.js.map +1 -0
  52. package/dist/chunk-VZQXX3WW.js +210 -0
  53. package/dist/chunk-VZQXX3WW.js.map +1 -0
  54. package/dist/chunk-WKBO5R4O.js +34 -0
  55. package/dist/chunk-WKBO5R4O.js.map +1 -0
  56. package/dist/chunk-XIFFTATU.js +15753 -0
  57. package/dist/chunk-XIFFTATU.js.map +1 -0
  58. package/dist/command-approvals-6Y57VCWZ.js +28 -0
  59. package/dist/command-approvals-6Y57VCWZ.js.map +1 -0
  60. package/dist/context-ZC5IFEAX.js +25 -0
  61. package/dist/context-ZC5IFEAX.js.map +1 -0
  62. package/dist/hook-approvals-CTUFKEJT.js +22 -0
  63. package/dist/hook-approvals-CTUFKEJT.js.map +1 -0
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.js +2582 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/interactive-assets-CCAAWVKH.js +31 -0
  68. package/dist/interactive-assets-CCAAWVKH.js.map +1 -0
  69. package/dist/job-runner.d.ts +2 -0
  70. package/dist/job-runner.js +235 -0
  71. package/dist/job-runner.js.map +1 -0
  72. package/dist/loader-4IDS2RIE.js +15 -0
  73. package/dist/loader-4IDS2RIE.js.map +1 -0
  74. package/dist/mcp-approvals-WNPH65BK.js +18 -0
  75. package/dist/mcp-approvals-WNPH65BK.js.map +1 -0
  76. package/dist/mcp-config-YIJIES2C.js +16 -0
  77. package/dist/mcp-config-YIJIES2C.js.map +1 -0
  78. package/dist/model-window-store-YFVNXCIP.js +27 -0
  79. package/dist/model-window-store-YFVNXCIP.js.map +1 -0
  80. package/dist/openai-compatible-RHZUBHV6.js +10 -0
  81. package/dist/openai-compatible-RHZUBHV6.js.map +1 -0
  82. package/dist/permission-approvals-76PVREFD.js +14 -0
  83. package/dist/permission-approvals-76PVREFD.js.map +1 -0
  84. package/dist/permissions-CHCKTFQQ.js +28 -0
  85. package/dist/permissions-CHCKTFQQ.js.map +1 -0
  86. package/dist/sandbox-CNVAQDA7.js +23 -0
  87. package/dist/sandbox-CNVAQDA7.js.map +1 -0
  88. package/dist/scrollback-LUKLIBVI.js +136 -0
  89. package/dist/scrollback-LUKLIBVI.js.map +1 -0
  90. package/dist/sdk.d.ts +4574 -0
  91. package/dist/sdk.js +665 -0
  92. package/dist/sdk.js.map +1 -0
  93. package/dist/settings-loader-M2QP5TX2.js +21 -0
  94. package/dist/settings-loader-M2QP5TX2.js.map +1 -0
  95. package/dist/settings-removed-JWWPT4WA.js +19 -0
  96. package/dist/settings-removed-JWWPT4WA.js.map +1 -0
  97. package/dist/shell-selection-V6QAYXPX.js +17 -0
  98. package/dist/shell-selection-V6QAYXPX.js.map +1 -0
  99. package/package.json +143 -0
  100. package/patches/ink+6.8.0.patch +13 -0
  101. package/scripts/apply-ink-patch.mjs +29 -0
  102. package/scripts/ink-patch.mjs +59 -0
  103. package/scripts/verify-ink-patch.mjs +11 -0
package/dist/sdk.d.ts ADDED
@@ -0,0 +1,4574 @@
1
+ import { ChildProcess } from 'child_process';
2
+ import { z } from 'zod';
3
+ import { ChildProcess as ChildProcess$1 } from 'node:child_process';
4
+ import { closeSync, existsSync, fsyncSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
5
+
6
+ /**
7
+ * Time that measures, separated from time that labels.
8
+ *
9
+ * `Date.now()` is the settable wall clock. NTP steps it, a VM resumes with it
10
+ * wrong and then corrects, an operator fixes a drifted host, DST changes the
11
+ * offset. None of that matters over a five-minute chat, and all of it is
12
+ * near-certain over the multi-day runs Book is built for — which is why every
13
+ * *duration* decision here reads a monotonic source instead.
14
+ *
15
+ * What the two are for:
16
+ *
17
+ * - **`monotonicNowMs()`** — "how long has this been going?" Retry budgets,
18
+ * flush deadlines, kill timeouts, elapsed run time. It only ever moves
19
+ * forward, at a rate nothing can adjust, and its origin is arbitrary, so a
20
+ * reading is meaningless on its own and comparable only to another reading
21
+ * *from the same process*.
22
+ * - **`wallNowMs()`** — "when did this happen?" Anything written to a file, put
23
+ * on screen, or compared against a stamp another process wrote. Still
24
+ * `Date.now()`; the name exists so the choice is visible at the call site
25
+ * rather than implied by its absence.
26
+ *
27
+ * **The origin is per-process, and that is the whole limit.** Two processes'
28
+ * monotonic clocks share no zero, so a monotonic reading cannot be persisted,
29
+ * sent, or compared across a process boundary. Cross-process liveness — the
30
+ * background-shell heartbeat, the run-status file `book status` reads, the
31
+ * retention sweeps — therefore stays on the wall clock by necessity, not by
32
+ * oversight. A backwards step there reads as "unexpectedly fresh" and a forward
33
+ * step can declare a live run stale; fixing that needs a sequence counter in the
34
+ * file, not a better clock. See `MILESTONES.md`.
35
+ *
36
+ * Injected rather than imported directly at the decision sites, per the
37
+ * module-level-mutable-state rule: a caller that needs to control time passes a
38
+ * `Clock`, and tests do exactly that instead of reaching for fake timers.
39
+ *
40
+ * One trap worth knowing when writing those tests: vitest's default
41
+ * `vi.useFakeTimers()` fakes `performance` too, so the monotonic clock stops
42
+ * with the fake wall clock and a test cannot tell the two apart. Use
43
+ * `vi.useFakeTimers({ toFake: ['Date'] })` to move the wall clock while the
44
+ * monotonic one keeps its own time — that divergence is the thing under test.
45
+ */
46
+ /** A monotonic reading, in milliseconds. Only comparable within one process. */
47
+ type MonotonicMs = number;
48
+ interface Clock {
49
+ /** Milliseconds since an arbitrary fixed origin. Never goes backwards. */
50
+ monotonicNowMs(): MonotonicMs;
51
+ /** Milliseconds since the Unix epoch. Settable; use only for labels. */
52
+ wallNowMs(): number;
53
+ }
54
+
55
+ declare const providerModelSchema: z.ZodObject<{
56
+ label: z.ZodOptional<z.ZodString>;
57
+ contextWindow: z.ZodOptional<z.ZodNumber>;
58
+ maxOutputTokens: z.ZodOptional<z.ZodNumber>;
59
+ /** Whether this model accepts image input. Unknown models remain optimistic. */
60
+ vision: z.ZodOptional<z.ZodBoolean>;
61
+ /**
62
+ * Entered by hand rather than returned by the endpoint's model-list API.
63
+ * Refreshing the catalog keeps these, since they exist precisely because
64
+ * discovery does not list them.
65
+ */
66
+ manual: z.ZodOptional<z.ZodBoolean>;
67
+ /** Mutation-tool preference for this model; defaults to a family-level prior. */
68
+ editFormat: z.ZodOptional<z.ZodEnum<["patch", "replace", "whole"]>>;
69
+ effort: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<false>, z.ZodObject<{
70
+ default: z.ZodOptional<z.ZodEnum<["low", "medium", "high", "xhigh", "max"]>>;
71
+ levels: z.ZodOptional<z.ZodArray<z.ZodEnum<["low", "medium", "high", "xhigh", "max"]>, "many">>;
72
+ }, "strip", z.ZodTypeAny, {
73
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
74
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
75
+ }, {
76
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
77
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
78
+ }>]>>;
79
+ }, "strip", z.ZodTypeAny, {
80
+ effort?: false | {
81
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
82
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
83
+ } | undefined;
84
+ label?: string | undefined;
85
+ contextWindow?: number | undefined;
86
+ maxOutputTokens?: number | undefined;
87
+ vision?: boolean | undefined;
88
+ manual?: boolean | undefined;
89
+ editFormat?: "patch" | "replace" | "whole" | undefined;
90
+ }, {
91
+ effort?: false | {
92
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
93
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
94
+ } | undefined;
95
+ label?: string | undefined;
96
+ contextWindow?: number | undefined;
97
+ maxOutputTokens?: number | undefined;
98
+ vision?: boolean | undefined;
99
+ manual?: boolean | undefined;
100
+ editFormat?: "patch" | "replace" | "whole" | undefined;
101
+ }>;
102
+ declare const toolDiscoverySettingsSchema: z.ZodObject<{
103
+ mode: z.ZodDefault<z.ZodEnum<["auto", "eager", "deferred"]>>;
104
+ eagerToolCount: z.ZodDefault<z.ZodNumber>;
105
+ schemaTokenBudget: z.ZodDefault<z.ZodNumber>;
106
+ maxLoadedTools: z.ZodDefault<z.ZodNumber>;
107
+ searchLimit: z.ZodDefault<z.ZodNumber>;
108
+ }, "strip", z.ZodTypeAny, {
109
+ mode: "auto" | "eager" | "deferred";
110
+ eagerToolCount: number;
111
+ schemaTokenBudget: number;
112
+ maxLoadedTools: number;
113
+ searchLimit: number;
114
+ }, {
115
+ mode?: "auto" | "eager" | "deferred" | undefined;
116
+ eagerToolCount?: number | undefined;
117
+ schemaTokenBudget?: number | undefined;
118
+ maxLoadedTools?: number | undefined;
119
+ searchLimit?: number | undefined;
120
+ }>;
121
+ type ToolDiscoverySettings = z.infer<typeof toolDiscoverySettingsSchema>;
122
+ declare const skillActivationSchema: z.ZodEnum<["auto", "name-only", "manual", "off"]>;
123
+ type SkillActivation = z.infer<typeof skillActivationSchema>;
124
+ declare const skillExecutionSchema: z.ZodEnum<["inherit", "ask", "deny"]>;
125
+ type SkillExecution = z.infer<typeof skillExecutionSchema>;
126
+ declare const skillSettingsSchema: z.ZodObject<{
127
+ /** Emergency switch that removes skill prompt and runtime effects without deleting packages. */
128
+ enabled: z.ZodDefault<z.ZodBoolean>;
129
+ /** Per-skill visibility overrides keyed by the skill's declared name. */
130
+ overrides: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["auto", "name-only", "manual", "off"]>>>;
131
+ /** Per-skill activation consent policy. Tool calls still use the normal permission system. */
132
+ execution: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["inherit", "ask", "deny"]>>>;
133
+ }, "strip", z.ZodTypeAny, {
134
+ enabled: boolean;
135
+ overrides: Record<string, "manual" | "off" | "auto" | "name-only">;
136
+ execution: Record<string, "ask" | "deny" | "inherit">;
137
+ }, {
138
+ enabled?: boolean | undefined;
139
+ overrides?: Record<string, "manual" | "off" | "auto" | "name-only"> | undefined;
140
+ execution?: Record<string, "ask" | "deny" | "inherit"> | undefined;
141
+ }>;
142
+ type SkillSettings = z.infer<typeof skillSettingsSchema>;
143
+ type ProviderModelConfig = z.infer<typeof providerModelSchema>;
144
+ /** The supported production strategy. */
145
+ declare const compactStrategySchema: z.ZodLiteral<"summary">;
146
+ type CompactStrategy = z.infer<typeof compactStrategySchema>;
147
+ /**
148
+ * Full settings.json schema — all keys that book supports.
149
+ * New keys added in later milestones extend this schema.
150
+ */
151
+ declare const bookSettingsSchema: z.ZodObject<{
152
+ model: z.ZodOptional<z.ZodString>;
153
+ /** Default strategy used to reduce historical conversation context. */
154
+ compactStrategy: z.ZodOptional<z.ZodLiteral<"summary">>;
155
+ /** Optional model used only to generate historical conversation checkpoints. */
156
+ compactModel: z.ZodOptional<z.ZodString>;
157
+ /** Max agent turns per user message. Omit for unlimited. */
158
+ maxTurns: z.ZodOptional<z.ZodNumber>;
159
+ maxTokens: z.ZodOptional<z.ZodNumber>;
160
+ effort: z.ZodOptional<z.ZodEnum<["low", "medium", "high", "xhigh", "max"]>>;
161
+ /** TUI color theme: apple (default), dark, light, auto, a bundled palette, or a custom theme filename. */
162
+ theme: z.ZodOptional<z.ZodString>;
163
+ /**
164
+ * The shell the Bash tool spawns: `bash`, `pwsh`, `powershell`, `cmd`, `sh`,
165
+ * or an executable path. Honoured from the user-global layer, an explicit
166
+ * `--settings` document, or `BOOK_SHELL` only: a workspace file cannot pick
167
+ * the program every command is handed to.
168
+ */
169
+ shell: z.ZodOptional<z.ZodString>;
170
+ ui: z.ZodDefault<z.ZodObject<{
171
+ /** Show provider-native and embedded model reasoning in the interactive transcript. */
172
+ showThinking: z.ZodDefault<z.ZodBoolean>;
173
+ /** Play the full-screen fire sequence on a newly created interactive startup session. */
174
+ startupAnimation: z.ZodDefault<z.ZodBoolean>;
175
+ }, "strip", z.ZodTypeAny, {
176
+ showThinking: boolean;
177
+ startupAnimation: boolean;
178
+ }, {
179
+ showThinking?: boolean | undefined;
180
+ startupAnimation?: boolean | undefined;
181
+ }>>;
182
+ skills: z.ZodDefault<z.ZodObject<{
183
+ /** Emergency switch that removes skill prompt and runtime effects without deleting packages. */
184
+ enabled: z.ZodDefault<z.ZodBoolean>;
185
+ /** Per-skill visibility overrides keyed by the skill's declared name. */
186
+ overrides: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["auto", "name-only", "manual", "off"]>>>;
187
+ /** Per-skill activation consent policy. Tool calls still use the normal permission system. */
188
+ execution: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["inherit", "ask", "deny"]>>>;
189
+ }, "strip", z.ZodTypeAny, {
190
+ enabled: boolean;
191
+ overrides: Record<string, "manual" | "off" | "auto" | "name-only">;
192
+ execution: Record<string, "ask" | "deny" | "inherit">;
193
+ }, {
194
+ enabled?: boolean | undefined;
195
+ overrides?: Record<string, "manual" | "off" | "auto" | "name-only"> | undefined;
196
+ execution?: Record<string, "ask" | "deny" | "inherit"> | undefined;
197
+ }>>;
198
+ autoCompactEnabled: z.ZodOptional<z.ZodBoolean>;
199
+ /** Permission mode used by each host when no invocation-specific mode is supplied. */
200
+ defaultMode: z.ZodOptional<z.ZodEnum<["default", "acceptEdits", "plan", "auto", "dontAsk", "bypassPermissions"]>>;
201
+ disableBypassPermissionsMode: z.ZodOptional<z.ZodBoolean>;
202
+ additionalDirectories: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
203
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
204
+ provider: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
205
+ type: z.ZodDefault<z.ZodEnum<["openai", "anthropic"]>>;
206
+ baseURL: z.ZodOptional<z.ZodString>;
207
+ baseUrl: z.ZodOptional<z.ZodString>;
208
+ apiKey: z.ZodOptional<z.ZodString>;
209
+ models: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
210
+ label: z.ZodOptional<z.ZodString>;
211
+ contextWindow: z.ZodOptional<z.ZodNumber>;
212
+ maxOutputTokens: z.ZodOptional<z.ZodNumber>;
213
+ /** Whether this model accepts image input. Unknown models remain optimistic. */
214
+ vision: z.ZodOptional<z.ZodBoolean>;
215
+ /**
216
+ * Entered by hand rather than returned by the endpoint's model-list API.
217
+ * Refreshing the catalog keeps these, since they exist precisely because
218
+ * discovery does not list them.
219
+ */
220
+ manual: z.ZodOptional<z.ZodBoolean>;
221
+ /** Mutation-tool preference for this model; defaults to a family-level prior. */
222
+ editFormat: z.ZodOptional<z.ZodEnum<["patch", "replace", "whole"]>>;
223
+ effort: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<false>, z.ZodObject<{
224
+ default: z.ZodOptional<z.ZodEnum<["low", "medium", "high", "xhigh", "max"]>>;
225
+ levels: z.ZodOptional<z.ZodArray<z.ZodEnum<["low", "medium", "high", "xhigh", "max"]>, "many">>;
226
+ }, "strip", z.ZodTypeAny, {
227
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
228
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
229
+ }, {
230
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
231
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
232
+ }>]>>;
233
+ }, "strip", z.ZodTypeAny, {
234
+ effort?: false | {
235
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
236
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
237
+ } | undefined;
238
+ label?: string | undefined;
239
+ contextWindow?: number | undefined;
240
+ maxOutputTokens?: number | undefined;
241
+ vision?: boolean | undefined;
242
+ manual?: boolean | undefined;
243
+ editFormat?: "patch" | "replace" | "whole" | undefined;
244
+ }, {
245
+ effort?: false | {
246
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
247
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
248
+ } | undefined;
249
+ label?: string | undefined;
250
+ contextWindow?: number | undefined;
251
+ maxOutputTokens?: number | undefined;
252
+ vision?: boolean | undefined;
253
+ manual?: boolean | undefined;
254
+ editFormat?: "patch" | "replace" | "whole" | undefined;
255
+ }>>>;
256
+ }, "strip", z.ZodTypeAny, {
257
+ type: "openai" | "anthropic";
258
+ models: Record<string, {
259
+ effort?: false | {
260
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
261
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
262
+ } | undefined;
263
+ label?: string | undefined;
264
+ contextWindow?: number | undefined;
265
+ maxOutputTokens?: number | undefined;
266
+ vision?: boolean | undefined;
267
+ manual?: boolean | undefined;
268
+ editFormat?: "patch" | "replace" | "whole" | undefined;
269
+ }>;
270
+ baseURL?: string | undefined;
271
+ baseUrl?: string | undefined;
272
+ apiKey?: string | undefined;
273
+ }, {
274
+ type?: "openai" | "anthropic" | undefined;
275
+ baseURL?: string | undefined;
276
+ baseUrl?: string | undefined;
277
+ apiKey?: string | undefined;
278
+ models?: Record<string, {
279
+ effort?: false | {
280
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
281
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
282
+ } | undefined;
283
+ label?: string | undefined;
284
+ contextWindow?: number | undefined;
285
+ maxOutputTokens?: number | undefined;
286
+ vision?: boolean | undefined;
287
+ manual?: boolean | undefined;
288
+ editFormat?: "patch" | "replace" | "whole" | undefined;
289
+ }> | undefined;
290
+ }>>>;
291
+ permissions: z.ZodDefault<z.ZodObject<{
292
+ allow: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
293
+ ask: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
294
+ deny: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
295
+ /**
296
+ * Decisions about `allow` rules the project layer declared. A project rule
297
+ * with no `approved` entry here never reaches the resolved allow list.
298
+ */
299
+ projectAllowRules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["approved", "rejected"]>>>;
300
+ }, "strip", z.ZodTypeAny, {
301
+ allow: string[];
302
+ ask: string[];
303
+ deny: string[];
304
+ projectAllowRules: Record<string, "approved" | "rejected">;
305
+ }, {
306
+ allow?: string[] | undefined;
307
+ ask?: string[] | undefined;
308
+ deny?: string[] | undefined;
309
+ projectAllowRules?: Record<string, "approved" | "rejected"> | undefined;
310
+ }>>;
311
+ sandbox: z.ZodDefault<z.ZodObject<{
312
+ enabled: z.ZodDefault<z.ZodBoolean>;
313
+ failIfUnavailable: z.ZodDefault<z.ZodBoolean>;
314
+ autoAllowBashIfSandboxed: z.ZodDefault<z.ZodBoolean>;
315
+ excludedCommands: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
316
+ allowUnsandboxedCommands: z.ZodDefault<z.ZodBoolean>;
317
+ filesystem: z.ZodDefault<z.ZodObject<{
318
+ allowWrite: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
319
+ denyWrite: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
320
+ denyRead: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
321
+ }, "strip", z.ZodTypeAny, {
322
+ allowWrite: string[];
323
+ denyWrite: string[];
324
+ denyRead: string[];
325
+ }, {
326
+ allowWrite?: string[] | undefined;
327
+ denyWrite?: string[] | undefined;
328
+ denyRead?: string[] | undefined;
329
+ }>>;
330
+ network: z.ZodDefault<z.ZodObject<{
331
+ allowedDomains: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
332
+ deniedDomains: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
333
+ }, "strip", z.ZodTypeAny, {
334
+ allowedDomains: string[];
335
+ deniedDomains: string[];
336
+ }, {
337
+ allowedDomains?: string[] | undefined;
338
+ deniedDomains?: string[] | undefined;
339
+ }>>;
340
+ }, "strip", z.ZodTypeAny, {
341
+ enabled: boolean;
342
+ failIfUnavailable: boolean;
343
+ autoAllowBashIfSandboxed: boolean;
344
+ excludedCommands: string[];
345
+ allowUnsandboxedCommands: boolean;
346
+ filesystem: {
347
+ allowWrite: string[];
348
+ denyWrite: string[];
349
+ denyRead: string[];
350
+ };
351
+ network: {
352
+ allowedDomains: string[];
353
+ deniedDomains: string[];
354
+ };
355
+ }, {
356
+ enabled?: boolean | undefined;
357
+ failIfUnavailable?: boolean | undefined;
358
+ autoAllowBashIfSandboxed?: boolean | undefined;
359
+ excludedCommands?: string[] | undefined;
360
+ allowUnsandboxedCommands?: boolean | undefined;
361
+ filesystem?: {
362
+ allowWrite?: string[] | undefined;
363
+ denyWrite?: string[] | undefined;
364
+ denyRead?: string[] | undefined;
365
+ } | undefined;
366
+ network?: {
367
+ allowedDomains?: string[] | undefined;
368
+ deniedDomains?: string[] | undefined;
369
+ } | undefined;
370
+ }>>;
371
+ hooks: z.ZodDefault<z.ZodObject<{
372
+ /**
373
+ * Decisions about hook entries the checked-in project layer declared. An
374
+ * entry whose fingerprint has no `approved` record here never reaches the
375
+ * resolved hooks.
376
+ */
377
+ projectEntries: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<["approved", "rejected"]>>>;
378
+ SessionStart: z.ZodDefault<z.ZodArray<z.ZodObject<{
379
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
380
+ matcher: z.ZodOptional<z.ZodString>;
381
+ /** Shell command to run (passed through system shell). */
382
+ command: z.ZodString;
383
+ /** Extra environment variables for this hook. */
384
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
385
+ }, "strip", z.ZodTypeAny, {
386
+ command: string;
387
+ env: Record<string, string>;
388
+ matcher?: string | undefined;
389
+ }, {
390
+ command: string;
391
+ matcher?: string | undefined;
392
+ env?: Record<string, string> | undefined;
393
+ }>, "many">>;
394
+ SessionEnd: z.ZodDefault<z.ZodArray<z.ZodObject<{
395
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
396
+ matcher: z.ZodOptional<z.ZodString>;
397
+ /** Shell command to run (passed through system shell). */
398
+ command: z.ZodString;
399
+ /** Extra environment variables for this hook. */
400
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
401
+ }, "strip", z.ZodTypeAny, {
402
+ command: string;
403
+ env: Record<string, string>;
404
+ matcher?: string | undefined;
405
+ }, {
406
+ command: string;
407
+ matcher?: string | undefined;
408
+ env?: Record<string, string> | undefined;
409
+ }>, "many">>;
410
+ UserPromptSubmit: z.ZodDefault<z.ZodArray<z.ZodObject<{
411
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
412
+ matcher: z.ZodOptional<z.ZodString>;
413
+ /** Shell command to run (passed through system shell). */
414
+ command: z.ZodString;
415
+ /** Extra environment variables for this hook. */
416
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
417
+ }, "strip", z.ZodTypeAny, {
418
+ command: string;
419
+ env: Record<string, string>;
420
+ matcher?: string | undefined;
421
+ }, {
422
+ command: string;
423
+ matcher?: string | undefined;
424
+ env?: Record<string, string> | undefined;
425
+ }>, "many">>;
426
+ PreToolUse: z.ZodDefault<z.ZodArray<z.ZodObject<{
427
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
428
+ matcher: z.ZodOptional<z.ZodString>;
429
+ /** Shell command to run (passed through system shell). */
430
+ command: z.ZodString;
431
+ /** Extra environment variables for this hook. */
432
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
433
+ }, "strip", z.ZodTypeAny, {
434
+ command: string;
435
+ env: Record<string, string>;
436
+ matcher?: string | undefined;
437
+ }, {
438
+ command: string;
439
+ matcher?: string | undefined;
440
+ env?: Record<string, string> | undefined;
441
+ }>, "many">>;
442
+ PostToolUse: z.ZodDefault<z.ZodArray<z.ZodObject<{
443
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
444
+ matcher: z.ZodOptional<z.ZodString>;
445
+ /** Shell command to run (passed through system shell). */
446
+ command: z.ZodString;
447
+ /** Extra environment variables for this hook. */
448
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
449
+ }, "strip", z.ZodTypeAny, {
450
+ command: string;
451
+ env: Record<string, string>;
452
+ matcher?: string | undefined;
453
+ }, {
454
+ command: string;
455
+ matcher?: string | undefined;
456
+ env?: Record<string, string> | undefined;
457
+ }>, "many">>;
458
+ Stop: z.ZodDefault<z.ZodArray<z.ZodObject<{
459
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
460
+ matcher: z.ZodOptional<z.ZodString>;
461
+ /** Shell command to run (passed through system shell). */
462
+ command: z.ZodString;
463
+ /** Extra environment variables for this hook. */
464
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
465
+ }, "strip", z.ZodTypeAny, {
466
+ command: string;
467
+ env: Record<string, string>;
468
+ matcher?: string | undefined;
469
+ }, {
470
+ command: string;
471
+ matcher?: string | undefined;
472
+ env?: Record<string, string> | undefined;
473
+ }>, "many">>;
474
+ PreCompact: z.ZodDefault<z.ZodArray<z.ZodObject<{
475
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
476
+ matcher: z.ZodOptional<z.ZodString>;
477
+ /** Shell command to run (passed through system shell). */
478
+ command: z.ZodString;
479
+ /** Extra environment variables for this hook. */
480
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
481
+ }, "strip", z.ZodTypeAny, {
482
+ command: string;
483
+ env: Record<string, string>;
484
+ matcher?: string | undefined;
485
+ }, {
486
+ command: string;
487
+ matcher?: string | undefined;
488
+ env?: Record<string, string> | undefined;
489
+ }>, "many">>;
490
+ PostCompact: z.ZodDefault<z.ZodArray<z.ZodObject<{
491
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
492
+ matcher: z.ZodOptional<z.ZodString>;
493
+ /** Shell command to run (passed through system shell). */
494
+ command: z.ZodString;
495
+ /** Extra environment variables for this hook. */
496
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
497
+ }, "strip", z.ZodTypeAny, {
498
+ command: string;
499
+ env: Record<string, string>;
500
+ matcher?: string | undefined;
501
+ }, {
502
+ command: string;
503
+ matcher?: string | undefined;
504
+ env?: Record<string, string> | undefined;
505
+ }>, "many">>;
506
+ SubagentStart: z.ZodDefault<z.ZodArray<z.ZodObject<{
507
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
508
+ matcher: z.ZodOptional<z.ZodString>;
509
+ /** Shell command to run (passed through system shell). */
510
+ command: z.ZodString;
511
+ /** Extra environment variables for this hook. */
512
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
513
+ }, "strip", z.ZodTypeAny, {
514
+ command: string;
515
+ env: Record<string, string>;
516
+ matcher?: string | undefined;
517
+ }, {
518
+ command: string;
519
+ matcher?: string | undefined;
520
+ env?: Record<string, string> | undefined;
521
+ }>, "many">>;
522
+ SubagentStop: z.ZodDefault<z.ZodArray<z.ZodObject<{
523
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
524
+ matcher: z.ZodOptional<z.ZodString>;
525
+ /** Shell command to run (passed through system shell). */
526
+ command: z.ZodString;
527
+ /** Extra environment variables for this hook. */
528
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
529
+ }, "strip", z.ZodTypeAny, {
530
+ command: string;
531
+ env: Record<string, string>;
532
+ matcher?: string | undefined;
533
+ }, {
534
+ command: string;
535
+ matcher?: string | undefined;
536
+ env?: Record<string, string> | undefined;
537
+ }>, "many">>;
538
+ Notification: z.ZodDefault<z.ZodArray<z.ZodObject<{
539
+ /** Tool(specifier) pattern to filter which events trigger this hook. */
540
+ matcher: z.ZodOptional<z.ZodString>;
541
+ /** Shell command to run (passed through system shell). */
542
+ command: z.ZodString;
543
+ /** Extra environment variables for this hook. */
544
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
545
+ }, "strip", z.ZodTypeAny, {
546
+ command: string;
547
+ env: Record<string, string>;
548
+ matcher?: string | undefined;
549
+ }, {
550
+ command: string;
551
+ matcher?: string | undefined;
552
+ env?: Record<string, string> | undefined;
553
+ }>, "many">>;
554
+ }, "strip", z.ZodTypeAny, {
555
+ SessionStart: {
556
+ command: string;
557
+ env: Record<string, string>;
558
+ matcher?: string | undefined;
559
+ }[];
560
+ SessionEnd: {
561
+ command: string;
562
+ env: Record<string, string>;
563
+ matcher?: string | undefined;
564
+ }[];
565
+ UserPromptSubmit: {
566
+ command: string;
567
+ env: Record<string, string>;
568
+ matcher?: string | undefined;
569
+ }[];
570
+ PreToolUse: {
571
+ command: string;
572
+ env: Record<string, string>;
573
+ matcher?: string | undefined;
574
+ }[];
575
+ PostToolUse: {
576
+ command: string;
577
+ env: Record<string, string>;
578
+ matcher?: string | undefined;
579
+ }[];
580
+ Stop: {
581
+ command: string;
582
+ env: Record<string, string>;
583
+ matcher?: string | undefined;
584
+ }[];
585
+ PreCompact: {
586
+ command: string;
587
+ env: Record<string, string>;
588
+ matcher?: string | undefined;
589
+ }[];
590
+ PostCompact: {
591
+ command: string;
592
+ env: Record<string, string>;
593
+ matcher?: string | undefined;
594
+ }[];
595
+ SubagentStart: {
596
+ command: string;
597
+ env: Record<string, string>;
598
+ matcher?: string | undefined;
599
+ }[];
600
+ SubagentStop: {
601
+ command: string;
602
+ env: Record<string, string>;
603
+ matcher?: string | undefined;
604
+ }[];
605
+ Notification: {
606
+ command: string;
607
+ env: Record<string, string>;
608
+ matcher?: string | undefined;
609
+ }[];
610
+ projectEntries: Record<string, "approved" | "rejected">;
611
+ }, {
612
+ SessionStart?: {
613
+ command: string;
614
+ matcher?: string | undefined;
615
+ env?: Record<string, string> | undefined;
616
+ }[] | undefined;
617
+ SessionEnd?: {
618
+ command: string;
619
+ matcher?: string | undefined;
620
+ env?: Record<string, string> | undefined;
621
+ }[] | undefined;
622
+ UserPromptSubmit?: {
623
+ command: string;
624
+ matcher?: string | undefined;
625
+ env?: Record<string, string> | undefined;
626
+ }[] | undefined;
627
+ PreToolUse?: {
628
+ command: string;
629
+ matcher?: string | undefined;
630
+ env?: Record<string, string> | undefined;
631
+ }[] | undefined;
632
+ PostToolUse?: {
633
+ command: string;
634
+ matcher?: string | undefined;
635
+ env?: Record<string, string> | undefined;
636
+ }[] | undefined;
637
+ Stop?: {
638
+ command: string;
639
+ matcher?: string | undefined;
640
+ env?: Record<string, string> | undefined;
641
+ }[] | undefined;
642
+ PreCompact?: {
643
+ command: string;
644
+ matcher?: string | undefined;
645
+ env?: Record<string, string> | undefined;
646
+ }[] | undefined;
647
+ PostCompact?: {
648
+ command: string;
649
+ matcher?: string | undefined;
650
+ env?: Record<string, string> | undefined;
651
+ }[] | undefined;
652
+ SubagentStart?: {
653
+ command: string;
654
+ matcher?: string | undefined;
655
+ env?: Record<string, string> | undefined;
656
+ }[] | undefined;
657
+ SubagentStop?: {
658
+ command: string;
659
+ matcher?: string | undefined;
660
+ env?: Record<string, string> | undefined;
661
+ }[] | undefined;
662
+ Notification?: {
663
+ command: string;
664
+ matcher?: string | undefined;
665
+ env?: Record<string, string> | undefined;
666
+ }[] | undefined;
667
+ projectEntries?: Record<string, "approved" | "rejected"> | undefined;
668
+ }>>;
669
+ retry: z.ZodDefault<z.ZodObject<{
670
+ maxAttempts: z.ZodDefault<z.ZodNumber>;
671
+ baseDelayMs: z.ZodDefault<z.ZodNumber>;
672
+ maxDelayMs: z.ZodDefault<z.ZodNumber>;
673
+ totalBudgetMs: z.ZodDefault<z.ZodNumber>;
674
+ requestTimeoutMs: z.ZodDefault<z.ZodNumber>;
675
+ streamStallTimeoutMs: z.ZodDefault<z.ZodNumber>;
676
+ /**
677
+ * Stall ceiling while the model is thinking, which is a different regime.
678
+ *
679
+ * `streamStallTimeoutMs` is tuned for a chat: 20s of silence means something
680
+ * broke. But with adaptive thinking on — the default for every Opus and Sonnet
681
+ * model here, at `high` effort unless told otherwise — a long quiet stretch
682
+ * before the first token is the model working, not a fault. Applying the chat
683
+ * timeout to it cancels a healthy request and reports `stream_stall`.
684
+ */
685
+ thinkingStallTimeoutMs: z.ZodDefault<z.ZodNumber>;
686
+ toolRetries: z.ZodDefault<z.ZodNumber>;
687
+ watchdog: z.ZodDefault<z.ZodBoolean>;
688
+ /**
689
+ * How many times a turn may be re-sent after a transport fault that left the
690
+ * work intact — a stalled stream, a dropped socket.
691
+ *
692
+ * `maxAttempts` covers connection setup only; once a 200 response is streaming
693
+ * it is out of scope, so without this a single 20-second provider silence ended
694
+ * the run. Set to 0 to restore that behavior exactly.
695
+ */
696
+ streamReissueAttempts: z.ZodDefault<z.ZodNumber>;
697
+ /**
698
+ * Separate allowance for continuing after the provider's output cap. A large
699
+ * generated file legitimately hits the cap on consecutive turns, and sharing
700
+ * the transport budget would leave nothing for a real socket drop afterwards.
701
+ */
702
+ outputCapContinuations: z.ZodDefault<z.ZodNumber>;
703
+ }, "strip", z.ZodTypeAny, {
704
+ maxAttempts: number;
705
+ baseDelayMs: number;
706
+ maxDelayMs: number;
707
+ totalBudgetMs: number;
708
+ requestTimeoutMs: number;
709
+ streamStallTimeoutMs: number;
710
+ thinkingStallTimeoutMs: number;
711
+ toolRetries: number;
712
+ watchdog: boolean;
713
+ streamReissueAttempts: number;
714
+ outputCapContinuations: number;
715
+ }, {
716
+ maxAttempts?: number | undefined;
717
+ baseDelayMs?: number | undefined;
718
+ maxDelayMs?: number | undefined;
719
+ totalBudgetMs?: number | undefined;
720
+ requestTimeoutMs?: number | undefined;
721
+ streamStallTimeoutMs?: number | undefined;
722
+ thinkingStallTimeoutMs?: number | undefined;
723
+ toolRetries?: number | undefined;
724
+ watchdog?: boolean | undefined;
725
+ streamReissueAttempts?: number | undefined;
726
+ outputCapContinuations?: number | undefined;
727
+ }>>;
728
+ memory: z.ZodDefault<z.ZodObject<{
729
+ enabled: z.ZodDefault<z.ZodBoolean>;
730
+ autoSave: z.ZodDefault<z.ZodBoolean>;
731
+ requireApproval: z.ZodDefault<z.ZodBoolean>;
732
+ }, "strip", z.ZodTypeAny, {
733
+ enabled: boolean;
734
+ autoSave: boolean;
735
+ requireApproval: boolean;
736
+ }, {
737
+ enabled?: boolean | undefined;
738
+ autoSave?: boolean | undefined;
739
+ requireApproval?: boolean | undefined;
740
+ }>>;
741
+ continuation: z.ZodDefault<z.ZodObject<{
742
+ enabled: z.ZodDefault<z.ZodBoolean>;
743
+ /** Consecutive host-authored continuations before the run stops regardless. */
744
+ maxConsecutive: z.ZodDefault<z.ZodNumber>;
745
+ /**
746
+ * Identical progress witnesses in a row before the run stops as no-progress.
747
+ * Minimum 1: a 0 here would fire on the first boundary, before any witness
748
+ * exists, and label a run that never got a chance to spin as spinning.
749
+ */
750
+ noProgressLimit: z.ZodDefault<z.ZodNumber>;
751
+ /**
752
+ * Consecutive turns in which EVERY tool call was refused before the run stops.
753
+ *
754
+ * Enforced even when `enabled` is false, deliberately. The spin it catches
755
+ * predates continuation and needs no continuation to happen: a headless run in
756
+ * the default permission mode answers every prompt `deny`, so the model can
757
+ * re-issue a refused call forever while `toolCalls.length > 0` keeps the
758
+ * turn-end gate from ever firing. None of the existing anti-loop signals see it
759
+ * either — the repeated-failure breaker ignores anything that is not `error`,
760
+ * and `toolCallStats.failures` excludes `blocked` by construction.
761
+ *
762
+ * 0 disables the guard.
763
+ */
764
+ blockedToolTurnLimit: z.ZodDefault<z.ZodNumber>;
765
+ /** Turns between host-authored work-state messages; 0 disables them. */
766
+ planRefreshTurns: z.ZodDefault<z.ZodNumber>;
767
+ /** Wall-clock ceiling for one continued run; 0 means no ceiling. */
768
+ maxWallClockMs: z.ZodDefault<z.ZodNumber>;
769
+ }, "strip", z.ZodTypeAny, {
770
+ enabled: boolean;
771
+ maxConsecutive: number;
772
+ noProgressLimit: number;
773
+ blockedToolTurnLimit: number;
774
+ planRefreshTurns: number;
775
+ maxWallClockMs: number;
776
+ }, {
777
+ enabled?: boolean | undefined;
778
+ maxConsecutive?: number | undefined;
779
+ noProgressLimit?: number | undefined;
780
+ blockedToolTurnLimit?: number | undefined;
781
+ planRefreshTurns?: number | undefined;
782
+ maxWallClockMs?: number | undefined;
783
+ }>>;
784
+ agents: z.ZodDefault<z.ZodObject<{
785
+ mode: z.ZodDefault<z.ZodEnum<["adaptive", "manual", "off"]>>;
786
+ maxConcurrent: z.ZodDefault<z.ZodNumber>;
787
+ maxSpawned: z.ZodDefault<z.ZodNumber>;
788
+ maxDepth: z.ZodDefault<z.ZodLiteral<1>>;
789
+ persist: z.ZodDefault<z.ZodBoolean>;
790
+ includeUntrackedInSnapshot: z.ZodDefault<z.ZodBoolean>;
791
+ telemetry: z.ZodDefault<z.ZodBoolean>;
792
+ retentionDays: z.ZodDefault<z.ZodNumber>;
793
+ checks: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>>;
794
+ /**
795
+ * Wall-clock ceiling for one `Check` run. The 120s default suits a focused
796
+ * suite; a full `npm test` on a large repository routinely exceeds it, and a
797
+ * check that always times out is worse than no check because the timeout was
798
+ * reported as a failing suite. Raise it per project rather than per invocation.
799
+ */
800
+ checkTimeoutMs: z.ZodDefault<z.ZodNumber>;
801
+ /**
802
+ * Simultaneous agent worktrees per repository; 0 disables the check.
803
+ *
804
+ * Nothing reclaims a worktree automatically outside the TUI, and the store's
805
+ * retention sweep runs once at startup with a 30-day default — so on a long
806
+ * unattended run the count only grows. Sized above `maxConcurrent` so ordinary
807
+ * fan-out is unaffected and only accumulation trips it.
808
+ */
809
+ maxWorktrees: z.ZodDefault<z.ZodNumber>;
810
+ /**
811
+ * Re-drive agents that were interrupted by process death on the next start.
812
+ *
813
+ * A restart otherwise converts the whole pending backlog into terminal records
814
+ * nothing picks up, silently losing hours of child work on a wide fan-out. The
815
+ * re-drive is contained: explorers are read-only and patchers/validators run in
816
+ * their own worktree, so nothing reaches the parent workspace without the usual
817
+ * evidence gate. Only `process_exit` interruptions qualify — a user stop stays
818
+ * stopped.
819
+ */
820
+ resumeInterrupted: z.ZodDefault<z.ZodBoolean>;
821
+ /**
822
+ * Refuse a new worktree when free disk would fall below this; 0 disables.
823
+ * Worktrees share the filesystem with the workspace, so exhausting it breaks
824
+ * the root agent's own Edit and Bash, not just the child's.
825
+ */
826
+ minFreeDiskBytes: z.ZodDefault<z.ZodNumber>;
827
+ profiles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
828
+ model: z.ZodOptional<z.ZodString>;
829
+ effort: z.ZodOptional<z.ZodEnum<["low", "medium", "high", "xhigh", "max"]>>;
830
+ maxTurns: z.ZodOptional<z.ZodNumber>;
831
+ color: z.ZodOptional<z.ZodString>;
832
+ }, "strip", z.ZodTypeAny, {
833
+ effort?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
834
+ model?: string | undefined;
835
+ maxTurns?: number | undefined;
836
+ color?: string | undefined;
837
+ }, {
838
+ effort?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
839
+ model?: string | undefined;
840
+ maxTurns?: number | undefined;
841
+ color?: string | undefined;
842
+ }>>>;
843
+ ui: z.ZodDefault<z.ZodObject<{
844
+ enabled: z.ZodDefault<z.ZodBoolean>;
845
+ }, "strip", z.ZodTypeAny, {
846
+ enabled: boolean;
847
+ }, {
848
+ enabled?: boolean | undefined;
849
+ }>>;
850
+ routing: z.ZodDefault<z.ZodObject<{
851
+ inlineSearchBudget: z.ZodDefault<z.ZodNumber>;
852
+ exploreReminder: z.ZodDefault<z.ZodBoolean>;
853
+ }, "strip", z.ZodTypeAny, {
854
+ inlineSearchBudget: number;
855
+ exploreReminder: boolean;
856
+ }, {
857
+ inlineSearchBudget?: number | undefined;
858
+ exploreReminder?: boolean | undefined;
859
+ }>>;
860
+ forwardTextEvents: z.ZodDefault<z.ZodBoolean>;
861
+ }, "strip", z.ZodTypeAny, {
862
+ mode: "manual" | "adaptive" | "off";
863
+ maxConcurrent: number;
864
+ maxSpawned: number;
865
+ maxDepth: 1;
866
+ persist: boolean;
867
+ includeUntrackedInSnapshot: boolean;
868
+ telemetry: boolean;
869
+ retentionDays: number;
870
+ checks: Record<string, string | string[]>;
871
+ checkTimeoutMs: number;
872
+ maxWorktrees: number;
873
+ resumeInterrupted: boolean;
874
+ minFreeDiskBytes: number;
875
+ profiles: Record<string, {
876
+ effort?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
877
+ model?: string | undefined;
878
+ maxTurns?: number | undefined;
879
+ color?: string | undefined;
880
+ }>;
881
+ ui: {
882
+ enabled: boolean;
883
+ };
884
+ routing: {
885
+ inlineSearchBudget: number;
886
+ exploreReminder: boolean;
887
+ };
888
+ forwardTextEvents: boolean;
889
+ }, {
890
+ mode?: "manual" | "adaptive" | "off" | undefined;
891
+ maxConcurrent?: number | undefined;
892
+ maxSpawned?: number | undefined;
893
+ maxDepth?: 1 | undefined;
894
+ persist?: boolean | undefined;
895
+ includeUntrackedInSnapshot?: boolean | undefined;
896
+ telemetry?: boolean | undefined;
897
+ retentionDays?: number | undefined;
898
+ checks?: Record<string, string | string[]> | undefined;
899
+ checkTimeoutMs?: number | undefined;
900
+ maxWorktrees?: number | undefined;
901
+ resumeInterrupted?: boolean | undefined;
902
+ minFreeDiskBytes?: number | undefined;
903
+ profiles?: Record<string, {
904
+ effort?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
905
+ model?: string | undefined;
906
+ maxTurns?: number | undefined;
907
+ color?: string | undefined;
908
+ }> | undefined;
909
+ ui?: {
910
+ enabled?: boolean | undefined;
911
+ } | undefined;
912
+ routing?: {
913
+ inlineSearchBudget?: number | undefined;
914
+ exploreReminder?: boolean | undefined;
915
+ } | undefined;
916
+ forwardTextEvents?: boolean | undefined;
917
+ }>>;
918
+ toolDiscovery: z.ZodDefault<z.ZodObject<{
919
+ mode: z.ZodDefault<z.ZodEnum<["auto", "eager", "deferred"]>>;
920
+ eagerToolCount: z.ZodDefault<z.ZodNumber>;
921
+ schemaTokenBudget: z.ZodDefault<z.ZodNumber>;
922
+ maxLoadedTools: z.ZodDefault<z.ZodNumber>;
923
+ searchLimit: z.ZodDefault<z.ZodNumber>;
924
+ }, "strip", z.ZodTypeAny, {
925
+ mode: "auto" | "eager" | "deferred";
926
+ eagerToolCount: number;
927
+ schemaTokenBudget: number;
928
+ maxLoadedTools: number;
929
+ searchLimit: number;
930
+ }, {
931
+ mode?: "auto" | "eager" | "deferred" | undefined;
932
+ eagerToolCount?: number | undefined;
933
+ schemaTokenBudget?: number | undefined;
934
+ maxLoadedTools?: number | undefined;
935
+ searchLimit?: number | undefined;
936
+ }>>;
937
+ toolExecution: z.ZodDefault<z.ZodObject<{
938
+ /** Shared across the root loop and managed children for parallel-safe tools. */
939
+ maxConcurrent: z.ZodDefault<z.ZodNumber>;
940
+ }, "strip", z.ZodTypeAny, {
941
+ maxConcurrent: number;
942
+ }, {
943
+ maxConcurrent?: number | undefined;
944
+ }>>;
945
+ observability: z.ZodDefault<z.ZodObject<{
946
+ /** Persist a per-tool-call JSONL record for `book tool-stats`. */
947
+ toolTelemetry: z.ZodDefault<z.ZodBoolean>;
948
+ /**
949
+ * Default reporting window (in days) for `book tool-stats` and the target for
950
+ * `book tool-stats --prune`. Disk use is bounded by size-based log rotation;
951
+ * records are only deleted when they rotate out or are explicitly pruned.
952
+ */
953
+ toolTelemetryRetentionDays: z.ZodDefault<z.ZodNumber>;
954
+ }, "strip", z.ZodTypeAny, {
955
+ toolTelemetry: boolean;
956
+ toolTelemetryRetentionDays: number;
957
+ }, {
958
+ toolTelemetry?: boolean | undefined;
959
+ toolTelemetryRetentionDays?: number | undefined;
960
+ }>>;
961
+ mcp: z.ZodDefault<z.ZodObject<{
962
+ /**
963
+ * Per-server trust decisions for workspace `.mcp.json` declarations.
964
+ * User-global servers (<BOOK_HOME>/.book/mcp.json) never require approval.
965
+ */
966
+ projectServers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
967
+ /** Hash of the server's command/args/env at decision time; a mismatch re-prompts. */
968
+ fingerprint: z.ZodString;
969
+ choice: z.ZodEnum<["approved", "rejected"]>;
970
+ }, "strip", z.ZodTypeAny, {
971
+ fingerprint: string;
972
+ choice: "approved" | "rejected";
973
+ }, {
974
+ fingerprint: string;
975
+ choice: "approved" | "rejected";
976
+ }>>>;
977
+ }, "strip", z.ZodTypeAny, {
978
+ projectServers: Record<string, {
979
+ fingerprint: string;
980
+ choice: "approved" | "rejected";
981
+ }>;
982
+ }, {
983
+ projectServers?: Record<string, {
984
+ fingerprint: string;
985
+ choice: "approved" | "rejected";
986
+ }> | undefined;
987
+ }>>;
988
+ commands: z.ZodDefault<z.ZodObject<{
989
+ /**
990
+ * Per-command trust decisions for `<workspace>/.book/commands/*.md` files
991
+ * that substitute shell into their prompt. That substitution runs outside the
992
+ * permission system and outside the sandbox, so a repository-controlled file
993
+ * needs a decision before it runs. User-global commands
994
+ * (<BOOK_HOME>/commands) were written by the user and never require one.
995
+ */
996
+ projectCommands: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
997
+ /** Digest of the shell the body substitutes at decision time; a mismatch re-prompts. */
998
+ fingerprint: z.ZodString;
999
+ choice: z.ZodEnum<["approved", "rejected"]>;
1000
+ }, "strip", z.ZodTypeAny, {
1001
+ fingerprint: string;
1002
+ choice: "approved" | "rejected";
1003
+ }, {
1004
+ fingerprint: string;
1005
+ choice: "approved" | "rejected";
1006
+ }>>>;
1007
+ }, "strip", z.ZodTypeAny, {
1008
+ projectCommands: Record<string, {
1009
+ fingerprint: string;
1010
+ choice: "approved" | "rejected";
1011
+ }>;
1012
+ }, {
1013
+ projectCommands?: Record<string, {
1014
+ fingerprint: string;
1015
+ choice: "approved" | "rejected";
1016
+ }> | undefined;
1017
+ }>>;
1018
+ }, "strip", z.ZodTypeAny, {
1019
+ mcp: {
1020
+ projectServers: Record<string, {
1021
+ fingerprint: string;
1022
+ choice: "approved" | "rejected";
1023
+ }>;
1024
+ };
1025
+ env: Record<string, string>;
1026
+ ui: {
1027
+ showThinking: boolean;
1028
+ startupAnimation: boolean;
1029
+ };
1030
+ skills: {
1031
+ enabled: boolean;
1032
+ overrides: Record<string, "manual" | "off" | "auto" | "name-only">;
1033
+ execution: Record<string, "ask" | "deny" | "inherit">;
1034
+ };
1035
+ additionalDirectories: string[];
1036
+ provider: Record<string, {
1037
+ type: "openai" | "anthropic";
1038
+ models: Record<string, {
1039
+ effort?: false | {
1040
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
1041
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
1042
+ } | undefined;
1043
+ label?: string | undefined;
1044
+ contextWindow?: number | undefined;
1045
+ maxOutputTokens?: number | undefined;
1046
+ vision?: boolean | undefined;
1047
+ manual?: boolean | undefined;
1048
+ editFormat?: "patch" | "replace" | "whole" | undefined;
1049
+ }>;
1050
+ baseURL?: string | undefined;
1051
+ baseUrl?: string | undefined;
1052
+ apiKey?: string | undefined;
1053
+ }>;
1054
+ permissions: {
1055
+ allow: string[];
1056
+ ask: string[];
1057
+ deny: string[];
1058
+ projectAllowRules: Record<string, "approved" | "rejected">;
1059
+ };
1060
+ sandbox: {
1061
+ enabled: boolean;
1062
+ failIfUnavailable: boolean;
1063
+ autoAllowBashIfSandboxed: boolean;
1064
+ excludedCommands: string[];
1065
+ allowUnsandboxedCommands: boolean;
1066
+ filesystem: {
1067
+ allowWrite: string[];
1068
+ denyWrite: string[];
1069
+ denyRead: string[];
1070
+ };
1071
+ network: {
1072
+ allowedDomains: string[];
1073
+ deniedDomains: string[];
1074
+ };
1075
+ };
1076
+ hooks: {
1077
+ SessionStart: {
1078
+ command: string;
1079
+ env: Record<string, string>;
1080
+ matcher?: string | undefined;
1081
+ }[];
1082
+ SessionEnd: {
1083
+ command: string;
1084
+ env: Record<string, string>;
1085
+ matcher?: string | undefined;
1086
+ }[];
1087
+ UserPromptSubmit: {
1088
+ command: string;
1089
+ env: Record<string, string>;
1090
+ matcher?: string | undefined;
1091
+ }[];
1092
+ PreToolUse: {
1093
+ command: string;
1094
+ env: Record<string, string>;
1095
+ matcher?: string | undefined;
1096
+ }[];
1097
+ PostToolUse: {
1098
+ command: string;
1099
+ env: Record<string, string>;
1100
+ matcher?: string | undefined;
1101
+ }[];
1102
+ Stop: {
1103
+ command: string;
1104
+ env: Record<string, string>;
1105
+ matcher?: string | undefined;
1106
+ }[];
1107
+ PreCompact: {
1108
+ command: string;
1109
+ env: Record<string, string>;
1110
+ matcher?: string | undefined;
1111
+ }[];
1112
+ PostCompact: {
1113
+ command: string;
1114
+ env: Record<string, string>;
1115
+ matcher?: string | undefined;
1116
+ }[];
1117
+ SubagentStart: {
1118
+ command: string;
1119
+ env: Record<string, string>;
1120
+ matcher?: string | undefined;
1121
+ }[];
1122
+ SubagentStop: {
1123
+ command: string;
1124
+ env: Record<string, string>;
1125
+ matcher?: string | undefined;
1126
+ }[];
1127
+ Notification: {
1128
+ command: string;
1129
+ env: Record<string, string>;
1130
+ matcher?: string | undefined;
1131
+ }[];
1132
+ projectEntries: Record<string, "approved" | "rejected">;
1133
+ };
1134
+ retry: {
1135
+ maxAttempts: number;
1136
+ baseDelayMs: number;
1137
+ maxDelayMs: number;
1138
+ totalBudgetMs: number;
1139
+ requestTimeoutMs: number;
1140
+ streamStallTimeoutMs: number;
1141
+ thinkingStallTimeoutMs: number;
1142
+ toolRetries: number;
1143
+ watchdog: boolean;
1144
+ streamReissueAttempts: number;
1145
+ outputCapContinuations: number;
1146
+ };
1147
+ memory: {
1148
+ enabled: boolean;
1149
+ autoSave: boolean;
1150
+ requireApproval: boolean;
1151
+ };
1152
+ continuation: {
1153
+ enabled: boolean;
1154
+ maxConsecutive: number;
1155
+ noProgressLimit: number;
1156
+ blockedToolTurnLimit: number;
1157
+ planRefreshTurns: number;
1158
+ maxWallClockMs: number;
1159
+ };
1160
+ agents: {
1161
+ mode: "manual" | "adaptive" | "off";
1162
+ maxConcurrent: number;
1163
+ maxSpawned: number;
1164
+ maxDepth: 1;
1165
+ persist: boolean;
1166
+ includeUntrackedInSnapshot: boolean;
1167
+ telemetry: boolean;
1168
+ retentionDays: number;
1169
+ checks: Record<string, string | string[]>;
1170
+ checkTimeoutMs: number;
1171
+ maxWorktrees: number;
1172
+ resumeInterrupted: boolean;
1173
+ minFreeDiskBytes: number;
1174
+ profiles: Record<string, {
1175
+ effort?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
1176
+ model?: string | undefined;
1177
+ maxTurns?: number | undefined;
1178
+ color?: string | undefined;
1179
+ }>;
1180
+ ui: {
1181
+ enabled: boolean;
1182
+ };
1183
+ routing: {
1184
+ inlineSearchBudget: number;
1185
+ exploreReminder: boolean;
1186
+ };
1187
+ forwardTextEvents: boolean;
1188
+ };
1189
+ toolDiscovery: {
1190
+ mode: "auto" | "eager" | "deferred";
1191
+ eagerToolCount: number;
1192
+ schemaTokenBudget: number;
1193
+ maxLoadedTools: number;
1194
+ searchLimit: number;
1195
+ };
1196
+ toolExecution: {
1197
+ maxConcurrent: number;
1198
+ };
1199
+ observability: {
1200
+ toolTelemetry: boolean;
1201
+ toolTelemetryRetentionDays: number;
1202
+ };
1203
+ commands: {
1204
+ projectCommands: Record<string, {
1205
+ fingerprint: string;
1206
+ choice: "approved" | "rejected";
1207
+ }>;
1208
+ };
1209
+ effort?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
1210
+ model?: string | undefined;
1211
+ maxTurns?: number | undefined;
1212
+ compactStrategy?: "summary" | undefined;
1213
+ compactModel?: string | undefined;
1214
+ maxTokens?: number | undefined;
1215
+ theme?: string | undefined;
1216
+ shell?: string | undefined;
1217
+ autoCompactEnabled?: boolean | undefined;
1218
+ defaultMode?: "default" | "auto" | "acceptEdits" | "plan" | "dontAsk" | "bypassPermissions" | undefined;
1219
+ disableBypassPermissionsMode?: boolean | undefined;
1220
+ }, {
1221
+ effort?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
1222
+ mcp?: {
1223
+ projectServers?: Record<string, {
1224
+ fingerprint: string;
1225
+ choice: "approved" | "rejected";
1226
+ }> | undefined;
1227
+ } | undefined;
1228
+ env?: Record<string, string> | undefined;
1229
+ model?: string | undefined;
1230
+ maxTurns?: number | undefined;
1231
+ ui?: {
1232
+ showThinking?: boolean | undefined;
1233
+ startupAnimation?: boolean | undefined;
1234
+ } | undefined;
1235
+ compactStrategy?: "summary" | undefined;
1236
+ compactModel?: string | undefined;
1237
+ maxTokens?: number | undefined;
1238
+ theme?: string | undefined;
1239
+ shell?: string | undefined;
1240
+ skills?: {
1241
+ enabled?: boolean | undefined;
1242
+ overrides?: Record<string, "manual" | "off" | "auto" | "name-only"> | undefined;
1243
+ execution?: Record<string, "ask" | "deny" | "inherit"> | undefined;
1244
+ } | undefined;
1245
+ autoCompactEnabled?: boolean | undefined;
1246
+ defaultMode?: "default" | "auto" | "acceptEdits" | "plan" | "dontAsk" | "bypassPermissions" | undefined;
1247
+ disableBypassPermissionsMode?: boolean | undefined;
1248
+ additionalDirectories?: string[] | undefined;
1249
+ provider?: Record<string, {
1250
+ type?: "openai" | "anthropic" | undefined;
1251
+ baseURL?: string | undefined;
1252
+ baseUrl?: string | undefined;
1253
+ apiKey?: string | undefined;
1254
+ models?: Record<string, {
1255
+ effort?: false | {
1256
+ default?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
1257
+ levels?: ("low" | "medium" | "high" | "xhigh" | "max")[] | undefined;
1258
+ } | undefined;
1259
+ label?: string | undefined;
1260
+ contextWindow?: number | undefined;
1261
+ maxOutputTokens?: number | undefined;
1262
+ vision?: boolean | undefined;
1263
+ manual?: boolean | undefined;
1264
+ editFormat?: "patch" | "replace" | "whole" | undefined;
1265
+ }> | undefined;
1266
+ }> | undefined;
1267
+ permissions?: {
1268
+ allow?: string[] | undefined;
1269
+ ask?: string[] | undefined;
1270
+ deny?: string[] | undefined;
1271
+ projectAllowRules?: Record<string, "approved" | "rejected"> | undefined;
1272
+ } | undefined;
1273
+ sandbox?: {
1274
+ enabled?: boolean | undefined;
1275
+ failIfUnavailable?: boolean | undefined;
1276
+ autoAllowBashIfSandboxed?: boolean | undefined;
1277
+ excludedCommands?: string[] | undefined;
1278
+ allowUnsandboxedCommands?: boolean | undefined;
1279
+ filesystem?: {
1280
+ allowWrite?: string[] | undefined;
1281
+ denyWrite?: string[] | undefined;
1282
+ denyRead?: string[] | undefined;
1283
+ } | undefined;
1284
+ network?: {
1285
+ allowedDomains?: string[] | undefined;
1286
+ deniedDomains?: string[] | undefined;
1287
+ } | undefined;
1288
+ } | undefined;
1289
+ hooks?: {
1290
+ SessionStart?: {
1291
+ command: string;
1292
+ matcher?: string | undefined;
1293
+ env?: Record<string, string> | undefined;
1294
+ }[] | undefined;
1295
+ SessionEnd?: {
1296
+ command: string;
1297
+ matcher?: string | undefined;
1298
+ env?: Record<string, string> | undefined;
1299
+ }[] | undefined;
1300
+ UserPromptSubmit?: {
1301
+ command: string;
1302
+ matcher?: string | undefined;
1303
+ env?: Record<string, string> | undefined;
1304
+ }[] | undefined;
1305
+ PreToolUse?: {
1306
+ command: string;
1307
+ matcher?: string | undefined;
1308
+ env?: Record<string, string> | undefined;
1309
+ }[] | undefined;
1310
+ PostToolUse?: {
1311
+ command: string;
1312
+ matcher?: string | undefined;
1313
+ env?: Record<string, string> | undefined;
1314
+ }[] | undefined;
1315
+ Stop?: {
1316
+ command: string;
1317
+ matcher?: string | undefined;
1318
+ env?: Record<string, string> | undefined;
1319
+ }[] | undefined;
1320
+ PreCompact?: {
1321
+ command: string;
1322
+ matcher?: string | undefined;
1323
+ env?: Record<string, string> | undefined;
1324
+ }[] | undefined;
1325
+ PostCompact?: {
1326
+ command: string;
1327
+ matcher?: string | undefined;
1328
+ env?: Record<string, string> | undefined;
1329
+ }[] | undefined;
1330
+ SubagentStart?: {
1331
+ command: string;
1332
+ matcher?: string | undefined;
1333
+ env?: Record<string, string> | undefined;
1334
+ }[] | undefined;
1335
+ SubagentStop?: {
1336
+ command: string;
1337
+ matcher?: string | undefined;
1338
+ env?: Record<string, string> | undefined;
1339
+ }[] | undefined;
1340
+ Notification?: {
1341
+ command: string;
1342
+ matcher?: string | undefined;
1343
+ env?: Record<string, string> | undefined;
1344
+ }[] | undefined;
1345
+ projectEntries?: Record<string, "approved" | "rejected"> | undefined;
1346
+ } | undefined;
1347
+ retry?: {
1348
+ maxAttempts?: number | undefined;
1349
+ baseDelayMs?: number | undefined;
1350
+ maxDelayMs?: number | undefined;
1351
+ totalBudgetMs?: number | undefined;
1352
+ requestTimeoutMs?: number | undefined;
1353
+ streamStallTimeoutMs?: number | undefined;
1354
+ thinkingStallTimeoutMs?: number | undefined;
1355
+ toolRetries?: number | undefined;
1356
+ watchdog?: boolean | undefined;
1357
+ streamReissueAttempts?: number | undefined;
1358
+ outputCapContinuations?: number | undefined;
1359
+ } | undefined;
1360
+ memory?: {
1361
+ enabled?: boolean | undefined;
1362
+ autoSave?: boolean | undefined;
1363
+ requireApproval?: boolean | undefined;
1364
+ } | undefined;
1365
+ continuation?: {
1366
+ enabled?: boolean | undefined;
1367
+ maxConsecutive?: number | undefined;
1368
+ noProgressLimit?: number | undefined;
1369
+ blockedToolTurnLimit?: number | undefined;
1370
+ planRefreshTurns?: number | undefined;
1371
+ maxWallClockMs?: number | undefined;
1372
+ } | undefined;
1373
+ agents?: {
1374
+ mode?: "manual" | "adaptive" | "off" | undefined;
1375
+ maxConcurrent?: number | undefined;
1376
+ maxSpawned?: number | undefined;
1377
+ maxDepth?: 1 | undefined;
1378
+ persist?: boolean | undefined;
1379
+ includeUntrackedInSnapshot?: boolean | undefined;
1380
+ telemetry?: boolean | undefined;
1381
+ retentionDays?: number | undefined;
1382
+ checks?: Record<string, string | string[]> | undefined;
1383
+ checkTimeoutMs?: number | undefined;
1384
+ maxWorktrees?: number | undefined;
1385
+ resumeInterrupted?: boolean | undefined;
1386
+ minFreeDiskBytes?: number | undefined;
1387
+ profiles?: Record<string, {
1388
+ effort?: "low" | "medium" | "high" | "xhigh" | "max" | undefined;
1389
+ model?: string | undefined;
1390
+ maxTurns?: number | undefined;
1391
+ color?: string | undefined;
1392
+ }> | undefined;
1393
+ ui?: {
1394
+ enabled?: boolean | undefined;
1395
+ } | undefined;
1396
+ routing?: {
1397
+ inlineSearchBudget?: number | undefined;
1398
+ exploreReminder?: boolean | undefined;
1399
+ } | undefined;
1400
+ forwardTextEvents?: boolean | undefined;
1401
+ } | undefined;
1402
+ toolDiscovery?: {
1403
+ mode?: "auto" | "eager" | "deferred" | undefined;
1404
+ eagerToolCount?: number | undefined;
1405
+ schemaTokenBudget?: number | undefined;
1406
+ maxLoadedTools?: number | undefined;
1407
+ searchLimit?: number | undefined;
1408
+ } | undefined;
1409
+ toolExecution?: {
1410
+ maxConcurrent?: number | undefined;
1411
+ } | undefined;
1412
+ observability?: {
1413
+ toolTelemetry?: boolean | undefined;
1414
+ toolTelemetryRetentionDays?: number | undefined;
1415
+ } | undefined;
1416
+ commands?: {
1417
+ projectCommands?: Record<string, {
1418
+ fingerprint: string;
1419
+ choice: "approved" | "rejected";
1420
+ }> | undefined;
1421
+ } | undefined;
1422
+ }>;
1423
+ type BookSettings = z.infer<typeof bookSettingsSchema>;
1424
+ /**
1425
+ * Resolved settings after layering (all defaults filled).
1426
+ */
1427
+ type ResolvedSettings = Required<Omit<BookSettings, 'model' | 'compactModel' | 'maxTurns' | 'maxTokens' | 'effort' | 'theme' | 'shell' | 'autoCompactEnabled' | 'defaultMode' | 'disableBypassPermissionsMode'>> & Pick<BookSettings, 'model' | 'compactModel' | 'maxTurns' | 'maxTokens' | 'effort' | 'theme' | 'shell' | 'autoCompactEnabled' | 'defaultMode' | 'disableBypassPermissionsMode'>;
1428
+
1429
+ declare const MEMORY_TYPES: readonly ["user", "feedback", "project", "reference"];
1430
+ type MemoryType = (typeof MEMORY_TYPES)[number];
1431
+ type MemoryStatus = 'approved' | 'pending' | 'discarded';
1432
+ interface MemoryFileSummary {
1433
+ name: string;
1434
+ path: string;
1435
+ type?: MemoryType;
1436
+ status?: MemoryStatus;
1437
+ title?: string;
1438
+ created?: string;
1439
+ updated?: string;
1440
+ size: number;
1441
+ }
1442
+ interface LoadedMemoryContext {
1443
+ dir: string;
1444
+ indexFile: string | null;
1445
+ indexLoaded: boolean;
1446
+ indexLineCount: number;
1447
+ loadedLineCount: number;
1448
+ indexText: string;
1449
+ files: MemoryFileSummary[];
1450
+ candidates: MemoryFileSummary[];
1451
+ }
1452
+
1453
+ interface LearnedModelWindowEntry {
1454
+ contextWindow: number;
1455
+ learnedAt: number;
1456
+ }
1457
+ interface ModelWindowStore {
1458
+ get(model: string): number | undefined;
1459
+ getEntry(model: string): LearnedModelWindowEntry | undefined;
1460
+ ratchet(model: string, ceiling: number): boolean;
1461
+ all(): Record<string, LearnedModelWindowEntry>;
1462
+ }
1463
+
1464
+ type PermissionMode = 'default' | 'auto' | 'plan' | 'accept-edits' | 'dontAsk' | 'bypassPermissions';
1465
+ /**
1466
+ * A command resolved to a direct argv spawn — no intermediate shell parses it.
1467
+ *
1468
+ * Sandboxed commands must use this form: joining a wrapper into one string and
1469
+ * spawning it with `shell: true` lets any metacharacter in the user's command
1470
+ * split at the *outer* shell, outside the sandbox. Callers that receive an
1471
+ * `exec` spawn `file` with `args` and `shell: false`; when it is absent the raw
1472
+ * command string goes to the platform shell as before. Unsandboxed commands
1473
+ * use the same form when the session shell is spawned as argv (see
1474
+ * `ResolvedShell`).
1475
+ */
1476
+ interface CommandExecution {
1477
+ file: string;
1478
+ args: string[];
1479
+ }
1480
+ /** The program that interprets a Bash tool command string. */
1481
+ type ShellKind = 'bash' | 'pwsh' | 'powershell' | 'cmd' | 'sh';
1482
+ /** Where a shell selection came from, for diagnostics. */
1483
+ type ShellSource = 'setting' | 'launching-shell' | 'detected' | 'fallback';
1484
+ /**
1485
+ * The shell the Bash tool spawns, resolved once per session by
1486
+ * `resolveShell()` in `src/shell-selection.ts`.
1487
+ *
1488
+ * `file` is the executable spawned as argv; when absent the command string
1489
+ * goes to Node's `shell: true` (`/bin/sh`, or `%ComSpec%` on Windows), which
1490
+ * is the only way cmd.exe is ever driven because its quoting cannot be
1491
+ * reproduced from a plain argument vector.
1492
+ */
1493
+ interface ResolvedShell {
1494
+ kind: ShellKind;
1495
+ file?: string;
1496
+ /** Short human name: "Git Bash", "PowerShell 7", "Windows PowerShell 5.1", "cmd.exe", "/bin/sh". */
1497
+ label: string;
1498
+ source: ShellSource;
1499
+ /** Set when a requested shell could not be honoured and the automatic ladder was used instead. */
1500
+ warning?: string;
1501
+ }
1502
+ /** What phase of retry is currently active (drives the TUI spinner label). */
1503
+ type RetryPhase = 'none' | 'transport' | 'stalled' | 'tool' | 'watchdog';
1504
+ /**
1505
+ * Retry configuration — all tunables live here.
1506
+ * Mirrors Claude Code's env vars: CLAUDE_CODE_MAX_RETRIES, API_TIMEOUT_MS,
1507
+ * CLAUDE_CODE_RETRY_WATCHDOG, plus the stream-stall threshold.
1508
+ */
1509
+ interface RetryConfig {
1510
+ maxAttempts: number;
1511
+ baseDelayMs: number;
1512
+ maxDelayMs: number;
1513
+ totalBudgetMs: number;
1514
+ requestTimeoutMs: number;
1515
+ streamStallTimeoutMs: number;
1516
+ toolRetries: number;
1517
+ watchdog: boolean;
1518
+ /**
1519
+ * Re-sends of a turn after a transport failure that left the work intact.
1520
+ * Optional, and absent means 0 — a caller that builds a RetryConfig by hand
1521
+ * keeps the original end-the-run-on-any-stream-error behavior rather than
1522
+ * silently inheriting a retry policy it never asked for.
1523
+ */
1524
+ streamReissueAttempts?: number;
1525
+ /**
1526
+ * Continuations allowed after the provider's output cap, budgeted separately
1527
+ * from transport faults so a large generated file cannot drain the allowance a
1528
+ * real socket drop needs. Optional, and absent means 0.
1529
+ */
1530
+ outputCapContinuations?: number;
1531
+ /** Stall ceiling while the model is thinking; absent falls back to the chat one. */
1532
+ thinkingStallTimeoutMs?: number;
1533
+ }
1534
+ type AgentTaskStatus = 'pending' | 'in_progress' | 'completed' | 'deleted';
1535
+ interface AgentTask {
1536
+ id: string;
1537
+ subject: string;
1538
+ description?: string;
1539
+ status: AgentTaskStatus;
1540
+ activeForm?: string;
1541
+ owner?: string;
1542
+ metadata?: Record<string, unknown>;
1543
+ blockedBy: string[];
1544
+ blocks: string[];
1545
+ createdAt: number;
1546
+ updatedAt: number;
1547
+ }
1548
+ type BackgroundShellStatus = 'starting' | 'running' | 'stopping' | 'exited' | 'failed' | 'killed' | 'timed_out' | 'lost';
1549
+ type BackgroundShellLifetime = 'session' | 'persistent';
1550
+ type BackgroundShellNotify = 'none' | 'ui' | 'agent';
1551
+ interface BackgroundShellRecord {
1552
+ id: string;
1553
+ command: string;
1554
+ effectiveCommand: string;
1555
+ title?: string;
1556
+ workdir: string;
1557
+ pid?: number;
1558
+ runnerPid?: number;
1559
+ process?: ChildProcess;
1560
+ status: BackgroundShellStatus;
1561
+ lifetime?: BackgroundShellLifetime;
1562
+ notify?: BackgroundShellNotify;
1563
+ output: string;
1564
+ readOffset: number;
1565
+ truncatedBytes: number;
1566
+ outputRevision?: number;
1567
+ completionSequence?: number;
1568
+ completionAcknowledgedSequence?: number;
1569
+ completionDeliveredSequence?: number;
1570
+ exitCode?: number | null;
1571
+ signal?: NodeJS.Signals | string | null;
1572
+ startedAt: number;
1573
+ finishedAt?: number;
1574
+ timeoutMs?: number;
1575
+ deadlineAt?: number;
1576
+ parentSessionId?: string;
1577
+ rootRunId?: string;
1578
+ parentRunId?: string;
1579
+ persistentRecordPath?: string;
1580
+ persistentControlPath?: string;
1581
+ persistentOutputPath?: string;
1582
+ persistentOutputRotationSequence?: number;
1583
+ controlToken?: string;
1584
+ timer?: NodeJS.Timeout;
1585
+ retentionTimer?: NodeJS.Timeout;
1586
+ sandboxed?: boolean;
1587
+ }
1588
+ interface BackgroundShellStore {
1589
+ nextId: number;
1590
+ shells: Map<string, BackgroundShellRecord>;
1591
+ }
1592
+ interface AgentConfig {
1593
+ /** May be empty until an interactive user adds a BYOK provider. */
1594
+ apiKey: string;
1595
+ baseUrl: string;
1596
+ model: string;
1597
+ /** Raw picker/settings reference, including provider prefix when configured. */
1598
+ modelSelection?: string;
1599
+ /**
1600
+ * Set when the model id carries a `<prefix>/` matching no configured provider,
1601
+ * so the run fell back to the default endpoint. Reported rather than thrown:
1602
+ * the same spelling is a legitimate vendor-namespaced model id.
1603
+ */
1604
+ modelProviderWarning?: string;
1605
+ /** Optional provider/model used only for historical conversation compaction. */
1606
+ compactModel?: string;
1607
+ /** Supported production context-reduction strategy. */
1608
+ compactStrategy: CompactStrategy;
1609
+ /** Max agent turns per user message. Undefined = unlimited. */
1610
+ maxTurns?: number;
1611
+ maxTokens: number;
1612
+ /** True when maxTokens came from user config/env, not a default or model metadata. */
1613
+ maxTokensExplicit?: boolean;
1614
+ /** Default output-token limit to restore when selected model has no metadata. */
1615
+ defaultMaxTokens?: number;
1616
+ /** True when effort came from user config/env or an explicit runtime choice. */
1617
+ effortExplicit?: boolean;
1618
+ /** Default effort to restore when selected model has no metadata. */
1619
+ defaultEffort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max';
1620
+ /** Plain-model fallback values used after provider/model switches. */
1621
+ defaultApiKey?: string;
1622
+ defaultBaseUrl?: string;
1623
+ defaultProvider?: 'anthropic' | 'openai' | 'auto';
1624
+ autoCompactEnabled: boolean;
1625
+ workspace: string;
1626
+ animation: {
1627
+ typewriterSpeed: number;
1628
+ spinnerStyle: 'braille' | 'dots';
1629
+ };
1630
+ accessibility: {
1631
+ screenReader: boolean;
1632
+ reducedMotion: boolean;
1633
+ };
1634
+ /** Resolved layered settings (permissions, sandbox, etc.). */
1635
+ settings: ResolvedSettings;
1636
+ /**
1637
+ * The shell the Bash tool spawns, resolved once at load from `BOOK_SHELL`,
1638
+ * `settings.shell`, and what is installed. Optional so hand-built configs in
1639
+ * tests keep working; the tool resolves it on demand when absent.
1640
+ */
1641
+ shell?: ResolvedShell;
1642
+ /** Settings-layer context retained for live TUI re-resolution. */
1643
+ settingsContext?: {
1644
+ overridePath?: string;
1645
+ noSettings?: boolean;
1646
+ };
1647
+ /** Retry configuration (from settings.json + env vars). */
1648
+ retry: RetryConfig;
1649
+ /** Thinking effort level (Anthropic adaptive thinking / output_config.effort). */
1650
+ effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max';
1651
+ /** Explicit provider override: 'anthropic' | 'openai' | 'auto' (default: auto-detect). */
1652
+ provider?: 'anthropic' | 'openai' | 'auto';
1653
+ /** Metadata from settings.provider.<id>.models.<model>, if selected. */
1654
+ modelInfo?: ProviderModelConfig;
1655
+ /** Approved memory snapshot loaded once at session start. */
1656
+ memoryContext?: LoadedMemoryContext;
1657
+ /** Optional store for learned context window ceilings. */
1658
+ modelWindowStore?: ModelWindowStore;
1659
+ }
1660
+
1661
+ type ShellJobEvent = {
1662
+ type: 'background_job_start';
1663
+ job: BackgroundShellRecord;
1664
+ } | {
1665
+ type: 'background_job_update';
1666
+ job: BackgroundShellRecord;
1667
+ } | {
1668
+ type: 'background_job_output';
1669
+ jobId: string;
1670
+ revision: number;
1671
+ } | {
1672
+ type: 'background_job_result';
1673
+ job: BackgroundShellRecord;
1674
+ } | {
1675
+ type: 'background_job_dismiss';
1676
+ jobId: string;
1677
+ };
1678
+ interface ShellStartOptions {
1679
+ command: string;
1680
+ effectiveCommand: string;
1681
+ /**
1682
+ * Present for sandboxed commands and for a session shell spawned as argv
1683
+ * (Git Bash or PowerShell on Windows); absent means Node's `shell: true`.
1684
+ */
1685
+ exec?: CommandExecution;
1686
+ workdir: string;
1687
+ env: NodeJS.ProcessEnv;
1688
+ sandboxed: boolean;
1689
+ title?: string;
1690
+ notify?: BackgroundShellNotify;
1691
+ timeoutMs?: number;
1692
+ lifetime?: 'session' | 'persistent';
1693
+ workspace?: string;
1694
+ envOverrides?: Record<string, string>;
1695
+ parentSessionId?: string;
1696
+ rootRunId?: string;
1697
+ parentRunId?: string;
1698
+ }
1699
+ interface ShellOutputResult {
1700
+ shell: BackgroundShellRecord;
1701
+ output: string;
1702
+ remaining: number;
1703
+ }
1704
+ interface ShellJobManagerOptions {
1705
+ persistentRoot?: string;
1706
+ /**
1707
+ * Budgets for observing the detached runner's state transitions. The
1708
+ * defaults suit an interactive host; tests on contended CI runners pass
1709
+ * wider windows because the transitions are eventual, not latency-bound.
1710
+ */
1711
+ runnerStartBudgetMs?: number;
1712
+ runnerStopBudgetMs?: number;
1713
+ /**
1714
+ * Injected by tests. Only the two budgets above read it: every other time in
1715
+ * this file is a stamp shared with the detached runner process, and those
1716
+ * stay on the wall clock because two processes share no monotonic origin.
1717
+ */
1718
+ clock?: Clock;
1719
+ }
1720
+ declare class ShellJobManager {
1721
+ private readonly store;
1722
+ private readonly options;
1723
+ private readonly subscribers;
1724
+ private workspace?;
1725
+ private persistentPaths?;
1726
+ private persistentStorageError?;
1727
+ private monitor?;
1728
+ constructor(store: BackgroundShellStore, options?: ShellJobManagerOptions);
1729
+ private readonly clock;
1730
+ subscribe(listener: (event: ShellJobEvent) => void): () => void;
1731
+ configureWorkspace(workspace: string): void;
1732
+ list(): BackgroundShellRecord[];
1733
+ get(shellId: string): BackgroundShellRecord | undefined;
1734
+ listPendingAgentCompletions(parentSessionId: string): BackgroundShellRecord[];
1735
+ listPendingUiCompletions(): BackgroundShellRecord[];
1736
+ start(options: ShellStartOptions): Promise<BackgroundShellRecord>;
1737
+ readOutput(shellId: string, limit?: number): ShellOutputResult | undefined;
1738
+ readTail(shellId: string, limit?: number): string | undefined;
1739
+ stop(shellId: string, reason?: 'killed' | 'timed_out'): Promise<boolean>;
1740
+ dismiss(shellId: string): void;
1741
+ acknowledgeCompletion(shellId: string, sequence?: number, consumer?: 'ui' | 'agent'): void;
1742
+ dispose(): void;
1743
+ private emit;
1744
+ private persistAcknowledgement;
1745
+ private startPersistent;
1746
+ private runnerInvocation;
1747
+ private stopPersistent;
1748
+ private refreshPersistentJobs;
1749
+ private refreshPersistentRecord;
1750
+ private reconcilePersistentState;
1751
+ private applyPersistentState;
1752
+ private recordFromPersistentState;
1753
+ private appendOutput;
1754
+ private clearTimer;
1755
+ private finish;
1756
+ private terminate;
1757
+ private deleteRecord;
1758
+ private pruneTerminalShells;
1759
+ private scheduleRetention;
1760
+ }
1761
+
1762
+ type AgentTerminalStatus = 'completed' | 'failed' | 'cancelled' | 'timed_out' | 'interrupted';
1763
+ type AgentTerminalReason = 'normal_completion' | 'blocked_by_policy' | 'provider_error' | 'provider_timeout' | 'stream_stall' | 'protocol_error' | 'context_overflow' | 'budget_exceeded' | 'budget_unverifiable' | 'max_turns' | 'runtime_error' | 'caller_cancelled' | 'user_cancelled' | 'session_disposed' | 'session_replaced' | 'transport_interrupted' | 'output_cap' | 'credentials_rejected' | 'objective_complete' | 'continuation_limit' | 'blocked_plan' | 'no_progress' | 'all_tools_blocked' | 'plan_stop' | 'handoff_requested' | 'missing_terminal';
1764
+ interface AgentTerminalOutcome {
1765
+ readonly status: AgentTerminalStatus;
1766
+ readonly reason: AgentTerminalReason;
1767
+ readonly message?: string;
1768
+ readonly partialOutput: boolean;
1769
+ readonly providerCode?: string;
1770
+ }
1771
+
1772
+ interface Usage {
1773
+ promptTokens: number;
1774
+ completionTokens: number;
1775
+ totalTokens: number;
1776
+ /** Context-pressure metric for auto-compact (includes Anthropic cache tokens when known). */
1777
+ contextTokens?: number;
1778
+ cacheCreationInputTokens?: number;
1779
+ cacheReadInputTokens?: number;
1780
+ }
1781
+ /** Provider-native response data needed to replay a provider protocol exactly. */
1782
+ interface ProviderMessageMetadata {
1783
+ /** Anthropic content blocks, including thinking signatures, in original order. */
1784
+ anthropicContentBlocks?: Array<Record<string, unknown>>;
1785
+ }
1786
+ /** A session-owned image referenced by a user message. */
1787
+ interface ImageAttachment {
1788
+ id: string;
1789
+ sha256: string;
1790
+ storageKey: string;
1791
+ mediaType: 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
1792
+ byteSize: number;
1793
+ displayName?: string;
1794
+ }
1795
+ /**
1796
+ * Structured presentation data for local slash-command output.
1797
+ *
1798
+ * These snapshots stay UI-only: local command messages are excluded from the
1799
+ * provider context and are never persisted as assistant turns.
1800
+ */
1801
+ interface ConfigCommandDisplay {
1802
+ kind: 'config';
1803
+ snapshot: Record<string, unknown>;
1804
+ runtime: {
1805
+ model: string;
1806
+ provider: string;
1807
+ effort?: string;
1808
+ mode: string;
1809
+ maxTokens: number;
1810
+ workspace: string;
1811
+ };
1812
+ }
1813
+ type ContextWindowSource = 'declared' | 'learned' | 'family' | 'default';
1814
+ interface ContextCommandDisplay {
1815
+ kind: 'context';
1816
+ model: string;
1817
+ maxTokens: number;
1818
+ /** Origin of the context window: declared in settings, matched from a family prior, or product default. */
1819
+ windowSource?: ContextWindowSource;
1820
+ /** @deprecated Use `windowSource`. False/absent when maxTokens is the assumed default. */
1821
+ windowDeclared?: boolean;
1822
+ estimatedTokens: number;
1823
+ totalMessages: number;
1824
+ userMessages: number;
1825
+ assistantMessages: number;
1826
+ toolCalls: number;
1827
+ toolResults: number;
1828
+ userTokens: number;
1829
+ assistantTokens: number;
1830
+ ambient: {
1831
+ commandCount: number;
1832
+ skillCount?: number;
1833
+ subagentCount?: number;
1834
+ hasMemoryIndex?: boolean;
1835
+ hasClaudeMdLoader: boolean;
1836
+ };
1837
+ }
1838
+ interface UsageCommandDisplay {
1839
+ kind: 'usage';
1840
+ model: string;
1841
+ currentTurn: number;
1842
+ messageCount: number;
1843
+ turnDurationMs: number;
1844
+ usage: Usage | null;
1845
+ rate?: {
1846
+ inputPerMillion: number;
1847
+ outputPerMillion: number;
1848
+ };
1849
+ estimatedCostUsd?: number;
1850
+ /** Per-tool call/failure counters for this session, ordered by call count. */
1851
+ toolCallStats?: Array<{
1852
+ tool: string;
1853
+ calls: number;
1854
+ failures: Record<string, number>;
1855
+ }>;
1856
+ }
1857
+ /** Compact completion metadata rendered as a host notification. */
1858
+ interface AgentNotificationDisplay {
1859
+ deliveryId?: string;
1860
+ sequence?: number;
1861
+ agentId: string;
1862
+ displayName: string;
1863
+ status: 'completed' | 'failed' | 'stopped' | 'interrupted';
1864
+ summary?: string;
1865
+ error?: string;
1866
+ evidenceIds: string[];
1867
+ durationMs?: number;
1868
+ }
1869
+ type LocalCommandDisplay = ConfigCommandDisplay | ContextCommandDisplay | UsageCommandDisplay;
1870
+ interface Message {
1871
+ id: string;
1872
+ role: 'user' | 'assistant';
1873
+ /** User-facing text shown in the TUI/history. */
1874
+ content: string;
1875
+ /** Provider-native reasoning/thinking text shown separately and replayed in later context. */
1876
+ reasoningContent?: string;
1877
+ /** Provider-native metadata used only when reconstructing provider requests. */
1878
+ providerMetadata?: ProviderMessageMetadata;
1879
+ /** Provider-facing text when it differs from the displayed content. */
1880
+ contextContent?: string;
1881
+ /**
1882
+ * Host-rendered `<session-state>` block appended to this turn for the provider.
1883
+ * Written once when the turn is first built and never rewritten, so rebuilds
1884
+ * reproduce the message byte-for-byte behind the conversation cache breakpoint.
1885
+ */
1886
+ sessionState?: string;
1887
+ /** Images attached to a user turn; bytes live in session attachment storage. */
1888
+ attachments?: ImageAttachment[];
1889
+ /** Whether this message is included in provider and compaction context. */
1890
+ includeInContext: boolean;
1891
+ kind?: 'conversation' | 'checkpoint' | 'local' | 'agent-notification';
1892
+ /**
1893
+ * The content was produced by resolving something -- a slash command's body, a
1894
+ * delegated task prompt -- rather than typed by the user. The role still reads
1895
+ * `user` because that is the turn's position in the conversation, so anything
1896
+ * that means "the user's own words" must check this too.
1897
+ */
1898
+ derivedContent?: boolean;
1899
+ /** Structured display data for automatically delivered child completions. */
1900
+ agentNotifications?: AgentNotificationDisplay[];
1901
+ toolCalls?: ToolCall[];
1902
+ toolResults?: ToolResult[];
1903
+ /** UI-only subagent activity. Never serialized as provider tool calls. */
1904
+ nestedToolInvocations?: NestedToolInvocation[];
1905
+ /** UI-only visual treatment for local slash-command output. */
1906
+ localCommand?: LocalCommandDisplay;
1907
+ fileObservations?: FileObservation[];
1908
+ timestamp: number;
1909
+ }
1910
+
1911
+ type AgentRunSource = 'tui' | 'headless' | 'sdk' | 'internal';
1912
+ type AgentModelIdentityStatus = 'verified' | 'requested_only' | 'unverifiable';
1913
+ interface AgentModelIdentity {
1914
+ readonly provider: string;
1915
+ readonly requestedModel: string;
1916
+ readonly responseModel?: string;
1917
+ readonly responseId?: string;
1918
+ readonly finishReasons?: readonly string[];
1919
+ readonly status: AgentModelIdentityStatus;
1920
+ }
1921
+ interface AgentRunAccounting {
1922
+ readonly rootRunIds: readonly string[];
1923
+ readonly runIds: readonly string[];
1924
+ readonly directUsage: Usage | null;
1925
+ readonly inclusiveUsage: Usage | null;
1926
+ /**
1927
+ * This execution's own spend. For a root snapshot that is the root turn alone —
1928
+ * it deliberately excludes managed agents and subagents.
1929
+ */
1930
+ readonly costUsd: number | null;
1931
+ /**
1932
+ * Everything spent under this root, delegated work included. This is what a
1933
+ * budget must be enforced against: `costUsd` omits every dollar a fan-out spent,
1934
+ * which is most of the money in a run that delegates.
1935
+ */
1936
+ readonly inclusiveCostUsd: number | null;
1937
+ readonly costStatus: 'known' | 'estimated' | 'unknown';
1938
+ readonly pricingVersion: string;
1939
+ readonly unknownModels: readonly string[];
1940
+ readonly budgetUsd?: number;
1941
+ readonly budgetStatus: 'not_configured' | 'within' | 'exceeded' | 'unknown';
1942
+ readonly modelIdentities: readonly AgentModelIdentity[];
1943
+ readonly completeness: 'partial' | 'complete';
1944
+ readonly missingSources: readonly string[];
1945
+ }
1946
+ interface AgentRunAmbientSnapshot {
1947
+ readonly schemaVersion: 2;
1948
+ readonly fingerprint: string;
1949
+ readonly capturedAt: number;
1950
+ readonly model: {
1951
+ readonly provider: string;
1952
+ readonly requestedModel: string;
1953
+ readonly modelSelection?: string;
1954
+ readonly endpointFingerprint: string;
1955
+ readonly effort?: string;
1956
+ readonly maxTokens: number;
1957
+ readonly maxTurns?: number;
1958
+ readonly modelInfoFingerprint: string;
1959
+ };
1960
+ readonly settings: {
1961
+ readonly fingerprint: string;
1962
+ readonly agentsMode: 'adaptive' | 'manual' | 'off';
1963
+ };
1964
+ readonly tools: {
1965
+ readonly fingerprint: string;
1966
+ readonly count: number;
1967
+ readonly names: readonly string[];
1968
+ readonly activationState: 'fresh' | 'unverified';
1969
+ };
1970
+ readonly commands: {
1971
+ readonly fingerprint: string;
1972
+ readonly count: number;
1973
+ readonly names: readonly string[];
1974
+ };
1975
+ readonly skills: {
1976
+ readonly fingerprint: string;
1977
+ readonly count: number;
1978
+ readonly names: readonly string[];
1979
+ readonly activationState: 'disabled' | 'not-captured';
1980
+ };
1981
+ readonly mcp: {
1982
+ readonly fingerprint: string;
1983
+ readonly count: number;
1984
+ readonly names: readonly string[];
1985
+ };
1986
+ readonly agents: {
1987
+ readonly fingerprint: string;
1988
+ readonly count: number;
1989
+ readonly names: readonly string[];
1990
+ readonly mode: 'adaptive' | 'manual' | 'off';
1991
+ };
1992
+ readonly prompt: {
1993
+ readonly fingerprint: string;
1994
+ readonly systemPromptVersion: string;
1995
+ readonly date: string;
1996
+ readonly projectInstructionCount: number;
1997
+ };
1998
+ readonly memory: {
1999
+ readonly fingerprint: string;
2000
+ readonly enabled: boolean;
2001
+ readonly indexLoaded: boolean;
2002
+ };
2003
+ readonly policies: {
2004
+ readonly permissionMode: PermissionMode;
2005
+ readonly hooksFingerprint: string;
2006
+ readonly contextFingerprint: string;
2007
+ readonly networkFingerprint: string;
2008
+ readonly delegationFingerprint: string;
2009
+ };
2010
+ readonly runtime: {
2011
+ readonly packageVersion: string;
2012
+ readonly runtimeRevision: string;
2013
+ readonly fixtureRevision: string;
2014
+ readonly randomSeed: string;
2015
+ readonly nodeVersion: string;
2016
+ readonly platform: NodeJS.Platform;
2017
+ readonly architecture: string;
2018
+ readonly timezone: string;
2019
+ readonly environmentFingerprint: string;
2020
+ readonly workspaceFingerprint: string;
2021
+ };
2022
+ readonly bookHome: {
2023
+ readonly pathFingerprint: string;
2024
+ readonly isolation: 'shared' | 'configured' | 'isolated';
2025
+ readonly contentsFingerprint?: string;
2026
+ readonly contentsStatus: 'not-captured' | 'captured' | 'incomplete';
2027
+ readonly fileCount?: number;
2028
+ readonly totalBytes?: number;
2029
+ };
2030
+ readonly completeness: 'partial' | 'complete';
2031
+ readonly missingSources: readonly string[];
2032
+ }
2033
+ /** Runtime attribution for one user request or a linked child execution. */
2034
+ interface AgentRunContext {
2035
+ /** Unique execution identity. A resumed request receives a new value. */
2036
+ readonly runId: string;
2037
+ /** Root request identity shared by linked child executions. */
2038
+ readonly rootRunId: string;
2039
+ /** Parent execution for a child continuation or managed agent. */
2040
+ readonly parentRunId?: string;
2041
+ /** Previous run when a host resumes a request/session. */
2042
+ readonly resumedFromRunId?: string;
2043
+ readonly sessionId: string;
2044
+ readonly source: AgentRunSource;
2045
+ readonly startedAt: number;
2046
+ }
2047
+ interface AgentRunResult {
2048
+ readonly context: AgentRunContext;
2049
+ readonly outcome: AgentTerminalOutcome;
2050
+ readonly usage: Usage | null;
2051
+ readonly accounting?: AgentRunAccounting;
2052
+ readonly ambient?: AgentRunAmbientSnapshot;
2053
+ }
2054
+
2055
+ type SkillSource = 'user' | 'project';
2056
+ type SkillRootKind = 'book' | 'agents' | 'claude' | 'opencode';
2057
+ type SkillIssueSeverity = 'error' | 'warning';
2058
+ interface SkillIssue {
2059
+ code: string;
2060
+ message: string;
2061
+ severity: SkillIssueSeverity;
2062
+ }
2063
+ interface SkillResource {
2064
+ relativePath: string;
2065
+ byteSize: number;
2066
+ mtimeMs: number;
2067
+ digest: string;
2068
+ }
2069
+ interface ShadowedSkill {
2070
+ path: string;
2071
+ source: SkillSource;
2072
+ rootKind: SkillRootKind;
2073
+ }
2074
+ /** Metadata discovered without retaining or injecting the SKILL.md body. */
2075
+ interface Skill {
2076
+ id: string;
2077
+ version: string;
2078
+ descriptorDigest: string;
2079
+ resourceDigest: string;
2080
+ name: string;
2081
+ description: string;
2082
+ whenToUse?: string;
2083
+ license?: string;
2084
+ compatibility?: string;
2085
+ metadata: Record<string, string>;
2086
+ /** Requested tool ceiling. It never grants permission by itself. */
2087
+ allowedTools?: string[];
2088
+ model?: string;
2089
+ lifetime: 'turn' | 'run';
2090
+ /** Absolute path to the skill entrypoint. */
2091
+ path: string;
2092
+ /** Absolute directory that owns the skill and its resources. */
2093
+ rootPath: string;
2094
+ source: SkillSource;
2095
+ rootKind: SkillRootKind;
2096
+ activation: SkillActivation;
2097
+ execution: SkillExecution;
2098
+ invocationCount: number;
2099
+ entryByteSize: number;
2100
+ entryMtimeMs: number;
2101
+ resources: SkillResource[];
2102
+ issues: SkillIssue[];
2103
+ valid: boolean;
2104
+ shadowed: ShadowedSkill[];
2105
+ }
2106
+ interface LoadedSkillBody {
2107
+ body: string;
2108
+ digest: string;
2109
+ byteSize: number;
2110
+ }
2111
+ interface SkillRoot {
2112
+ path: string;
2113
+ source: SkillSource;
2114
+ kind: SkillRootKind;
2115
+ }
2116
+ interface DiscoverSkillsOptions {
2117
+ homeDir?: string;
2118
+ bookHomeDir?: string;
2119
+ includeUser?: boolean;
2120
+ projectRoot?: string;
2121
+ includeOpenCode?: boolean;
2122
+ enabled?: boolean;
2123
+ executionOverrides?: Readonly<Record<string, SkillExecution>>;
2124
+ }
2125
+ /** Roots are ordered from lowest to highest precedence. */
2126
+ declare function skillRoots(workspace: string, options?: DiscoverSkillsOptions): SkillRoot[];
2127
+ declare function discoverSkills(workspace: string, overrides?: Readonly<Record<string, SkillActivation>>, options?: DiscoverSkillsOptions): Skill[];
2128
+ declare function applySkillOverrides(skills: readonly Skill[], overrides: Readonly<Record<string, SkillActivation>>, executionOverrides?: Readonly<Record<string, SkillExecution>>, enabled?: boolean): Skill[];
2129
+ declare function loadSkillBody(skill: Skill): LoadedSkillBody;
2130
+ interface SkillListingResult {
2131
+ text: string;
2132
+ budgetChars: number;
2133
+ visibleCount: number;
2134
+ included: string[];
2135
+ omitted: string[];
2136
+ collapsed: string[];
2137
+ charCount: number;
2138
+ }
2139
+ declare function buildSkillListing(skills: readonly Skill[], budgetChars?: number): SkillListingResult;
2140
+ declare function generateSkillListing(skills: readonly Skill[], budgetChars?: number): string;
2141
+ declare function explicitSkillMentions(prompt: string, skills: readonly Skill[]): string[];
2142
+
2143
+ type SkillActivationReason = 'model' | 'user' | 'workflow' | 'subagent-preload';
2144
+ type SkillLifecycleEventType = 'skill_discovered' | 'skill_shadowed' | 'skill_activation_requested' | 'skill_activation_applied' | 'skill_activation_blocked' | 'skill_activation_expired' | 'skill_consent_requested' | 'skill_consent_granted' | 'skill_consent_denied' | 'skill_resource_read' | 'skill_resource_blocked' | 'skill_reloaded' | 'skill_watcher_failed';
2145
+ interface SkillLifecycleEvent {
2146
+ type: SkillLifecycleEventType;
2147
+ timestamp: number;
2148
+ skill?: string;
2149
+ reason?: SkillActivationReason;
2150
+ details?: Record<string, unknown>;
2151
+ }
2152
+ interface SkillActivationFrame {
2153
+ skillId: string;
2154
+ skillName: string;
2155
+ version: string;
2156
+ descriptorDigest: string;
2157
+ resourceDigest: string;
2158
+ reason: SkillActivationReason;
2159
+ bodyDigest: string;
2160
+ bodyByteSize: number;
2161
+ body: string;
2162
+ activatedAt: number;
2163
+ activatedAtTurn: number;
2164
+ expires: 'turn' | 'run';
2165
+ expiresAtTurn?: number;
2166
+ allowedTools?: string[];
2167
+ source: Skill['source'];
2168
+ rootKind: Skill['rootKind'];
2169
+ path: string;
2170
+ resources: Skill['resources'];
2171
+ }
2172
+ type SkillActivationFrameSummary = Omit<SkillActivationFrame, 'body'>;
2173
+ interface SkillRegistrySnapshot {
2174
+ catalogDigest: string;
2175
+ skills: Skill[];
2176
+ active: SkillActivationFrameSummary[];
2177
+ previous: SkillActivationFrameSummary[];
2178
+ effectiveTools?: string[];
2179
+ promptCatalog?: Omit<SkillListingResult, 'text'>;
2180
+ events: SkillLifecycleEvent[];
2181
+ }
2182
+ declare class SkillRegistryError extends Error {
2183
+ readonly code: string;
2184
+ constructor(code: string, message: string);
2185
+ }
2186
+ /** Session-owned catalog, activation frames, resources, and lifecycle evidence. */
2187
+ declare class SkillRegistry {
2188
+ readonly workspace: string;
2189
+ private settings;
2190
+ private settingsFingerprint;
2191
+ private readonly discoveryOptions;
2192
+ private descriptors;
2193
+ private readonly loadedBodies;
2194
+ private readonly active;
2195
+ private readonly previous;
2196
+ private explicitRequests;
2197
+ private consented;
2198
+ private runNumber;
2199
+ private effectiveTools?;
2200
+ private promptCatalog?;
2201
+ readonly events: SkillLifecycleEvent[];
2202
+ constructor(workspace: string, settings: SkillSettings, discoveryOptions?: DiscoverSkillsOptions);
2203
+ updateSettings(settings: SkillSettings): boolean;
2204
+ reload(cause?: string): Skill[];
2205
+ list(): Skill[];
2206
+ get(name: string): Skill | undefined;
2207
+ beginRun(prompt: string): string[];
2208
+ endRun(cause?: string): void;
2209
+ dispose(): void;
2210
+ isExplicitlyRequested(name: string): boolean;
2211
+ executionPolicy(name: string): Skill['execution'] | undefined;
2212
+ activationPolicy(name: string, reason?: SkillActivationReason): 'allow' | 'ask' | 'deny' | undefined;
2213
+ requestConsent(name: string, reason: SkillActivationReason): void;
2214
+ grantConsent(name: string, reason?: SkillActivationReason): void;
2215
+ denyConsent(name: string, reason: SkillActivationReason, cause?: string): void;
2216
+ isActive(name: string, currentTurn?: number): boolean;
2217
+ recordActivationBlocked(name: string, reason: SkillActivationReason, code: string, message: string): void;
2218
+ recordWatcherFailure(message: string): void;
2219
+ recordResourceBlocked(name: string, code: string, message: string): void;
2220
+ activate(name: string, reason: SkillActivationReason, currentTurn?: number): SkillActivationFrame;
2221
+ activeFrames(currentTurn?: number): SkillActivationFrame[];
2222
+ previousFrames(): SkillActivationFrame[];
2223
+ inspect(currentTurn?: number): SkillRegistrySnapshot;
2224
+ recordPromptCatalog(contextWindow: number): void;
2225
+ recordEffectiveTools(tools: readonly string[] | undefined): void;
2226
+ renderActivePolicy(currentTurn?: number): string;
2227
+ readResource(name: string, resourcePath: string, currentTurn?: number): {
2228
+ content: string;
2229
+ digest: string;
2230
+ byteSize: number;
2231
+ path: string;
2232
+ };
2233
+ catalogDigest(): string;
2234
+ private block;
2235
+ private expireAll;
2236
+ private emit;
2237
+ }
2238
+
2239
+ type AgentMode = 'adaptive' | 'manual' | 'off';
2240
+ type AgentRole = 'explorer' | 'reviewer' | 'patcher' | 'validator' | 'custom';
2241
+ type AgentStatus = 'queued' | 'starting' | 'running' | 'waiting_input' | 'waiting_permission' | 'completed' | 'failed' | 'stopped' | 'interrupted';
2242
+ type AgentApplicationStatus = 'not_applied' | 'applied' | 'conflicted';
2243
+ type AgentTopology = 'single' | 'parallel_research' | 'explore_then_patch' | 'patch_validate';
2244
+ type IssueQuality = 'clear' | 'ambiguous' | 'unknown';
2245
+ type AgentIsolation = 'workspace-readonly' | 'worktree';
2246
+ interface AgentProfile {
2247
+ name: string;
2248
+ role: AgentRole;
2249
+ description: string;
2250
+ allowedTools: string[];
2251
+ model?: string;
2252
+ maxTurns?: number;
2253
+ effort?: string;
2254
+ isolation: AgentIsolation;
2255
+ color?: string;
2256
+ }
2257
+ interface AgentActivitySummary {
2258
+ kind: 'thinking' | 'tool' | 'waiting' | 'compacting';
2259
+ label: string;
2260
+ toolName?: string;
2261
+ status: 'running' | 'completed' | 'failed';
2262
+ startedAt: number;
2263
+ }
2264
+ interface AgentActivity extends AgentActivitySummary {
2265
+ id: string;
2266
+ /** Full live tool call used by hosts to render nested child activity. */
2267
+ toolCall?: ToolCall;
2268
+ /** Bounded, display-only result projection for realtime host rendering. */
2269
+ result?: ToolResult;
2270
+ finishedAt?: number;
2271
+ }
2272
+ interface AgentPlanRecord {
2273
+ id: string;
2274
+ parentSessionId?: string;
2275
+ rootRunId?: string;
2276
+ parentRunId?: string;
2277
+ taskShape: string;
2278
+ issueQuality: IssueQuality;
2279
+ topology: AgentTopology;
2280
+ rationale: string;
2281
+ agentBudget: number;
2282
+ createdAt: number;
2283
+ }
2284
+ interface AgentRunMetrics {
2285
+ toolCalls: number;
2286
+ compactions: number;
2287
+ retries: number;
2288
+ }
2289
+ interface AgentSnapshot {
2290
+ id: string;
2291
+ repoRoot: string;
2292
+ repoHash: string;
2293
+ baseHead: string;
2294
+ commit: string;
2295
+ tree: string;
2296
+ ref: string;
2297
+ fingerprint: string;
2298
+ dirty: boolean;
2299
+ includeUntracked: boolean;
2300
+ manifest: Array<{
2301
+ status: string;
2302
+ path: string;
2303
+ }>;
2304
+ createdAt: number;
2305
+ }
2306
+ interface PatchCandidate {
2307
+ baseCommit: string;
2308
+ headCommit: string;
2309
+ branch: string;
2310
+ agentId: string;
2311
+ }
2312
+ interface AgentRecord {
2313
+ id: string;
2314
+ profile?: string;
2315
+ displayName?: string;
2316
+ profileDescription?: string;
2317
+ purpose?: string;
2318
+ requestedModel?: string;
2319
+ resolvedModel?: string;
2320
+ provider?: string;
2321
+ effort?: string;
2322
+ isolation?: AgentIsolation;
2323
+ currentActivity?: AgentActivitySummary;
2324
+ /** @deprecated Use profile. Retained for persisted version 1 compatibility. */
2325
+ name: string;
2326
+ role: AgentRole;
2327
+ /** @deprecated Use profileDescription. */
2328
+ description: string;
2329
+ parentSessionId?: string;
2330
+ rootRunId?: string;
2331
+ parentRunId?: string;
2332
+ /** See {@link AgentSpawnRequest.notifyParentOnCompletion}. Defaults to true. */
2333
+ notifyParentOnCompletion?: boolean;
2334
+ runId?: string;
2335
+ planId?: string;
2336
+ status: AgentStatus;
2337
+ /**
2338
+ * Set when this record was interrupted by process death rather than by a
2339
+ * decision, so a later start can tell "died mid-flight" from "genuinely
2340
+ * stopped" and re-drive it.
2341
+ */
2342
+ resumable?: boolean;
2343
+ /** The status held before the interruption, for the re-drive to reason about. */
2344
+ resumedFromStatus?: AgentStatus;
2345
+ applicationStatus: AgentApplicationStatus;
2346
+ worktree?: string;
2347
+ branch?: string;
2348
+ snapshotId?: string;
2349
+ prompt: string;
2350
+ referencedEvidenceIds: string[];
2351
+ /** Evidence published by this agent, kept separate from supplied inputs. */
2352
+ producedEvidenceIds?: string[];
2353
+ transcript: Message[];
2354
+ pendingMessages: string[];
2355
+ pendingQuestion?: UserQuestionRequest;
2356
+ pendingQuestionCreatedAt?: number;
2357
+ pendingPermission?: AgentPermissionRequest;
2358
+ result?: string;
2359
+ error?: string;
2360
+ stopReason?: string;
2361
+ patchCandidate?: PatchCandidate;
2362
+ usage?: Usage;
2363
+ createdAt: number;
2364
+ startedAt?: number;
2365
+ updatedAt: number;
2366
+ finishedAt?: number;
2367
+ /** Monotonic terminal-result generation used for durable parent delivery. */
2368
+ completionSequence?: number;
2369
+ /** Latest completion generation durably accepted by the parent host. */
2370
+ completionDeliveredSequence?: number;
2371
+ /** Monotonic execution generation for resumed/follow-up runs. */
2372
+ runSequence?: number;
2373
+ /** Start and usage for the current execution generation. */
2374
+ runStartedAt?: number;
2375
+ runUsage?: Usage;
2376
+ runMetrics?: AgentRunMetrics;
2377
+ /** Terminal outcome for the latest execution generation. */
2378
+ runOutcome?: AgentTerminalOutcome;
2379
+ /** Runtime-only warning when the latest state is waiting for durable storage. */
2380
+ durabilityWarning?: string;
2381
+ }
2382
+ interface AgentSummary {
2383
+ agentId: string;
2384
+ displayName: string;
2385
+ profile: string;
2386
+ /**
2387
+ * What this agent was spawned to do, bounded.
2388
+ *
2389
+ * The root's view of its own fan-out was `patcher-3 / interrupted / <no
2390
+ * summary>` — enough to know something happened, not enough to decide anything
2391
+ * — while purpose and planId sat unused on disk. After a compaction or two that
2392
+ * is all the parent has left of a delegated unit of work.
2393
+ */
2394
+ purpose?: string;
2395
+ /** Groups agents that share a snapshot and a plan. */
2396
+ planId?: string;
2397
+ status: AgentStatus;
2398
+ resolvedModel: string;
2399
+ isolation: AgentIsolation;
2400
+ currentActivity?: AgentActivitySummary;
2401
+ summary?: string;
2402
+ summaryCharacters?: number;
2403
+ summaryTruncated?: boolean;
2404
+ error?: string;
2405
+ errorCharacters?: number;
2406
+ errorTruncated?: boolean;
2407
+ usage?: Usage;
2408
+ createdAt: number;
2409
+ startedAt?: number;
2410
+ updatedAt: number;
2411
+ finishedAt?: number;
2412
+ durabilityWarning?: string;
2413
+ }
2414
+ interface AgentCompletion extends AgentSummary {
2415
+ evidenceIds: string[];
2416
+ applicationStatus?: AgentApplicationStatus;
2417
+ }
2418
+ interface AgentCompletionNotification {
2419
+ deliveryId: string;
2420
+ sequence: number;
2421
+ completion: AgentCompletion;
2422
+ parentSessionId?: string;
2423
+ rootRunId?: string;
2424
+ parentRunId?: string;
2425
+ runId?: string;
2426
+ outcome?: AgentTerminalOutcome;
2427
+ }
2428
+ type EvidenceKind = 'finding' | 'hypothesis' | 'test_result' | 'patch_candidate' | 'blocker';
2429
+ type EvidenceVerificationState = 'unverified' | 'verified' | 'rejected' | 'inconclusive';
2430
+ interface EvidenceReference {
2431
+ type: 'file' | 'command' | 'diff' | 'commit';
2432
+ value: string;
2433
+ }
2434
+ interface EvidenceItem {
2435
+ id: string;
2436
+ kind: EvidenceKind;
2437
+ sourceAgentId: string;
2438
+ summary: string;
2439
+ confidence: number;
2440
+ references: EvidenceReference[];
2441
+ verificationState: EvidenceVerificationState;
2442
+ patchCandidate?: PatchCandidate;
2443
+ reviewerAgentId?: string;
2444
+ verdict?: 'pass' | 'fail' | 'inconclusive';
2445
+ reviewNotes?: string;
2446
+ createdAt: number;
2447
+ reviewedAt?: number;
2448
+ updatedAt: number;
2449
+ }
2450
+ type AgentRuntimeEvent = {
2451
+ type: 'skill_lifecycle';
2452
+ event: SkillLifecycleEvent;
2453
+ } | {
2454
+ type: 'agent_status';
2455
+ agent: AgentSummary;
2456
+ parentSessionId?: string;
2457
+ rootRunId?: string;
2458
+ parentRunId?: string;
2459
+ } | {
2460
+ type: 'agent_activity';
2461
+ agentId: string;
2462
+ activity: AgentActivity;
2463
+ } | {
2464
+ type: 'agent_text_delta';
2465
+ agentId: string;
2466
+ text: string;
2467
+ } | {
2468
+ type: 'agent_message';
2469
+ agentId: string;
2470
+ message: Message;
2471
+ } | {
2472
+ type: 'agent_permission';
2473
+ agentId: string;
2474
+ request: AgentPermissionRequest;
2475
+ } | {
2476
+ type: 'agent_start';
2477
+ agent: AgentRecord;
2478
+ snapshot?: Pick<AgentSnapshot, 'id' | 'manifest'>;
2479
+ } | {
2480
+ type: 'agent_update';
2481
+ agent: AgentRecord;
2482
+ } | {
2483
+ type: 'agent_completion';
2484
+ notification: AgentCompletionNotification;
2485
+ } | {
2486
+ type: 'agent_result';
2487
+ agent: AgentRecord;
2488
+ } | {
2489
+ type: 'agent_question';
2490
+ agentId: string;
2491
+ request: UserQuestionRequest;
2492
+ } | {
2493
+ type: 'evidence_update';
2494
+ evidence: EvidenceItem;
2495
+ } | {
2496
+ type: 'agent_persistence';
2497
+ state: 'degraded' | 'recovered';
2498
+ reason: 'busy' | 'unavailable';
2499
+ errorCode?: string;
2500
+ message: string;
2501
+ agentId?: string;
2502
+ retrying: boolean;
2503
+ timestamp: number;
2504
+ } | {
2505
+ type: 'agent_apply';
2506
+ agentId: string;
2507
+ evidenceId: string;
2508
+ status: AgentApplicationStatus;
2509
+ commit?: string;
2510
+ error?: string;
2511
+ };
2512
+ interface AgentSpawnRequest {
2513
+ agent: string;
2514
+ description?: string;
2515
+ prompt: string;
2516
+ model?: string;
2517
+ planId?: string;
2518
+ evidenceIds?: string[];
2519
+ parentSessionId?: string;
2520
+ rootRunId?: string;
2521
+ parentRunId?: string;
2522
+ /**
2523
+ * Whether this agent's terminal result is delivered to `parentSessionId` as a
2524
+ * completion notification. Defaults to true.
2525
+ *
2526
+ * Set false by a host that owns the agent's output itself — `/review` renders
2527
+ * its own report, so re-delivering each reviewer's completion would bill an
2528
+ * extra model turn to re-narrate a report the user already has. Suppressing
2529
+ * *delivery* is deliberately separate from clearing `parentSessionId`: the
2530
+ * agent stays owned by the session, so it remains visible in that session's
2531
+ * agent surface while it runs.
2532
+ */
2533
+ notifyParentOnCompletion?: boolean;
2534
+ }
2535
+ interface AgentPermissionRequest {
2536
+ id: string;
2537
+ agentId: string;
2538
+ displayName: string;
2539
+ toolName: string;
2540
+ toolCall: ToolCall;
2541
+ createdAt: number;
2542
+ }
2543
+ interface AgentApplyResult {
2544
+ status: AgentApplicationStatus;
2545
+ commit?: string;
2546
+ error?: string;
2547
+ }
2548
+
2549
+ /**
2550
+ * A small, always-current file saying what a run is doing right now.
2551
+ *
2552
+ * `book status` answers "what was this asked to do and what has it spent" from the
2553
+ * session JSONL, which is durable and never rewritten. What the JSONL cannot
2554
+ * answer is *liveness*: at hour 90 the only on-disk signal that a run is healthy
2555
+ * is the transcript's mtime, and that advances at exactly the same rate for a
2556
+ * productive run, a refusal spin, and a run wedged on a permission prompt. The
2557
+ * default `--output-format text` writes nothing at all until the run terminates,
2558
+ * and the alternative is a firehose. Between silence and every reasoning delta
2559
+ * there was nothing an operator could leave on.
2560
+ *
2561
+ * So: one bounded record, rewritten at each turn boundary, carrying the handful of
2562
+ * facts a supervisor needs to decide "is this stuck" — plus a crash record written
2563
+ * from the exit path, because when a long run dies today nothing durable says why.
2564
+ *
2565
+ * Deliberately synchronous. The file is ~1 KB against turns that take seconds, and
2566
+ * the crash path runs inside `process.on('exit')`, where nothing async can run at
2567
+ * all. Written to a temp file and renamed, so a reader never sees a torn record.
2568
+ */
2569
+ declare const RUN_STATUS_VERSION = 1;
2570
+ interface RunStatusRecord {
2571
+ readonly version: typeof RUN_STATUS_VERSION;
2572
+ readonly sessionId: string;
2573
+ readonly runId: string;
2574
+ /** The process holding this run, so a reader can test liveness itself. */
2575
+ readonly pid: number;
2576
+ readonly startedAt: number;
2577
+ readonly updatedAt: number;
2578
+ readonly turn: number;
2579
+ readonly elapsedMs: number;
2580
+ /** Canonical name of the most recent tool call, if any. */
2581
+ readonly lastTool?: string;
2582
+ /** The todo currently marked in progress, if any. */
2583
+ readonly currentTodo?: string;
2584
+ readonly openTodos: number;
2585
+ readonly costUsd: number | null;
2586
+ readonly budgetUsd?: number;
2587
+ /** Free bytes on the volume holding the workspace, when the platform reports it. */
2588
+ readonly freeDiskBytes?: number;
2589
+ /** Set once the run has stopped, so a reader can tell finished from wedged. */
2590
+ readonly terminal?: {
2591
+ readonly status: string;
2592
+ readonly reason: string;
2593
+ readonly message?: string;
2594
+ };
2595
+ /**
2596
+ * Set when the process died without a terminal outcome. This is the difference
2597
+ * between "the objective finished" and "the socket died", which a supervisor
2598
+ * otherwise cannot tell apart.
2599
+ */
2600
+ readonly crash?: {
2601
+ readonly message: string;
2602
+ readonly at: number;
2603
+ };
2604
+ }
2605
+ interface RunStatusSeed {
2606
+ readonly sessionId: string;
2607
+ readonly runId: string;
2608
+ readonly workspace: string;
2609
+ readonly budgetUsd?: number;
2610
+ readonly startedAt: number;
2611
+ readonly home?: string;
2612
+ }
2613
+ interface RunStatusUpdate {
2614
+ readonly turn: number;
2615
+ readonly lastTool?: string;
2616
+ readonly currentTodo?: string;
2617
+ readonly openTodos: number;
2618
+ readonly costUsd: number | null;
2619
+ readonly terminal?: RunStatusRecord['terminal'];
2620
+ }
2621
+ /**
2622
+ * Keeps one run's status file current.
2623
+ *
2624
+ * Created by a host rather than by the loop: a subagent and a managed agent both
2625
+ * call `runAgentLoop` directly, and each writing its own liveness file would make
2626
+ * "is the run alive" ambiguous. The host that owns the session owns the file.
2627
+ */
2628
+ declare class RunStatusWriter {
2629
+ private readonly seed;
2630
+ private last;
2631
+ private crashHandlerInstalled;
2632
+ constructor(seed: RunStatusSeed);
2633
+ update(update: RunStatusUpdate, now?: number): void;
2634
+ /**
2635
+ * Record that the process is dying without a terminal outcome.
2636
+ *
2637
+ * Synchronous by necessity: this runs from `process.on('exit')`, where the event
2638
+ * loop is already closed and no promise will ever settle.
2639
+ */
2640
+ recordCrash(message: string, now?: number): void;
2641
+ /**
2642
+ * Install last-resort handlers so an unhandled fault leaves a record.
2643
+ *
2644
+ * There is no `uncaughtException` or `unhandledRejection` handler anywhere in the
2645
+ * codebase, and `index.ts` ends in a bare `program.parse()` whose returned promise
2646
+ * nothing awaits — so a rejection escaping the action handler becomes an unhandled
2647
+ * rejection and the operator gets a stack trace on a stderr they may have
2648
+ * redirected days ago. Returns a disposer.
2649
+ */
2650
+ installCrashHandlers(): () => void;
2651
+ snapshot(): RunStatusRecord;
2652
+ }
2653
+
2654
+ /**
2655
+ * A slash command loaded from .book/commands/*.md or ~/.book/commands/*.md.
2656
+ * Matches Claude Code's command loading model.
2657
+ */
2658
+ interface SlashCommand {
2659
+ /** File basename without extension — the command name invoked via /name */
2660
+ name: string;
2661
+ /** Human-readable description (from frontmatter `description`) */
2662
+ description: string;
2663
+ /** Argument hint shown in help and autocomplete (from frontmatter `argument-hint`) */
2664
+ argumentHint?: string;
2665
+ /** Named positional arguments for $name substitution (from frontmatter `arguments`) */
2666
+ arguments?: string[];
2667
+ /** Restrict which tools this command can use (from frontmatter `allowed-tools`) */
2668
+ allowedTools?: string[];
2669
+ /** Override model for this command (from frontmatter `model`) */
2670
+ model?: string;
2671
+ /** The raw Markdown body — injected as the prompt when invoked. */
2672
+ body: string;
2673
+ /** Source directory for priority/debugging (user vs project). */
2674
+ source: 'user' | 'project';
2675
+ /** Hide from / autocomplete and /help listing (default false). */
2676
+ isHidden?: boolean;
2677
+ /** Whether users can type /name to invoke (default true). */
2678
+ userInvocable?: boolean;
2679
+ }
2680
+ /**
2681
+ * A slash command a host performed itself instead of running a model turn for
2682
+ * it (`book -p "/review"`, `query({ prompt: '/review' })`).
2683
+ *
2684
+ * Both renderings travel together because both hosts consume it: `output` is
2685
+ * what a human reads, `data` is the command's own machine contract (`/review`
2686
+ * projects `ReviewJsonReport` from `review/host.ts`). Carrying it on the result
2687
+ * rather than only writing it to stdout is what keeps an SDK caller — whose
2688
+ * stdout is a discard sink — from paying for the work and receiving nothing.
2689
+ */
2690
+ interface HostCommandResult {
2691
+ /** Command name as the user typed it (an alias, possibly). */
2692
+ command: string;
2693
+ output?: string;
2694
+ data?: unknown;
2695
+ }
2696
+
2697
+ /** What triggered a compaction attempt. */
2698
+ type CompactTrigger = 'manual' | 'auto';
2699
+ /**
2700
+ * What the agent loop knows about the request behind a compaction and the
2701
+ * compactor cannot see for itself. Hosts forward these into `RunCompactOptions`
2702
+ * unchanged.
2703
+ */
2704
+ interface CompactRequestHints {
2705
+ /**
2706
+ * The provider has just rejected the request as too large. The compactor
2707
+ * must keep only the short tail: the residual tail was sized for a window
2708
+ * the provider has said it does not have, and the loop gets one retry.
2709
+ */
2710
+ recovery?: boolean;
2711
+ /**
2712
+ * Estimated tokens of the request outside the history: system prompt, tool
2713
+ * schemas, session state. The compaction target is measured against the
2714
+ * preflight gate, which counts them.
2715
+ */
2716
+ requestOverheadTokens?: number;
2717
+ /**
2718
+ * The loop's estimate of the request whose provider-measured usage is being
2719
+ * passed as pressure. Together they measure how far the estimator undercounts
2720
+ * this session's text, and the target shrinks by that ratio.
2721
+ */
2722
+ estimatedRequestTokens?: number;
2723
+ }
2724
+ /**
2725
+ * Result of `runCompact`. Discriminated so hosts can clear usage only on success
2726
+ * and avoid treating blocked/too-short outcomes as a history rewrite.
2727
+ */
2728
+ type CompactResult = {
2729
+ status: 'compacted';
2730
+ trigger: CompactTrigger;
2731
+ replacementHistory: Message[];
2732
+ summary: string;
2733
+ compactId: string;
2734
+ generation: number;
2735
+ checkpoint: ConversationCheckpointV2;
2736
+ checkpointVersion: 2;
2737
+ summarizedCount: number;
2738
+ retainedCount: number;
2739
+ postContextTokens: number;
2740
+ throughEventRef?: string;
2741
+ preContextTokens?: number;
2742
+ preMessageCount: number;
2743
+ strategy?: 'single-pass' | 'multi-pass' | 'degraded-fallback';
2744
+ modelCalls?: number;
2745
+ degraded?: boolean;
2746
+ warning?: string;
2747
+ } | {
2748
+ status: 'skipped';
2749
+ reason: 'too-short' | 'blocked' | 'disabled';
2750
+ message?: string;
2751
+ } | {
2752
+ status: 'failed';
2753
+ reason: 'provider-error' | 'empty-summary' | 'aborted' | 'unexpected-stream' | 'invalid-checkpoint' | 'budget-overflow';
2754
+ error: string;
2755
+ };
2756
+ interface CompactBoundary {
2757
+ id: string;
2758
+ trigger: CompactTrigger;
2759
+ transcriptOrdinal: number;
2760
+ preContextCount: number;
2761
+ postContextCount: number;
2762
+ preContextTokens?: number;
2763
+ postContextTokens?: number;
2764
+ generation: number;
2765
+ checkpointVersion: 1 | 2;
2766
+ timestamp: number;
2767
+ }
2768
+ interface RewindCheckpointMetadata {
2769
+ snapshotId?: string;
2770
+ gitHead?: string;
2771
+ entryCount?: number;
2772
+ logicalBytes?: number;
2773
+ codeUnavailableReason?: string;
2774
+ }
2775
+ interface RewindTarget extends RewindCheckpointMetadata {
2776
+ id: string;
2777
+ userEventId: string;
2778
+ prompt: string;
2779
+ attachments?: ImageAttachment[];
2780
+ timestamp: number;
2781
+ codeAvailable: boolean;
2782
+ }
2783
+ interface SessionRecord {
2784
+ type: 'user' | 'assistant' | 'local' | 'tool_call' | 'tool_result' | 'usage' | 'session_meta' | 'compact' | 'turn_checkpoint' | 'rewind' | 'plan';
2785
+ eventId?: string;
2786
+ timestamp: number;
2787
+ data: unknown;
2788
+ }
2789
+ /**
2790
+ * Payload stored in a SessionRecord of type `plan` — last record wins.
2791
+ *
2792
+ * The plan is the only long-horizon state with no other home: todos live on the
2793
+ * per-invocation ToolContext and the task graph on the in-process SessionRuntime,
2794
+ * so both die at process exit.
2795
+ *
2796
+ * Readers must tolerate absence: `SessionStore.load` dispatches record types with
2797
+ * no default branch, so an older binary ignores this record rather than failing.
2798
+ */
2799
+ interface PlanRecordData {
2800
+ version: 1;
2801
+ todos?: PersistedTodo[];
2802
+ tasks?: AgentTask[];
2803
+ }
2804
+ interface PersistedTodo {
2805
+ content: string;
2806
+ status: string;
2807
+ activeForm?: string;
2808
+ }
2809
+ interface CheckpointSourceRef {
2810
+ eventRef: string;
2811
+ quote?: string;
2812
+ toolResultRef?: string;
2813
+ }
2814
+ type CompactCoverageReason = 'pass-limit' | 'context-overflow' | 'invalid-checkpoint' | 'post-budget';
2815
+ interface ConversationCheckpointCoverage {
2816
+ /** This generation's coverage. Answers "is the checkpoint I just made sound?" */
2817
+ status: 'complete' | 'degraded';
2818
+ reasons: CompactCoverageReason[];
2819
+ /**
2820
+ * The accumulated record across every generation of this conversation. Kept
2821
+ * separate because merging it into `status` made the signal saturate: one
2822
+ * degraded generation marked every later one, so within a few hours of a long
2823
+ * run everything read `degraded` and the flag stopped carrying information.
2824
+ * Optional: absent on checkpoints written before the split.
2825
+ */
2826
+ lifetime?: {
2827
+ status: 'complete' | 'degraded';
2828
+ reasons: CompactCoverageReason[];
2829
+ };
2830
+ processedMessages: number;
2831
+ omittedMessages: number;
2832
+ partiallyProcessedMessages: number;
2833
+ firstProcessedEventRef?: string;
2834
+ lastProcessedEventRef?: string;
2835
+ }
2836
+ /**
2837
+ * One user-authored constraint, recorded verbatim by the host.
2838
+ *
2839
+ * Host-owned: the reducer never writes this. It is extracted from the user's
2840
+ * own turns by `agent/carried-ledger.ts` and re-attached after every
2841
+ * generation, which is what lets it outlive the model-authored narrative that
2842
+ * `fitCheckpoint` is free to rewrite.
2843
+ */
2844
+ interface CarriedConstraint {
2845
+ /** Stable across generations: derived from the normalized text. */
2846
+ id: string;
2847
+ /** The user's sentence, unrewritten. Truncated only past `CARRIED_ENTRY_MAX_CHARS`. */
2848
+ text: string;
2849
+ /**
2850
+ * How explicit the user was. `strong` is an unambiguous directive ("must",
2851
+ * "never", "do not"); `weak` is a softer steer ("only", "avoid", "prefer").
2852
+ * The cap evicts `weak` before `strong`.
2853
+ */
2854
+ strength: 'strong' | 'weak';
2855
+ /** The user turn the text came from. Never minimized away by the fitter. */
2856
+ source: CheckpointSourceRef;
2857
+ /** Generation at which the entry was first recorded. */
2858
+ firstSeenGeneration: number;
2859
+ /** Generation at which the user last restated it. */
2860
+ lastSeenGeneration: number;
2861
+ /** Id of the later entry that restates this one, when the host detected one. */
2862
+ supersededBy?: string;
2863
+ }
2864
+ /**
2865
+ * The Carried Ledger: a monotonic, host-owned record of what the user asked
2866
+ * for, ordered oldest to newest and never reordered.
2867
+ *
2868
+ * `fitCheckpoint` may not evict from it. Its growth is bounded by its own cap
2869
+ * (`capCarriedLedger`) instead, so "the fitter cannot touch it" does not turn
2870
+ * into "it eats the checkpoint budget".
2871
+ */
2872
+ interface CarriedLedger {
2873
+ version: 1;
2874
+ /** Oldest first. Later entries win over earlier ones on conflict. */
2875
+ constraints: CarriedConstraint[];
2876
+ /** Entries the cap had to drop. Non-zero means the ledger is lossy. */
2877
+ droppedCount?: number;
2878
+ }
2879
+ interface ConversationCheckpointV2 {
2880
+ version: 2;
2881
+ generation: number;
2882
+ state: {
2883
+ summary: string;
2884
+ status: 'active' | 'blocked' | 'complete' | 'unknown';
2885
+ };
2886
+ constraints: Array<{
2887
+ text: string;
2888
+ scope: 'global' | 'workspace' | 'task' | 'unknown';
2889
+ sources: CheckpointSourceRef[];
2890
+ }>;
2891
+ files: Array<{
2892
+ path: string;
2893
+ summary: string;
2894
+ sources: CheckpointSourceRef[];
2895
+ observation?: FileObservation;
2896
+ }>;
2897
+ episodes: Array<{
2898
+ task: string;
2899
+ outcome: string;
2900
+ status: 'complete' | 'partial' | 'failed' | 'unknown';
2901
+ sources: CheckpointSourceRef[];
2902
+ }>;
2903
+ openThreads: Array<{
2904
+ text: string;
2905
+ sources: CheckpointSourceRef[];
2906
+ }>;
2907
+ statistics: {
2908
+ summarizedMessages: number;
2909
+ retainedMessages: number;
2910
+ preTokens: number;
2911
+ postTokens: number;
2912
+ };
2913
+ /** Missing on older V2 checkpoints, which are treated as complete. */
2914
+ coverage?: ConversationCheckpointCoverage;
2915
+ /**
2916
+ * Host-owned verbatim user constraints. Absent on checkpoints written before
2917
+ * the Carried Ledger, and absent when the conversation stated no constraint.
2918
+ */
2919
+ carried?: CarriedLedger;
2920
+ }
2921
+ interface SessionMeta {
2922
+ id: string;
2923
+ name?: string;
2924
+ cwd: string;
2925
+ createdAt: number;
2926
+ updatedAt: number;
2927
+ messageCount: number;
2928
+ }
2929
+ interface LoadedSession {
2930
+ transcript: Message[];
2931
+ contextHistory: Message[];
2932
+ compactBoundaries: CompactBoundary[];
2933
+ rewindTargets: RewindTarget[];
2934
+ activeEventIds: string[];
2935
+ meta: SessionMeta;
2936
+ /**
2937
+ * The newest `plan` record, if the session ever wrote one. Absent means no plan
2938
+ * was ever recorded; present-but-empty means it was deliberately cleared. That
2939
+ * distinction is what lets a resumed run tell "nothing to do" from "my plan did
2940
+ * not survive the restart".
2941
+ */
2942
+ plan?: PlanRecordData;
2943
+ /**
2944
+ * Token totals recorded by earlier processes of this session, summed from the
2945
+ * `usage` records. USD is deliberately not stored: pricing changes between
2946
+ * processes, so cost is re-derived from these tokens at bootstrap.
2947
+ */
2948
+ carriedUsage?: Usage;
2949
+ /** Models those tokens were spent on, for re-deriving the cost. */
2950
+ carriedModels?: string[];
2951
+ /** @deprecated Use contextHistory. */
2952
+ history: Message[];
2953
+ }
2954
+ interface SessionHistorySearchResult {
2955
+ ref: string;
2956
+ role: Message['role'];
2957
+ preview: string;
2958
+ timestamp: number;
2959
+ }
2960
+ /** Minimal interface for SessionStore, defined here to avoid circular imports. */
2961
+ interface SessionStoreInterface {
2962
+ create(meta: {
2963
+ cwd: string;
2964
+ name?: string;
2965
+ id?: string;
2966
+ }): string;
2967
+ append(id: string, record: SessionRecord): void;
2968
+ patchMeta(id: string, patch: {
2969
+ name?: string;
2970
+ }): void;
2971
+ touch(id: string): void;
2972
+ load(id: string): LoadedSession;
2973
+ readRecords?(id: string): SessionRecord[];
2974
+ fork?(sourceId: string, meta: {
2975
+ cwd: string;
2976
+ name?: string;
2977
+ id?: string;
2978
+ }): string;
2979
+ searchCurrent?(id: string, query: string, limit?: number): SessionHistorySearchResult[];
2980
+ readCurrent?(id: string, refs: string[]): Array<{
2981
+ ref: string;
2982
+ content: string;
2983
+ }>;
2984
+ listRewindTargets?(id: string): RewindTarget[];
2985
+ listSnapshotReferences?(cwd: string): Set<string>;
2986
+ list(): SessionMeta[];
2987
+ findByName(name: string): SessionMeta | undefined;
2988
+ findById(id: string): SessionMeta | undefined;
2989
+ mostRecentInCwd(cwd: string): SessionMeta | undefined;
2990
+ saveImageAttachment?(sessionId: string, image: {
2991
+ bytes: Uint8Array;
2992
+ mediaType: ImageAttachment['mediaType'];
2993
+ displayName?: string;
2994
+ }): ImageAttachment;
2995
+ readImageAttachment?(sessionId: string, attachment: ImageAttachment): Uint8Array;
2996
+ cleanup(days: number, preserveIds?: ReadonlySet<string>): number;
2997
+ }
2998
+
2999
+ type ProviderContentPart = {
3000
+ type: 'text';
3001
+ text: string;
3002
+ } | {
3003
+ type: 'image';
3004
+ mediaType: 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
3005
+ data: string;
3006
+ };
3007
+ interface SystemPromptZones {
3008
+ /** Cacheable session-stable system prompt prefix. */
3009
+ cachedPrefix: string;
3010
+ /** Dynamic per-turn system prompt suffix, such as the active todo list. */
3011
+ dynamicSuffix: string;
3012
+ }
3013
+ interface ProviderMessage {
3014
+ role: string;
3015
+ content: string | ProviderContentPart[] | SystemPromptZones | null;
3016
+ /** Prior assistant reasoning to replay as provider context when supported. */
3017
+ reasoningContent?: string;
3018
+ providerMetadata?: ProviderMessageMetadata;
3019
+ tool_calls?: Array<{
3020
+ id: string;
3021
+ type: 'function';
3022
+ function: {
3023
+ name: string;
3024
+ arguments: string;
3025
+ };
3026
+ }>;
3027
+ tool_call_id?: string;
3028
+ }
3029
+ interface ProviderStreamEvent {
3030
+ type: 'text' | 'reasoning' | 'tool_call' | 'done' | 'error';
3031
+ content?: string;
3032
+ reasoning?: string;
3033
+ toolCall?: ToolCall;
3034
+ error?: string;
3035
+ /** Stable machine-readable provider/transport classification when available. */
3036
+ errorCode?: string;
3037
+ usage?: Usage;
3038
+ /** Provider response identity when the stream exposes it. */
3039
+ responseModel?: string;
3040
+ responseId?: string;
3041
+ finishReasons?: string[];
3042
+ providerMetadata?: ProviderMessageMetadata;
3043
+ }
3044
+ interface ProviderResponseMetadata {
3045
+ provider: string;
3046
+ requestedModel: string;
3047
+ responseModel?: string;
3048
+ responseId?: string;
3049
+ finishReasons?: string[];
3050
+ }
3051
+ interface AgentLoopCallbacks {
3052
+ onText: (text: string) => void;
3053
+ /** Called with provider-native reasoning deltas, displayed separately from answer text. */
3054
+ onReasoning?: (text: string) => void;
3055
+ onToolCall: (call: ToolCall) => void;
3056
+ onToolResult: (result: ToolResult) => void;
3057
+ onError: (error: string) => void;
3058
+ /**
3059
+ * The current attempt was abandoned and the same turn is being retried, so
3060
+ * everything already delivered for it should be dropped.
3061
+ *
3062
+ * Deltas are handed over as they arrive and cannot be recalled, so without
3063
+ * this a retried turn leaves the abandoned attempt's text sitting in front of
3064
+ * its replacement while persisted history keeps only the replacement. Holding
3065
+ * the deltas back instead would render a long thinking phase as silence, which
3066
+ * is the failure this whole path exists to stop showing people.
3067
+ */
3068
+ onAttemptDiscarded?: () => void;
3069
+ onTurnStart: (turn: number) => void;
3070
+ onDone: () => void;
3071
+ /** Final runtime outcome. Emitted once; onError remains diagnostic only. */
3072
+ onTerminal?: (outcome: AgentTerminalOutcome) => void;
3073
+ onPermissionRequired: (toolCall: ToolCall) => Promise<PermissionResult | PermissionDecision>;
3074
+ /** Reads the host's current permission mode while an agent loop is active. */
3075
+ getMode?: () => PermissionMode;
3076
+ /** @deprecated use onUsage for real token counts from the API. */
3077
+ onTokenCount?: (count: number) => void;
3078
+ onUsage?: (usage: Usage, response?: ProviderResponseMetadata) => void;
3079
+ /** Called when a tool changes the live permission mode. */
3080
+ onModeChange?: (mode: PermissionMode) => void;
3081
+ /** Called when ExitPlanMode submits a plan for host approval. */
3082
+ onPlanApprovalRequired?: (plan: string) => Promise<PlanApprovalResult>;
3083
+ /**
3084
+ * Called when the user approves a plan with "fresh context" (`approve-fresh`).
3085
+ * The loop restores the pre-plan mode and stops the current turn; the host is
3086
+ * responsible for reseeding a fresh context with the approved plan.
3087
+ */
3088
+ onPlanHandoff?: (handoff: {
3089
+ plan: string;
3090
+ mode: PermissionMode;
3091
+ }) => void;
3092
+ /** Called when the root agent or a Task subagent needs structured user input. */
3093
+ onUserQuestionRequired?: UserQuestionHandler;
3094
+ /**
3095
+ * Called when the context approaches its limit mid-loop, and as the last
3096
+ * resort after the provider rejects a request as too large (`hints.recovery`).
3097
+ * Return a CompactResult; only `status: 'compacted'` replaces loop history.
3098
+ * Hosts forward `hints` into `RunCompactOptions` unchanged.
3099
+ */
3100
+ onCompact?: (history: Message[], usage: Usage | null, hints?: CompactRequestHints) => Promise<CompactResult>;
3101
+ /**
3102
+ * Called when an assistant turn (including tool results) is finalized.
3103
+ * Hosts should persist this immediately rather than slicing the final history.
3104
+ */
3105
+ onAssistantMessageComplete?: (message: Message) => void;
3106
+ /** Called after each tool execution with the current agent todo list. */
3107
+ onTodos?: (todos: Array<{
3108
+ content: string;
3109
+ status: string;
3110
+ activeForm?: string;
3111
+ }>) => void;
3112
+ /**
3113
+ * Fires after a tool wave that may have mutated the task graph, mirroring
3114
+ * `onTodos`. The task tools have no callback of their own, so this is the seam a
3115
+ * host uses to persist the plan; it fires per wave, not per mutation.
3116
+ */
3117
+ onTasks?: (tasks: AgentTask[]) => void;
3118
+ /**
3119
+ * A user-role message the loop itself appended (a continuation, a work-state
3120
+ * refresh). Hosts write the user message before `send`, so without this the only
3121
+ * writer to the session file never sees these and a `--resume` reads a
3122
+ * conversation with unexplained gaps between assistant turns.
3123
+ */
3124
+ onUserMessageAppended?: (message: Message) => void;
3125
+ /** Called when a transport-level retry starts (delay > 0). */
3126
+ onRetry?: (phase: RetryPhase, attempt: number, max: number, delayMs: number) => void;
3127
+ /** Called when the response stream stalls (no data for streamStallTimeoutMs). */
3128
+ onStreamStall?: (countdownMs: number) => void;
3129
+ /** Called when data resumes after a stream stall. */
3130
+ onStreamResume?: () => void;
3131
+ /**
3132
+ * Called when the user picks "always allow" at a permission prompt — so the
3133
+ * host can persist a Tool(specifier) rule (CC-aligned approval flow). The
3134
+ * `rule` string is canonical (e.g. `Bash(npm install)` or `Read`).
3135
+ */
3136
+ onPersistPermissionRule?: (rule: string) => void;
3137
+ /** Called when a hook lifecycle event fires (for --include-hook-events in stream-json mode). */
3138
+ onHookEvent?: (event: string, payload: Record<string, unknown>) => void;
3139
+ /** Called for managed-agent lifecycle, evidence, and application events. */
3140
+ onAgentEvent?: (event: AgentRuntimeEvent) => void;
3141
+ }
3142
+
3143
+ interface ProviderStreamOptions {
3144
+ signal?: AbortSignal;
3145
+ onRetry?: (attempt: number, max: number, delayMs: number) => void;
3146
+ onStreamStall?: (countdownMs: number) => void;
3147
+ onStreamResume?: () => void;
3148
+ maxOutputTokens?: number;
3149
+ }
3150
+ interface Provider {
3151
+ readonly id: string;
3152
+ stream(config: AgentConfig, messages: ProviderMessage[], tools: ToolDefinition[], options?: ProviderStreamOptions): AsyncGenerator<ProviderStreamEvent>;
3153
+ }
3154
+
3155
+ interface SessionHistoryCapability {
3156
+ store: SessionStoreInterface;
3157
+ sessionId: () => string;
3158
+ }
3159
+
3160
+ interface PreparedToolCall {
3161
+ call: ToolCall;
3162
+ tool: ToolDefinition;
3163
+ timeoutMs: number;
3164
+ }
3165
+ type PrepareToolCallResult = {
3166
+ status: 'ready';
3167
+ prepared: PreparedToolCall;
3168
+ } | {
3169
+ status: 'rejected';
3170
+ result: ToolResult;
3171
+ };
3172
+ declare function createRegistry(): {
3173
+ normalizeCall: (call: ToolCall) => ToolCall;
3174
+ register(tool: ToolDefinition): void;
3175
+ registerAll(toolList: ToolDefinition[]): void;
3176
+ getTool(name: string): ToolDefinition | undefined;
3177
+ getDefinitions(): ToolDefinition[];
3178
+ prepare(call: ToolCall, context: ToolContext): PrepareToolCallResult;
3179
+ executePrepared(prepared: PreparedToolCall, context: ToolContext, maxRetries?: number): Promise<ToolResult>;
3180
+ execute(call: ToolCall, context: ToolContext, maxRetries?: number): Promise<ToolResult>;
3181
+ };
3182
+ type ToolRegistry = ReturnType<typeof createRegistry>;
3183
+
3184
+ declare function createDefaultRegistry(capabilities?: {
3185
+ sessionHistory?: SessionHistoryCapability;
3186
+ agents?: boolean;
3187
+ }): ToolRegistry;
3188
+
3189
+ type TodoStatus = 'pending' | 'in_progress' | 'completed';
3190
+ interface Todo {
3191
+ content: string;
3192
+ status: TodoStatus;
3193
+ activeForm?: string;
3194
+ }
3195
+
3196
+ /**
3197
+ * A subagent definition loaded from .book/agents/<name>.md or
3198
+ * ~/.book/agents/<name>.md. Matches Claude Code's subagent model.
3199
+ */
3200
+ interface SubagentDef {
3201
+ /** Name used for invocation via the Task tool. */
3202
+ name: string;
3203
+ /** Human-readable description. */
3204
+ description: string;
3205
+ /** Strict capability rules. Empty denies all tools; `*` explicitly inherits. */
3206
+ allowedTools: string[];
3207
+ /** Override model for this subagent (optional). */
3208
+ model?: string;
3209
+ /** Maximum turns for this subagent. Undefined = unlimited. */
3210
+ maxTurns?: number;
3211
+ effort?: string;
3212
+ isolation?: 'workspace-readonly' | 'worktree';
3213
+ color?: string;
3214
+ unknownTools?: string[];
3215
+ /** The raw Markdown body — injected as the subagent's system prompt. */
3216
+ body: string;
3217
+ /** Source directory. */
3218
+ source: 'user' | 'project' | 'builtin';
3219
+ }
3220
+
3221
+ interface StaticDiscovery {
3222
+ fingerprint: string;
3223
+ skills: ReturnType<typeof discoverSkills>;
3224
+ commands: SlashCommand[];
3225
+ projectInstructions: string;
3226
+ agents: SubagentDef[];
3227
+ }
3228
+ declare class AgentContextCache {
3229
+ private readonly discoveries;
3230
+ private readonly turnFingerprints;
3231
+ private readonly turnGit;
3232
+ beginTurn(): void;
3233
+ invalidateGit(workspace: string): void;
3234
+ invalidateWorkspace(workspace: string): void;
3235
+ discovery(workspace: string): StaticDiscovery;
3236
+ git(workspace: string, signal?: AbortSignal): Promise<string>;
3237
+ }
3238
+
3239
+ /** Session-scoped concurrency bound for tools explicitly reviewed as parallel-safe. */
3240
+ declare class ToolExecutionScheduler {
3241
+ private active;
3242
+ private limit;
3243
+ private readonly queue;
3244
+ constructor(limit?: number);
3245
+ setLimit(limit: number): void;
3246
+ run<T>(task: () => Promise<T>, signal?: AbortSignal): Promise<T>;
3247
+ private acquire;
3248
+ private releasePermit;
3249
+ private drain;
3250
+ }
3251
+
3252
+ type BudgetStatus = AgentRunAccounting['budgetStatus'];
3253
+ interface CarriedSpend {
3254
+ usage: Usage | null;
3255
+ /** Null when a prior process could not price some of its spend. */
3256
+ costUsd: number | null;
3257
+ }
3258
+ interface BudgetCheck {
3259
+ allowed: boolean;
3260
+ status: Exclude<BudgetStatus, 'not_configured'>;
3261
+ message?: string;
3262
+ }
3263
+ /**
3264
+ * In-memory accounting shared by all executions in one logical session.
3265
+ *
3266
+ * Missing provider-attempt usage is recorded only when such an attempt actually occurs.
3267
+ */
3268
+ declare class RunAccounting {
3269
+ private readonly roots;
3270
+ private ensureExecution;
3271
+ startRoot(context: AgentRunContext, budgetUsd?: number): void;
3272
+ /**
3273
+ * Restore spend recorded by earlier processes of the same root.
3274
+ *
3275
+ * Idempotent per root: the seed replaces rather than accumulates, so calling it
3276
+ * twice with the same restored total cannot double-charge the budget.
3277
+ */
3278
+ seedRoot(rootRunId: string, carried: CarriedSpend): void;
3279
+ startExecution(context: AgentRunContext): void;
3280
+ record(context: AgentRunContext, usage: Usage, metadata: ProviderResponseMetadata): void;
3281
+ markUsageUnknown(context: AgentRunContext, metadata: ProviderResponseMetadata, source: string): void;
3282
+ checkBeforeModelCall(rootRunId: string, requestedModel: string): BudgetCheck;
3283
+ /** Has a root by this id been started in THIS process? */
3284
+ hasRoot(rootRunId: string): boolean;
3285
+ /**
3286
+ * The live budgeted root that work from a dead process should join.
3287
+ *
3288
+ * A managed agent re-driven after a restart still carries the previous
3289
+ * process's `rootRunId`. Nothing recreates that root here, so `startExecution`
3290
+ * would mint it with no `budgetUsd` and `checkBeforeModelCall` would then allow
3291
+ * every call unconditionally — while the host's own gate stayed green, because
3292
+ * the spend never reached its inclusive total either. Undefined when the answer
3293
+ * is ambiguous, so adoption is never a guess.
3294
+ */
3295
+ budgetedRootRunId(): string | undefined;
3296
+ snapshotRun(runId: string): AgentRunAccounting | undefined;
3297
+ snapshotRoot(rootRunId: string): AgentRunAccounting;
3298
+ snapshotAll(): AgentRunAccounting;
3299
+ private makeSnapshot;
3300
+ }
3301
+
3302
+ interface Sandbox {
3303
+ /**
3304
+ * Resolve a shell command into a sandboxed argv spawn.
3305
+ *
3306
+ * `workspaceRoot` is the only directory bound writable by default, and it is
3307
+ * deliberately not the command's working directory: `workdir` is a
3308
+ * model-supplied tool argument, and binding it would let the model widen its
3309
+ * own sandbox to any path — `workdir: "/"` would shadow every other mount and
3310
+ * hand back the whole host filesystem, read-write. Callers must keep the
3311
+ * working directory inside the workspace; extra paths go through
3312
+ * `sandbox.filesystem.allowWrite`.
3313
+ *
3314
+ * Returns null if the sandbox is unavailable.
3315
+ */
3316
+ wrap(command: string, workspaceRoot: string): CommandExecution | null;
3317
+ /** One-line description of the policy actually enforced, for diagnostics. */
3318
+ describe(): string;
3319
+ }
3320
+
3321
+ interface SessionRuntimeOptions {
3322
+ tasks?: AgentTask[];
3323
+ todos?: Todo[];
3324
+ backgroundShells?: BackgroundShellStore;
3325
+ fileObservationLedger?: Map<string, FileObservation>;
3326
+ toolDiscoveryState?: ToolDiscoveryState;
3327
+ agentContextCache?: AgentContextCache;
3328
+ toolExecutionScheduler?: ToolExecutionScheduler;
3329
+ runAccounting?: RunAccounting;
3330
+ runAmbientSnapshots?: Map<string, AgentRunAmbientSnapshot>;
3331
+ traceId?: string;
3332
+ skillRegistry?: SkillRegistry;
3333
+ /** Skill root discovery options, shared by the registry and the watcher so they agree. */
3334
+ skillDiscoveryOptions?: DiscoverSkillsOptions;
3335
+ }
3336
+ /** Mutable resources owned by one logical agent session. */
3337
+ declare class SessionRuntime {
3338
+ readonly tasks: AgentTask[];
3339
+ /**
3340
+ * The session's todo list. A readonly *binding* over a mutable array, matching
3341
+ * `tasks` — the loop seeds `ToolContext.todos` from it per invocation and
3342
+ * TodoWrite mutates it in place, so the plan survives across invocations within
3343
+ * one process and can be rehydrated from a `plan` record after a restart.
3344
+ */
3345
+ readonly todos: Todo[];
3346
+ /**
3347
+ * Set by a host that resumed a session with prior work but no persisted plan.
3348
+ * Surfaced once in `<session-state>` so a dropped plan is a visible event.
3349
+ */
3350
+ planUnrestored: boolean;
3351
+ readonly backgroundShells: BackgroundShellStore;
3352
+ readonly fileObservationLedger: Map<string, FileObservation>;
3353
+ readonly toolDiscoveryState: ToolDiscoveryState;
3354
+ readonly agentContextCache: AgentContextCache;
3355
+ readonly toolExecutionScheduler: ToolExecutionScheduler;
3356
+ readonly traceId: string;
3357
+ readonly runAccounting: RunAccounting;
3358
+ readonly runAmbientSnapshots: Map<string, AgentRunAmbientSnapshot>;
3359
+ readonly shellManager: ShellJobManager;
3360
+ private sandboxInstance?;
3361
+ private skillRegistry?;
3362
+ private readonly skillDiscoveryOptions;
3363
+ private skillWatcher?;
3364
+ private skillWatcherWorkspace?;
3365
+ private skillCatalogDirty;
3366
+ private skillWatcherFailure?;
3367
+ private readonly skillChangeListeners;
3368
+ /** Advisory memory of recent identical tool failures (registry circuit breaker). */
3369
+ readonly recentToolFailures: Map<string, number>;
3370
+ /** Per-session tool call/failure counters keyed by canonical tool name. */
3371
+ readonly toolCallStats: Map<string, {
3372
+ calls: number;
3373
+ failures: Record<string, number>;
3374
+ }>;
3375
+ agentManager?: AgentManager;
3376
+ private readonly abortControllers;
3377
+ private readonly timers;
3378
+ private readonly childProcesses;
3379
+ private disposed;
3380
+ constructor(options?: SessionRuntimeOptions);
3381
+ /**
3382
+ * The session's sandbox, built once per distinct settings object.
3383
+ *
3384
+ * Building it per Bash call would re-emit its startup diagnostics — the
3385
+ * unenforceable-domain-policy and skipped-path warnings — on every single
3386
+ * command, interleaved into the Ink render or the headless stderr stream.
3387
+ */
3388
+ sandbox(settings: ResolvedSettings['sandbox']): Sandbox | null;
3389
+ skills(workspace: string, settings: SkillSettings): SkillRegistry;
3390
+ reloadSkills(workspace: string, settings: SkillSettings, cause?: string): SkillRegistry;
3391
+ consumeSkillChanges(workspace: string, settings: SkillSettings): SkillRegistry;
3392
+ subscribeSkillChanges(workspace: string, listener: () => void, enabled?: boolean): () => void;
3393
+ get skillWatcherError(): string | undefined;
3394
+ inspectSkills(currentTurn?: number): SkillRegistrySnapshot | undefined;
3395
+ private ensureSkillWatcher;
3396
+ private stopSkillWatcher;
3397
+ trackAbortController(controller: AbortController): AbortController;
3398
+ releaseAbortController(controller: AbortController): void;
3399
+ trackTimer(timer: NodeJS.Timeout): NodeJS.Timeout;
3400
+ releaseTimer(timer: NodeJS.Timeout): void;
3401
+ trackChildProcess(child: ChildProcess$1): ChildProcess$1;
3402
+ releaseChildProcess(child: ChildProcess$1): void;
3403
+ recordRunAmbientSnapshot(runId: string, snapshot: AgentRunAmbientSnapshot): AgentRunAmbientSnapshot;
3404
+ snapshotRunAmbient(runId: string): AgentRunAmbientSnapshot | undefined;
3405
+ get isDisposed(): boolean;
3406
+ /** Dispose every resource registered by this session exactly once. */
3407
+ dispose(reason?: string): void;
3408
+ }
3409
+
3410
+ declare function runAgentLoop(config: AgentConfig, registry: ToolRegistry, userMessage: string, history: Message[], callbacks: AgentLoopCallbacks, mode?: string, options?: {
3411
+ signal?: AbortSignal;
3412
+ isNewSession?: boolean;
3413
+ /** When false, the host owns SessionStart/SessionEnd boundaries. */
3414
+ manageSessionHooks?: boolean;
3415
+ commands?: SlashCommand[];
3416
+ allowedTools?: string[];
3417
+ modelOverride?: string;
3418
+ /** User-facing text to retain in history when userMessage is expanded context. */
3419
+ displayMessage?: string;
3420
+ /** Stable host-assigned identity for the user event. */
3421
+ userMessageId?: string;
3422
+ userMessageTimestamp?: number;
3423
+ userFileObservations?: Message['fileObservations'];
3424
+ userAttachments?: Message['attachments'];
3425
+ resolveAttachment?: (attachment: ImageAttachment) => Promise<Uint8Array> | Uint8Array;
3426
+ userMessageKind?: Message['kind'];
3427
+ /** The prompt was resolved from a command or delegated, not typed by the user. */
3428
+ userMessageDerived?: boolean;
3429
+ /** Synthetic host notifications bypass user-authored prompt hooks and memory capture. */
3430
+ skipUserPromptHooks?: boolean;
3431
+ /** Host identity for each streamed assistant turn. */
3432
+ assistantMessageId?: (turn: number) => string | undefined;
3433
+ /** True when this loop is a subagent (Task tool) invocation — skips memory auto-capture. */
3434
+ isSubagent?: boolean;
3435
+ /**
3436
+ * True when nobody is watching this run, so a refusal cannot be resolved by
3437
+ * asking. Set by headless and the SDK; the TUI leaves it false.
3438
+ */
3439
+ unattended?: boolean;
3440
+ /**
3441
+ * Liveness writer for the run's status file, supplied by the host that owns
3442
+ * the session. Subagents and managed agents call this loop directly, so
3443
+ * letting the loop create its own would make "is the run alive" ambiguous.
3444
+ */
3445
+ statusWriter?: RunStatusWriter;
3446
+ /** Display-only observer for tools invoked by this subagent. */
3447
+ nestedToolObserver?: NestedToolObserver;
3448
+ /** Trace id of the Task invocation that launched this subagent loop. */
3449
+ parentToolTraceId?: string;
3450
+ /** Nested agent names from the root agent to this loop. */
3451
+ agentPath?: string[];
3452
+ /** Extra managed-agent identity and policy appended to the system prompt. */
3453
+ systemPromptAppend?: string;
3454
+ /**
3455
+ * Injected by tests. The loop reads it only for durations — how long this
3456
+ * run has been going, how long a tool took — never for a stamp anything
3457
+ * else will read.
3458
+ */
3459
+ clock?: Clock;
3460
+ /** Hide delegation discovery from child agents. */
3461
+ hideAgents?: boolean;
3462
+ /** Managed child identity and parent-session attribution. */
3463
+ agentId?: string;
3464
+ agentRole?: AgentRole;
3465
+ parentSessionId?: string;
3466
+ /** Non-owning managed-agent coordinator used by child-only evidence tools. */
3467
+ agentManager?: ToolContext['agentManager'];
3468
+ /** Mutable resources owned by the logical session. */
3469
+ runtime?: SessionRuntime;
3470
+ /** Frozen attribution for this root or linked child execution. */
3471
+ runContext?: AgentRunContext;
3472
+ /** Override user-local oversized tool-output storage (primarily for isolated hosts/tests). */
3473
+ toolOutputRoot?: string;
3474
+ /** Override user-local tool-use telemetry storage (primarily for isolated hosts/tests). */
3475
+ toolTelemetryRoot?: string;
3476
+ /** User-global store for learned context window ceilings (primarily for isolated tests). */
3477
+ modelWindowStore?: ModelWindowStore;
3478
+ provider?: Provider;
3479
+ }): Promise<Message[]>;
3480
+
3481
+ interface RunCompactOptions extends CompactRequestHints {
3482
+ trigger: CompactTrigger;
3483
+ focus?: string;
3484
+ upcomingUserIntent?: string;
3485
+ sessionId?: string;
3486
+ preContextTokens?: number;
3487
+ signal?: AbortSignal;
3488
+ onHookEvent?: (event: string, payload: Record<string, unknown>) => void;
3489
+ minMessages?: number;
3490
+ provider?: Provider;
3491
+ beforeModelCall?: (model: string) => {
3492
+ allowed: boolean;
3493
+ message?: string;
3494
+ };
3495
+ onUsage?: (usage: Usage, metadata: ProviderResponseMetadata) => void;
3496
+ onUsageMissing?: (metadata: ProviderResponseMetadata) => void;
3497
+ /** Overrides the reducer output cap for controlled evaluation experiments. */
3498
+ checkpointMaxTokens?: number;
3499
+ /** Overrides reducer reasoning effort for controlled evaluation experiments. */
3500
+ effort?: AgentConfig['effort'];
3501
+ }
3502
+ declare function runCompact(config: AgentConfig, history: readonly Message[], options: RunCompactOptions): Promise<CompactResult>;
3503
+
3504
+ declare function findGitRoot(workspace: string): Promise<string | undefined>;
3505
+ declare function createSyntheticSnapshot(workspace: string, includeUntracked?: boolean): Promise<AgentSnapshot>;
3506
+ declare function createAgentWorktree(snapshot: AgentSnapshot, agentId: string, worktreeRoot?: string, startCommit?: string): Promise<{
3507
+ path: string;
3508
+ branch: string;
3509
+ }>;
3510
+ declare function removeSnapshotRef(snapshot: AgentSnapshot): Promise<void>;
3511
+ declare function checkoutAgentCommit(worktree: string, commit: string): Promise<void>;
3512
+ declare function commitAgentWork(record: AgentRecord, snapshot: AgentSnapshot): Promise<PatchCandidate | undefined>;
3513
+ declare function applyVerifiedCandidate(snapshot: AgentSnapshot, candidate: PatchCandidate): Promise<AgentApplyResult>;
3514
+
3515
+ type AtomicWriteOperation = 'lock' | 'serialize' | 'write' | 'fsync' | 'rename';
3516
+ type AtomicWriteResult = {
3517
+ status: 'ok';
3518
+ target: string;
3519
+ attempts: number;
3520
+ elapsedMs: number;
3521
+ } | {
3522
+ status: 'busy';
3523
+ target: string;
3524
+ tempPath?: string;
3525
+ operation: 'lock' | 'rename';
3526
+ attempts: number;
3527
+ elapsedMs: number;
3528
+ } | {
3529
+ status: 'unavailable';
3530
+ target: string;
3531
+ tempPath?: string;
3532
+ operation: AtomicWriteOperation;
3533
+ errorCode?: string;
3534
+ message: string;
3535
+ attempts: number;
3536
+ elapsedMs: number;
3537
+ };
3538
+ interface AtomicLockOwner {
3539
+ schemaVersion: 1;
3540
+ instanceId: string;
3541
+ pid: number;
3542
+ hostname: string;
3543
+ createdAt: number;
3544
+ }
3545
+ interface AtomicJsonWriterOptions {
3546
+ instanceId: string;
3547
+ pid?: number;
3548
+ hostname: string;
3549
+ deadlineMs?: number;
3550
+ now?: () => number;
3551
+ randomId?: () => string;
3552
+ sleep?: (milliseconds: number) => void;
3553
+ isLockOwnerAlive?: (owner: AtomicLockOwner) => boolean;
3554
+ staleLockMs?: number;
3555
+ onStaleLock?: (target: string) => void;
3556
+ fs?: Partial<AtomicJsonFileSystem>;
3557
+ }
3558
+ interface AtomicJsonFileSystem {
3559
+ closeSync: typeof closeSync;
3560
+ existsSync: typeof existsSync;
3561
+ fsyncSync: typeof fsyncSync;
3562
+ openSync: typeof openSync;
3563
+ readFileSync: typeof readFileSync;
3564
+ renameSync: typeof renameSync;
3565
+ statSync: typeof statSync;
3566
+ unlinkSync: typeof unlinkSync;
3567
+ writeFileSync: typeof writeFileSync;
3568
+ }
3569
+ declare class AtomicJsonWriter {
3570
+ private readonly fs;
3571
+ private readonly instanceId;
3572
+ private readonly pid;
3573
+ private readonly hostname;
3574
+ private readonly deadlineMs;
3575
+ private readonly staleLockMs;
3576
+ private readonly now;
3577
+ private readonly randomId;
3578
+ private readonly sleep;
3579
+ private readonly isLockOwnerAlive?;
3580
+ private readonly onStaleLock?;
3581
+ constructor(options: AtomicJsonWriterOptions);
3582
+ write(target: string, value: unknown, preparedTemp?: string): AtomicWriteResult;
3583
+ private pause;
3584
+ private reclaimStaleLock;
3585
+ }
3586
+
3587
+ interface AgentStoreCleanup {
3588
+ agents: AgentRecord[];
3589
+ snapshots: AgentSnapshot[];
3590
+ }
3591
+ interface AgentStorePersistenceEvent {
3592
+ type: 'agent_persistence';
3593
+ state: 'degraded' | 'recovered';
3594
+ reason: 'busy' | 'unavailable';
3595
+ errorCode?: string;
3596
+ message: string;
3597
+ agentId?: string;
3598
+ retrying: boolean;
3599
+ timestamp: number;
3600
+ }
3601
+ interface AgentStoreOptions {
3602
+ instanceId?: string;
3603
+ pid?: number;
3604
+ hostname?: string;
3605
+ now?: () => number;
3606
+ processStartedAt?: number;
3607
+ heartbeatMs?: number;
3608
+ leaseFreshMs?: number;
3609
+ setTimeout?: typeof setTimeout;
3610
+ clearTimeout?: typeof clearTimeout;
3611
+ processAlive?: (pid: number) => boolean;
3612
+ eventSink?: (event: AgentStorePersistenceEvent) => void;
3613
+ writer?: AtomicJsonWriter;
3614
+ writerOptions?: Omit<AtomicJsonWriterOptions, 'instanceId' | 'hostname'>;
3615
+ }
3616
+ declare class AgentStore {
3617
+ readonly directory: string;
3618
+ readonly instanceId: string;
3619
+ private readonly statePath;
3620
+ private readonly agentsDirectory;
3621
+ private readonly agentSummariesDirectory;
3622
+ private readonly plansDirectory;
3623
+ private readonly evidenceDirectory;
3624
+ private readonly snapshotsDirectory;
3625
+ private readonly instancesDirectory;
3626
+ private readonly leasePath;
3627
+ private readonly pid;
3628
+ private readonly hostname;
3629
+ private readonly processStartedAt;
3630
+ private readonly heartbeatMs;
3631
+ private readonly leaseFreshMs;
3632
+ private readonly now;
3633
+ private readonly schedule;
3634
+ private readonly cancel;
3635
+ private readonly processAlive;
3636
+ private readonly eventSink?;
3637
+ private readonly writer;
3638
+ private enabled;
3639
+ private disposed;
3640
+ private heartbeatTimer?;
3641
+ private persistenceState;
3642
+ private readonly agents;
3643
+ private readonly agentOwners;
3644
+ private readonly summaryAgentIds;
3645
+ private readonly plans;
3646
+ private readonly evidence;
3647
+ private readonly snapshots;
3648
+ private readonly pendingWrites;
3649
+ constructor(repoHash: string, root?: string, enabled?: boolean, options?: AgentStoreOptions);
3650
+ private ensureDirectories;
3651
+ private safeLog;
3652
+ private quarantine;
3653
+ private readJson;
3654
+ private targetMatchesId;
3655
+ private loadDirectory;
3656
+ private load;
3657
+ private migrateAgent;
3658
+ private agentPath;
3659
+ private agentSummaryPath;
3660
+ private agentSummary;
3661
+ private planPath;
3662
+ private evidencePath;
3663
+ private snapshotPath;
3664
+ private ownerMetadata;
3665
+ private persistedAgent;
3666
+ private discardFailedRequiredWrite;
3667
+ private cancelPendingWrite;
3668
+ private queueWrite;
3669
+ private flushPending;
3670
+ private schedulePending;
3671
+ private enterDegraded;
3672
+ private maybeRecover;
3673
+ private flushAll;
3674
+ listAgents(): AgentRecord[];
3675
+ loadAgent(id: string): AgentRecord | undefined;
3676
+ listPlans(): AgentPlanRecord[];
3677
+ listEvidence(): EvidenceItem[];
3678
+ listSnapshots(): AgentSnapshot[];
3679
+ saveAgent(agent: AgentRecord, options?: {
3680
+ defer?: boolean;
3681
+ required?: boolean;
3682
+ }): AtomicWriteResult;
3683
+ removeAgent(agentId: string): void;
3684
+ removePlan(planId: string): void;
3685
+ removeEvidence(evidenceId: string): void;
3686
+ removeSnapshot(snapshotId: string): void;
3687
+ savePlan(plan: AgentPlanRecord): AtomicWriteResult;
3688
+ saveEvidence(evidence: EvidenceItem, options?: {
3689
+ required?: boolean;
3690
+ }): AtomicWriteResult;
3691
+ saveSnapshot(snapshot: AgentSnapshot): AtomicWriteResult;
3692
+ recoverAbandonedAgents(): AgentRecord[];
3693
+ markActiveInterrupted(): AgentRecord[];
3694
+ isOwnedByCurrent(agentId: string): boolean;
3695
+ isOwnedByLiveForeign(agentId: string): boolean;
3696
+ hasPendingAgent(agentId: string): boolean;
3697
+ getPersistenceState(): 'healthy' | 'degraded_busy' | 'degraded_unavailable';
3698
+ cleanupDetailed(retentionDays: number): AgentStoreCleanup;
3699
+ cleanup(retentionDays: number): number;
3700
+ appendTelemetry(event: Record<string, unknown>): void;
3701
+ dispose(): void;
3702
+ private refreshForeignSummaries;
3703
+ private refreshLease;
3704
+ private scheduleHeartbeat;
3705
+ private defaultProcessAlive;
3706
+ private isInstanceAlive;
3707
+ private isLockOwnerAlive;
3708
+ private parseTemp;
3709
+ private recoverTemps;
3710
+ }
3711
+
3712
+ interface ManagerOptions {
3713
+ storeRoot?: string;
3714
+ worktreeRoot?: string;
3715
+ eventSink?: (event: AgentRuntimeEvent) => void;
3716
+ hookEventSink?: (event: string, payload: Record<string, unknown>) => void;
3717
+ runLoop?: typeof runAgentLoop;
3718
+ compactRunner?: typeof runCompact;
3719
+ findGitRoot?: typeof findGitRoot;
3720
+ createSnapshot?: typeof createSyntheticSnapshot;
3721
+ createWorktree?: typeof createAgentWorktree;
3722
+ checkoutWorktree?: typeof checkoutAgentCommit;
3723
+ commitWork?: typeof commitAgentWork;
3724
+ applyCandidate?: typeof applyVerifiedCandidate;
3725
+ removeWorktree?: (record: AgentRecord, repoRoot?: string) => Promise<void>;
3726
+ removeSnapshot?: typeof removeSnapshotRef;
3727
+ runtime?: SessionRuntime;
3728
+ permissionMode?: string;
3729
+ persistPermissionRule?: (rule: string) => void;
3730
+ createStore?: (repoHash: string, root: string | undefined, enabled: boolean) => AgentStore;
3731
+ }
3732
+ interface SubscribeOptions {
3733
+ snapshot?: boolean;
3734
+ }
3735
+ interface PublishEvidenceInput {
3736
+ kind: EvidenceKind;
3737
+ summary: string;
3738
+ confidence?: number;
3739
+ references?: EvidenceReference[];
3740
+ }
3741
+ declare class AgentManager {
3742
+ private config;
3743
+ private readonly parentDefinitions;
3744
+ private readonly options;
3745
+ private store?;
3746
+ private repoRoot?;
3747
+ private initialized?;
3748
+ private readonly agents;
3749
+ private readonly hydratedAgentIds;
3750
+ private readonly plans;
3751
+ private readonly evidence;
3752
+ private readonly snapshots;
3753
+ private readonly planSnapshots;
3754
+ private readonly snapshotPromises;
3755
+ private readonly queue;
3756
+ private readonly controllers;
3757
+ private readonly questionResolvers;
3758
+ private readonly permissionResolvers;
3759
+ private readonly permissionRules;
3760
+ private readonly activities;
3761
+ private readonly waiters;
3762
+ private readonly idleWaiters;
3763
+ private active;
3764
+ private interactivePermissions;
3765
+ private permissionMode;
3766
+ private permissionModeOverride?;
3767
+ private readonly subscribers;
3768
+ private legacyEventSink?;
3769
+ private readonly textBuffers;
3770
+ private readonly textTimers;
3771
+ private hookEventSink?;
3772
+ private exitHandler?;
3773
+ private disposed;
3774
+ private persistenceState;
3775
+ constructor(config: AgentConfig, parentDefinitions: ToolDefinition[], options?: ManagerOptions);
3776
+ setEventSink(sink?: (event: AgentRuntimeEvent) => void, hookSink?: (event: string, payload: Record<string, unknown>) => void): void;
3777
+ updateConfig(config: AgentConfig): void;
3778
+ hasActiveProfile(profile: string): boolean;
3779
+ setInteractivePermissions(enabled: boolean): void;
3780
+ setPermissionMode(mode: string | undefined): void;
3781
+ resolvePermission(agentId: string, requestId: string, response: 'allow' | 'deny' | 'always'): Promise<AgentRecord>;
3782
+ resolveQuestion(agentId: string, response: UserQuestionResponse): Promise<AgentRecord>;
3783
+ recordRoutingTelemetry(event: string, data?: Record<string, unknown>): void;
3784
+ subscribe(listener: (event: AgentRuntimeEvent) => void, options?: SubscribeOptions): () => void;
3785
+ listPendingCompletions(): Promise<AgentCompletionNotification[]>;
3786
+ acknowledgeCompletion(deliveryId: string): Promise<void>;
3787
+ waitForIdle(): Promise<void>;
3788
+ private ensureInitialized;
3789
+ private hydrateAgent;
3790
+ private agentListRecord;
3791
+ dispose(): void;
3792
+ private notifyIdle;
3793
+ private emit;
3794
+ private recordForReturn;
3795
+ private assertMutable;
3796
+ getPersistenceState(): 'healthy' | 'degraded_busy' | 'degraded_unavailable';
3797
+ private handlePersistenceEvent;
3798
+ private requirePersisted;
3799
+ private queueTextDelta;
3800
+ private flushTextDelta;
3801
+ private persist;
3802
+ private completionNotification;
3803
+ private notify;
3804
+ private definitions;
3805
+ createPlan(input: {
3806
+ taskShape: string;
3807
+ issueQuality: IssueQuality;
3808
+ topology: AgentTopology;
3809
+ rationale: string;
3810
+ agentBudget: number;
3811
+ parentSessionId?: string;
3812
+ rootRunId?: string;
3813
+ parentRunId?: string;
3814
+ }): Promise<AgentPlanRecord>;
3815
+ listPlans(): Promise<AgentPlanRecord[]>;
3816
+ private snapshotForPlan;
3817
+ spawn(request: AgentSpawnRequest): Promise<AgentRecord>;
3818
+ list(): Promise<AgentRecord[]>;
3819
+ listProfiles(): Promise<Array<{
3820
+ name: string;
3821
+ description: string;
3822
+ role: AgentRecord['role'];
3823
+ isolation: NonNullable<AgentRecord['isolation']>;
3824
+ resolvedModel: string;
3825
+ configuredModel?: string;
3826
+ color?: string;
3827
+ }>>;
3828
+ get(agentId: string): Promise<AgentRecord | undefined>;
3829
+ dismiss(agentId: string): Promise<void>;
3830
+ send(agentId: string, message: string, evidenceIds?: string[], runContext?: Pick<AgentRunContext, 'rootRunId' | 'runId'>): Promise<AgentRecord>;
3831
+ wait(agentId: string, timeoutMs?: number): Promise<AgentRecord>;
3832
+ stop(agentId: string, reason?: string): Promise<AgentRecord>;
3833
+ publishEvidence(agentId: string, input: PublishEvidenceInput): Promise<EvidenceItem>;
3834
+ listEvidence(options?: {
3835
+ requesterAgentId?: string;
3836
+ includeUnverified?: boolean;
3837
+ ids?: string[];
3838
+ }): Promise<EvidenceItem[]>;
3839
+ reviewEvidence(reviewerAgentId: string, evidenceId: string, verdict: 'pass' | 'fail' | 'inconclusive', notes?: string): Promise<EvidenceItem>;
3840
+ apply(agentId: string, evidenceId?: string): Promise<AgentApplyResult>;
3841
+ private pump;
3842
+ private evidenceContext;
3843
+ private systemPrompt;
3844
+ private run;
3845
+ private telemetry;
3846
+ }
3847
+
3848
+ type PermissionResult = 'allow' | 'deny' | 'always';
3849
+ /**
3850
+ * An `always` answer that names the rule to persist.
3851
+ *
3852
+ * The rule was previously derived from the call alone, which meant a shell
3853
+ * approval could only ever be written as the exact command string. An approver
3854
+ * that lets the user widen the scope has to be able to say which rule they
3855
+ * picked; approvers that do not simply keep returning the bare result.
3856
+ */
3857
+ interface PermissionDecision {
3858
+ result: PermissionResult;
3859
+ /** Overrides the derived rule when the result is `always`. */
3860
+ rule?: string;
3861
+ }
3862
+ /** Why a submitted plan was never applied when the host could not approve it. */
3863
+ type PlanNotAppliedReason = 'approval_unavailable' | 'approval_declined' | 'approval_cancelled' | 'invalid_approval_response';
3864
+ /**
3865
+ * `stop` is the non-interactive terminal decision: no approver exists (or the
3866
+ * approver refused to decide), so the run ends with the plan as its deliverable
3867
+ * instead of asking the model to revise and resubmit forever.
3868
+ */
3869
+ type PlanApprovalResult = 'approve' | 'approve-fresh' | 'reject' | {
3870
+ decision: 'revise';
3871
+ feedback: string;
3872
+ } | {
3873
+ decision: 'stop';
3874
+ reason: PlanNotAppliedReason;
3875
+ message: string;
3876
+ };
3877
+ interface UserQuestionOption {
3878
+ label: string;
3879
+ description: string;
3880
+ }
3881
+ interface UserQuestion {
3882
+ question: string;
3883
+ header: string;
3884
+ options: UserQuestionOption[];
3885
+ multiSelect: boolean;
3886
+ }
3887
+ type UserQuestionSource = {
3888
+ kind: 'root';
3889
+ traceId?: string;
3890
+ } | {
3891
+ kind: 'subagent';
3892
+ agentPath: string[];
3893
+ traceId?: string;
3894
+ };
3895
+ interface UserQuestionRequest {
3896
+ id: string;
3897
+ questions: UserQuestion[];
3898
+ source: UserQuestionSource;
3899
+ }
3900
+ type UserQuestionResponse = {
3901
+ action: 'answer';
3902
+ answers: Record<string, string | string[]>;
3903
+ } | {
3904
+ action: 'decline';
3905
+ message?: string;
3906
+ } | {
3907
+ action: 'cancel';
3908
+ message?: string;
3909
+ };
3910
+ type UserQuestionHandler = (request: UserQuestionRequest, context: {
3911
+ signal?: AbortSignal;
3912
+ }) => Promise<UserQuestionResponse>;
3913
+ /**
3914
+ * One field of an MCP `elicitation/create` form, flattened from the protocol's
3915
+ * restricted JSON Schema subset (top-level primitives only). Enum options come
3916
+ * from either `enum`/`enumNames` or the `oneOf: [{const, title}]` spelling.
3917
+ */
3918
+ type ElicitationField = {
3919
+ name: string;
3920
+ title: string;
3921
+ description?: string;
3922
+ required: boolean;
3923
+ } & ({
3924
+ kind: 'string';
3925
+ format?: string;
3926
+ minLength?: number;
3927
+ maxLength?: number;
3928
+ default?: string;
3929
+ } | {
3930
+ kind: 'number';
3931
+ integer: boolean;
3932
+ minimum?: number;
3933
+ maximum?: number;
3934
+ default?: number;
3935
+ } | {
3936
+ kind: 'boolean';
3937
+ default?: boolean;
3938
+ } | {
3939
+ kind: 'enum';
3940
+ options: Array<{
3941
+ value: string;
3942
+ label: string;
3943
+ }>;
3944
+ default?: string;
3945
+ });
3946
+ interface ElicitationRequest {
3947
+ id: string;
3948
+ /** Declared name of the MCP server asking, so the user can see who is asking. */
3949
+ server: string;
3950
+ message: string;
3951
+ fields: ElicitationField[];
3952
+ }
3953
+ type ElicitationValue = string | number | boolean;
3954
+ type ElicitationResponse = {
3955
+ action: 'accept';
3956
+ content: Record<string, ElicitationValue>;
3957
+ } | {
3958
+ action: 'decline';
3959
+ } | {
3960
+ action: 'cancel';
3961
+ };
3962
+ type ElicitationHandler = (request: ElicitationRequest, context: {
3963
+ signal?: AbortSignal;
3964
+ }) => Promise<ElicitationResponse>;
3965
+ interface ToolCall {
3966
+ id: string;
3967
+ name: string;
3968
+ arguments: Record<string, unknown>;
3969
+ }
3970
+ /** Display-only trace for a tool invoked inside a Task subagent. */
3971
+ interface NestedToolInvocation {
3972
+ /** Globally unique UI identity. Raw provider tool-call ids may repeat across subagents. */
3973
+ traceId: string;
3974
+ /** Trace id of the Task invocation that directly launched this tool. */
3975
+ parentTraceId: string;
3976
+ call: ToolCall;
3977
+ result?: ToolResult;
3978
+ }
3979
+ /** Optional observer used by hosts that want live visibility into subagent tools. */
3980
+ interface NestedToolObserver {
3981
+ onToolCall: (invocation: NestedToolInvocation) => void;
3982
+ onToolResult: (traceId: string, result: ToolResult) => void;
3983
+ }
3984
+ interface FileMutationSummary {
3985
+ kind: 'create' | 'update' | 'delete';
3986
+ filePath: string;
3987
+ addedLines: number;
3988
+ removedLines: number;
3989
+ }
3990
+ type ToolResultStatus = 'success' | 'error' | 'blocked' | 'cancelled' | 'timed_out';
3991
+ interface ToolResultError {
3992
+ code: string;
3993
+ message: string;
3994
+ retryable: boolean;
3995
+ remediation?: string;
3996
+ details?: Record<string, unknown>;
3997
+ }
3998
+ interface ToolResultPresentation {
3999
+ kind: 'text' | 'markdown' | 'diff' | 'file' | 'command' | 'search' | 'task' | 'agent';
4000
+ summary: string;
4001
+ details?: string;
4002
+ metadata?: string[];
4003
+ target?: string;
4004
+ }
4005
+ interface ToolResultArtifacts {
4006
+ fileMutation?: FileMutationSummary;
4007
+ /** All mutations produced by a transactional multi-file tool call. */
4008
+ fileMutations?: FileMutationSummary[];
4009
+ fileObservations?: FileObservation[];
4010
+ eventRef?: string;
4011
+ /** User-local file containing the complete output when model-facing content is clipped. */
4012
+ outputPath?: string;
4013
+ }
4014
+ /** V2 result contract shared by runtime, persistence, SDK, and TUI. */
4015
+ interface ToolResult<TData = unknown> {
4016
+ version: 2;
4017
+ toolCallId: string;
4018
+ status: ToolResultStatus;
4019
+ /** Concise provider-facing content. */
4020
+ content: string;
4021
+ /** Machine-readable payload for consumers that should not parse content. */
4022
+ data?: TData;
4023
+ structuredError?: ToolResultError;
4024
+ presentation?: ToolResultPresentation;
4025
+ metrics?: {
4026
+ durationMs?: number;
4027
+ retryAttempt?: number;
4028
+ };
4029
+ artifacts?: ToolResultArtifacts;
4030
+ pagination?: {
4031
+ cursor?: string;
4032
+ nextCursor?: string;
4033
+ truncated?: boolean;
4034
+ omittedItems?: number;
4035
+ omittedBytes?: number;
4036
+ };
4037
+ }
4038
+ /** JSON-schema subset accepted by provider tool definitions. */
4039
+ interface JsonSchemaObject extends Record<string, unknown> {
4040
+ type?: 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null';
4041
+ title?: string;
4042
+ description?: string;
4043
+ properties?: Record<string, JsonSchemaObject>;
4044
+ required?: string[];
4045
+ additionalProperties?: boolean | JsonSchemaObject;
4046
+ items?: JsonSchemaObject;
4047
+ enum?: Array<string | number | boolean | null>;
4048
+ const?: string | number | boolean | null;
4049
+ minimum?: number;
4050
+ maximum?: number;
4051
+ minLength?: number;
4052
+ maxLength?: number;
4053
+ minItems?: number;
4054
+ maxItems?: number;
4055
+ oneOf?: JsonSchemaObject[];
4056
+ anyOf?: JsonSchemaObject[];
4057
+ }
4058
+ type ToolCategory = 'filesystem' | 'shell' | 'git' | 'web' | 'planning' | 'tasks' | 'skills' | 'agents' | 'evidence' | 'session' | 'notebook' | 'mcp' | 'other';
4059
+ type ToolEffect = 'read' | 'write' | 'execute' | 'network' | 'delegate' | 'interactive';
4060
+ interface ToolCatalogMetadata {
4061
+ /** Search terms in addition to the canonical tool name. */
4062
+ aliases?: string[];
4063
+ keywords?: string[];
4064
+ category?: ToolCategory;
4065
+ namespace?: string;
4066
+ /** Tool is always available in the practical core, normally deferred, or runtime-gated. */
4067
+ exposure?: 'core' | 'deferred' | 'runtime';
4068
+ /** Agent roles allowed to discover and invoke this definition. */
4069
+ roles?: Array<'root' | 'child'>;
4070
+ effects?: ToolEffect[];
4071
+ /** Optional runtime predicate for stateful tools such as background shells. */
4072
+ available?: (context: ToolContext) => boolean;
4073
+ /** Short catalog summary; descriptions remain the full provider-facing guidance. */
4074
+ summary?: string;
4075
+ }
4076
+ interface ToolPolicy {
4077
+ idempotent?: boolean;
4078
+ concurrency?: 'parallel' | 'serial';
4079
+ requiresPermission?: boolean;
4080
+ }
4081
+ type FileObservationOperation = 'read' | 'mention' | 'edit' | 'write' | 'create' | 'notebook-read';
4082
+ interface FileObservation {
4083
+ path: string;
4084
+ workspaceId: string;
4085
+ sha256: string;
4086
+ byteSize: number;
4087
+ lineStart?: number;
4088
+ lineEnd?: number;
4089
+ operation: FileObservationOperation;
4090
+ sourceRef: string;
4091
+ timestamp: number;
4092
+ }
4093
+ interface ToolDefinition {
4094
+ name: string;
4095
+ description: string;
4096
+ parameters: Record<string, unknown>;
4097
+ /** Typed provider schema. `parameters` remains accepted while tools migrate. */
4098
+ inputSchema?: JsonSchemaObject;
4099
+ /** Cross-harness argument-name aliases (alias → canonical), applied before validation. */
4100
+ argumentAliases?: Record<string, string>;
4101
+ /** Per-item argument aliases for array arguments (argument name → alias → canonical). */
4102
+ arrayItemArgumentAliases?: Record<string, Record<string, string>>;
4103
+ catalog?: ToolCatalogMetadata;
4104
+ policy?: ToolPolicy;
4105
+ /** When true, the tool is safe to retry once on transient failure (Read, Grep, WebFetch, etc.). */
4106
+ idempotent?: boolean;
4107
+ /**
4108
+ * Default deadline in milliseconds, and a declaration that the tool enforces
4109
+ * its own. The registry gives such a tool a grace margin on top so the tool's
4110
+ * report — which carries whatever output it captured — is the one that wins.
4111
+ * A function form lets a tool whose deadline comes from settings (Check) keep
4112
+ * both sides reading the same number.
4113
+ */
4114
+ timeoutMs?: number | ((context: ToolContext) => number | undefined);
4115
+ execute: (args: Record<string, unknown>, context: ToolContext) => Promise<ToolResult>;
4116
+ }
4117
+ interface ToolContext {
4118
+ workspaceRoot: string;
4119
+ env: Record<string, string>;
4120
+ /** Explicit environment overrides safe to persist for opt-in persistent jobs. */
4121
+ envOverrides?: Record<string, string>;
4122
+ /** Glob patterns to ignore during file discovery (e.g. from .gitignore). */
4123
+ gitignorePatterns?: string[];
4124
+ /** Resolved sandbox settings for the Bash tool. */
4125
+ sandbox?: ResolvedSettings['sandbox'];
4126
+ /** The shell the Bash tool spawns for unsandboxed commands; resolved on demand when absent. */
4127
+ shell?: ResolvedShell;
4128
+ /** The active AgentConfig, set by the agent loop before tool execution. */
4129
+ agentConfig?: AgentConfig;
4130
+ /** Abort signal shared with nested Task subagents. */
4131
+ signal?: AbortSignal;
4132
+ /** Stable trace identity of the tool currently executing. */
4133
+ currentToolTraceId?: string;
4134
+ /** Current model-loop turn, used for bounded skill activation frames. */
4135
+ currentTurn?: number;
4136
+ /** Observer for display-only tools invoked inside Task subagents. */
4137
+ nestedToolObserver?: NestedToolObserver;
4138
+ /**
4139
+ * Agent todo list — written by TodoWrite, read by the loop for context injection.
4140
+ * Bound to the array `SessionRuntime` owns; TodoWrite mutates it in place rather
4141
+ * than reassigning, so the runtime, the session-state render, and the plan
4142
+ * persistence writer all observe the same list.
4143
+ */
4144
+ todos?: Array<{
4145
+ content: string;
4146
+ status: string;
4147
+ activeForm?: string;
4148
+ }>;
4149
+ /** Agent task list — written by TaskCreate/TaskUpdate and shared across tool calls. */
4150
+ tasks?: AgentTask[];
4151
+ /** Background shells started by Bash(run_in_background), shared across tool calls. */
4152
+ backgroundShells?: BackgroundShellStore;
4153
+ shellManager?: ShellJobManager;
4154
+ /** Runtime-only newest file observation per workspace/path. */
4155
+ fileObservationLedger?: Map<string, FileObservation>;
4156
+ /** Live permission mode for the active agent loop; tools may update this. */
4157
+ currentMode?: PermissionMode;
4158
+ /** Mode to restore after a tool-initiated plan-mode session exits. */
4159
+ previousMode?: PermissionMode;
4160
+ /** Plan text submitted by ExitPlanMode and awaiting host approval. */
4161
+ pendingPlanApproval?: {
4162
+ plan: string;
4163
+ };
4164
+ /** Structured questions submitted by AskUserQuestion and awaiting the host. */
4165
+ pendingUserQuestion?: {
4166
+ questions: UserQuestion[];
4167
+ };
4168
+ /** Host interaction capability propagated into Task subagents. */
4169
+ userQuestionHandler?: UserQuestionHandler;
4170
+ /** Nested agent names from the root agent to this loop. */
4171
+ agentPath?: string[];
4172
+ /** Active definitions used to derive a child's capability intersection. */
4173
+ availableTools?: ToolDefinition[];
4174
+ /** Managed-agent runtime shared by the parent session. */
4175
+ agentManager?: AgentManager;
4176
+ /** Identity set only inside a managed child. */
4177
+ agentId?: string;
4178
+ agentRole?: AgentRole;
4179
+ /** Parent session attribution for managed agents and hooks. */
4180
+ parentSessionId?: string;
4181
+ /** Root/parent execution attribution for managed agents and evidence. */
4182
+ runContext?: AgentRunContext;
4183
+ /** Host sink for managed-agent lifecycle and evidence events. */
4184
+ onAgentEvent?: (event: AgentRuntimeEvent) => void;
4185
+ /** Host sink used by lifecycle hooks started from managed-agent tools. */
4186
+ onHookEvent?: (event: string, payload: Record<string, unknown>) => void;
4187
+ /** Per-session capability/discovery controller installed by the agent loop. */
4188
+ toolDiscovery?: ToolDiscoveryContext;
4189
+ /** Mutable resources owned by the current session, separate from configuration. */
4190
+ runtime?: SessionRuntime;
4191
+ }
4192
+ interface ToolSearchMatch {
4193
+ name: string;
4194
+ description: string;
4195
+ summary: string;
4196
+ category: ToolCategory;
4197
+ namespace?: string;
4198
+ loaded: boolean;
4199
+ }
4200
+ interface ToolDiscoveryContext {
4201
+ /** Return authorized catalog matches without exposing their full schemas. */
4202
+ search(query: string, category?: ToolCategory, namespace?: string, limit?: number): ToolSearchMatch[];
4203
+ /** Activate selected definitions for the next provider request. */
4204
+ activate(names: string[]): string[];
4205
+ /** Intersect the current surface with an additional command/skill capability policy. */
4206
+ restrict(rules: string[]): void;
4207
+ /** Add a scoped capability intersection and return a disposer that restores the parent surface. */
4208
+ pushRestriction(rules: string[]): () => void;
4209
+ /** Preview the authorized definitions after an additional scoped intersection. */
4210
+ previewRestriction(rules: string[]): ToolDefinition[];
4211
+ /** Whether a tool is currently visible and executable for this turn. */
4212
+ canExecute(call: ToolCall): boolean;
4213
+ /** Definitions to send to the provider for the current request. */
4214
+ activeDefinitions(): ToolDefinition[];
4215
+ /** Compact catalog text used by the system prompt. */
4216
+ catalogSummary(): string;
4217
+ }
4218
+ interface ToolDiscoveryState {
4219
+ /** Monotonic access counter used for deterministic LRU eviction. */
4220
+ clock: number;
4221
+ loaded: Map<string, number>;
4222
+ }
4223
+
4224
+ /**
4225
+ * Resolve settings from all scopes: user → project → local.
4226
+ * Priority: Local > Project > User.
4227
+ *
4228
+ * @param workspace - Project root directory
4229
+ * @param overridePath - Optional path to an ad-hoc settings file (--settings flag)
4230
+ * @returns Fully resolved settings with all defaults filled
4231
+ */
4232
+ interface SettingsResolutionPaths {
4233
+ home?: string;
4234
+ userSettingsPath?: string;
4235
+ projectSettingsPath?: string;
4236
+ localSettingsPath?: string;
4237
+ trustStorePath?: string;
4238
+ }
4239
+
4240
+ interface LoadConfigOptions {
4241
+ /** Path to an ad-hoc settings file (--settings flag); takes highest priority. */
4242
+ settingsOverridePath?: string;
4243
+ /** If true, skip all settings.json layers entirely (use defaults + legacy .bookrc.json). */
4244
+ noSettings?: boolean;
4245
+ /** Run storage migrations after effective settings are validated. */
4246
+ runMigrations?: boolean;
4247
+ /** CLI -m/--model override, applied before provider registry resolution. */
4248
+ modelOverride?: string;
4249
+ /**
4250
+ * CLI --effort override. Passed in rather than assigned after the fact so it
4251
+ * counts as an explicit choice: model metadata must not override a level the
4252
+ * user typed, and providers that gate on `effortExplicit` must see it.
4253
+ */
4254
+ effortOverride?: AgentConfig['effort'];
4255
+ /**
4256
+ * Redirect individual settings layers. Pointing one at a path that does not
4257
+ * exist drops it from the merge, which is how `book doctor` finds the layer a
4258
+ * failure first appears in.
4259
+ */
4260
+ settingsPaths?: SettingsResolutionPaths;
4261
+ /** Let the interactive TUI start before a BYOK credential has been added. */
4262
+ allowMissingApiKey?: boolean;
4263
+ /** Optional store for learned context window ceilings. */
4264
+ modelWindowStore?: ModelWindowStore;
4265
+ }
4266
+ declare function loadConfig(workspace?: string, options?: LoadConfigOptions): AgentConfig;
4267
+
4268
+ type AgentEvent = {
4269
+ type: 'run_started';
4270
+ context: AgentRunContext;
4271
+ ambient: AgentRunAmbientSnapshot;
4272
+ } | {
4273
+ type: 'system';
4274
+ model: string;
4275
+ cwd: string;
4276
+ } | {
4277
+ type: 'session';
4278
+ sessionId: string;
4279
+ } | {
4280
+ type: 'text';
4281
+ content: string;
4282
+ } | {
4283
+ type: 'reasoning';
4284
+ content: string;
4285
+ } | {
4286
+ type: 'tool_use';
4287
+ toolCall: ToolCall;
4288
+ } | {
4289
+ type: 'tool_result';
4290
+ toolResult: ToolResult;
4291
+ } | {
4292
+ type: 'user_question';
4293
+ request: UserQuestionRequest;
4294
+ status: 'pending' | 'unavailable';
4295
+ } | {
4296
+ type: 'user_question_result';
4297
+ requestId: string;
4298
+ response: UserQuestionResponse;
4299
+ } | ShellJobEvent | {
4300
+ type: 'agent_start';
4301
+ agent: AgentRecord;
4302
+ } | {
4303
+ type: 'agent_update';
4304
+ agent: AgentRecord;
4305
+ } | {
4306
+ type: 'agent_result';
4307
+ agent: AgentRecord;
4308
+ } | Extract<AgentRuntimeEvent, {
4309
+ type: 'agent_question' | 'agent_apply' | 'agent_status' | 'agent_activity' | 'agent_text_delta' | 'agent_message' | 'agent_completion' | 'agent_permission' | 'agent_persistence' | 'skill_lifecycle';
4310
+ }> | {
4311
+ type: 'evidence_update';
4312
+ evidence: EvidenceItem;
4313
+ } | {
4314
+ type: 'attempt_discarded';
4315
+ reason: 'empty_response';
4316
+ } | {
4317
+ type: 'error';
4318
+ error: string;
4319
+ } | {
4320
+ type: 'result';
4321
+ messages: Message[];
4322
+ usage: Usage | null;
4323
+ sessionId: string;
4324
+ outcome?: AgentTerminalOutcome;
4325
+ runContext?: AgentRunContext;
4326
+ runs?: AgentRunResult[];
4327
+ /**
4328
+ * Slash commands the host performed itself rather than sending to the
4329
+ * model. Present for SDK callers, whose stdout is a discard sink and who
4330
+ * would otherwise have no way to read a `/review` they paid for.
4331
+ */
4332
+ commandResults?: HostCommandResult[];
4333
+ } | {
4334
+ type: 'terminal';
4335
+ outcome: AgentTerminalOutcome;
4336
+ runContext?: AgentRunContext;
4337
+ } | {
4338
+ type: 'done';
4339
+ };
4340
+ type AgentSessionStatus = 'idle' | 'running' | 'waiting_for_user' | 'completed' | 'failed' | 'cancelled' | 'timed_out' | 'interrupted';
4341
+ interface AgentSessionSnapshot {
4342
+ status: AgentSessionStatus;
4343
+ sessionId?: string;
4344
+ model?: string;
4345
+ cwd?: string;
4346
+ assistantText: string;
4347
+ toolCalls: ToolCall[];
4348
+ toolResults: ToolResult[];
4349
+ pendingUserQuestion?: UserQuestionRequest;
4350
+ agents: AgentRecord[];
4351
+ evidence: EvidenceItem[];
4352
+ messages: Message[];
4353
+ usage: Usage | null;
4354
+ terminal?: AgentTerminalOutcome;
4355
+ runContext?: AgentRunContext;
4356
+ ambient?: AgentRunAmbientSnapshot;
4357
+ error?: string;
4358
+ }
4359
+ declare function createAgentSessionSnapshot(): AgentSessionSnapshot;
4360
+ declare function reduceAgentSessionSnapshot(snapshot: AgentSessionSnapshot, event: AgentEvent): AgentSessionSnapshot;
4361
+
4362
+ interface SurfaceOptions {
4363
+ config: AgentConfig;
4364
+ context: ToolContext;
4365
+ definitions: ToolDefinition[];
4366
+ capabilityRules?: string[];
4367
+ isSubagent?: boolean;
4368
+ }
4369
+ declare function createToolSurface(options: SurfaceOptions): ToolDiscoveryContext;
4370
+
4371
+ interface ToolResultOptions<TData> {
4372
+ toolCallId?: string;
4373
+ data?: TData;
4374
+ presentation?: Partial<ToolResultPresentation>;
4375
+ artifacts?: ToolResultArtifacts;
4376
+ pagination?: ToolResult['pagination'];
4377
+ }
4378
+ declare function toolSuccess<TData = unknown>(content: string, options?: ToolResultOptions<TData>): ToolResult<TData>;
4379
+ declare function toolFailure(message: string, options?: ToolResultOptions<unknown> & {
4380
+ code?: string;
4381
+ status?: Exclude<ToolResultStatus, 'success'>;
4382
+ retryable?: boolean;
4383
+ remediation?: string;
4384
+ details?: Record<string, unknown>;
4385
+ content?: string;
4386
+ }): ToolResult;
4387
+
4388
+ declare const SKILL_EVALUATION_CATEGORIES: readonly ["direct", "indirect", "negative", "ambiguous", "conflicting", "disabled", "invalid", "missing-body", "missing-resource"];
4389
+ type SkillEvaluationCategory = (typeof SKILL_EVALUATION_CATEGORIES)[number];
4390
+ interface SkillEvaluationFixture {
4391
+ id: string;
4392
+ category: SkillEvaluationCategory;
4393
+ promptHash: string;
4394
+ expectedSkills: string[];
4395
+ expectedBlockedCodes?: string[];
4396
+ }
4397
+ interface SkillEvaluationPromptFixture {
4398
+ id: string;
4399
+ category: SkillEvaluationCategory;
4400
+ prompt: string;
4401
+ expectedSkills: string[];
4402
+ expectedBlockedCodes?: string[];
4403
+ }
4404
+ interface SkillEvaluationOutcome {
4405
+ promptChars?: number;
4406
+ promptTokens?: number;
4407
+ bodyTokens?: number;
4408
+ taskCompleted?: boolean;
4409
+ userCorrections?: number;
4410
+ skillToolFailures?: number;
4411
+ exposure?: 'eager' | 'deferred';
4412
+ activationTurns?: number;
4413
+ }
4414
+ interface SkillEvaluationObservation extends SkillEvaluationFixture {
4415
+ activatedSkills: string[];
4416
+ blockedCodes: string[];
4417
+ promptChars: number;
4418
+ promptTokens: number;
4419
+ bodyBytes: number;
4420
+ bodyTokens: number;
4421
+ falseActivationBytes: number;
4422
+ activationLatencyMs: number[];
4423
+ consentRequests: number;
4424
+ unnecessaryPermissionPrompts: number;
4425
+ taskCompleted?: boolean;
4426
+ userCorrections: number;
4427
+ skillToolFailures: number;
4428
+ blockingMismatch: boolean;
4429
+ exposure?: 'eager' | 'deferred';
4430
+ activationTurns?: number;
4431
+ }
4432
+ interface SkillEvaluationThresholds {
4433
+ minimumPrecision: number;
4434
+ minimumRecall: number;
4435
+ maximumFalseActivations: number;
4436
+ maximumUnnecessaryPermissionPrompts: number;
4437
+ maximumSkillToolFailures: number;
4438
+ }
4439
+ interface SkillEvaluationExecution {
4440
+ evidenceKind: 'offline-observation';
4441
+ providerRunEligibility: 'not-applicable';
4442
+ controls: {
4443
+ evaluationDate: string;
4444
+ randomSeed: string;
4445
+ runtimeRevision: string;
4446
+ fixtureRevision: string;
4447
+ fixtureRevisionStatus: 'captured' | 'incomplete';
4448
+ };
4449
+ }
4450
+ interface SkillEvaluationReport {
4451
+ generatedAt: string;
4452
+ fixtureCount: number;
4453
+ categoryCounts: Record<SkillEvaluationCategory, number>;
4454
+ truePositives: number;
4455
+ falsePositives: number;
4456
+ falseNegatives: number;
4457
+ precision: number;
4458
+ recall: number;
4459
+ falseActivationBytes: number;
4460
+ promptChars: {
4461
+ total: number;
4462
+ median: number;
4463
+ };
4464
+ promptTokens: {
4465
+ total: number;
4466
+ median: number;
4467
+ };
4468
+ bodyBytes: {
4469
+ total: number;
4470
+ median: number;
4471
+ };
4472
+ bodyTokens: {
4473
+ total: number;
4474
+ median: number;
4475
+ };
4476
+ activationLatencyMs: {
4477
+ median: number;
4478
+ p95: number;
4479
+ };
4480
+ consentRequests: number;
4481
+ unnecessaryPermissionPrompts: number;
4482
+ taskCompletionRate?: number;
4483
+ userCorrections: number;
4484
+ skillToolFailures: number;
4485
+ blockingMismatches: number;
4486
+ exposure: Record<'eager' | 'deferred', {
4487
+ samples: number;
4488
+ activationRate: number;
4489
+ medianActivationTurns: number;
4490
+ }>;
4491
+ thresholds: SkillEvaluationThresholds;
4492
+ rolloutReady: boolean;
4493
+ reasons: string[];
4494
+ observations: SkillEvaluationObservation[];
4495
+ evaluation?: SkillEvaluationExecution;
4496
+ }
4497
+ declare const DEFAULT_SKILL_EVALUATION_THRESHOLDS: SkillEvaluationThresholds;
4498
+ declare const DEFAULT_SKILL_EVALUATION_FIXTURES: readonly SkillEvaluationPromptFixture[];
4499
+ /** Convert bounded lifecycle evidence into one prompt-free evaluation observation. */
4500
+ declare function observeSkillEvaluation(fixture: SkillEvaluationFixture, events: readonly SkillLifecycleEvent[], outcome?: SkillEvaluationOutcome): SkillEvaluationObservation;
4501
+ declare function evaluateSkillActivation(observations: readonly SkillEvaluationObservation[], thresholds?: SkillEvaluationThresholds): SkillEvaluationReport;
4502
+ declare function runSkillActivationEvaluation(fixtures: readonly SkillEvaluationPromptFixture[], execute: (fixture: SkillEvaluationPromptFixture) => Promise<{
4503
+ events: readonly SkillLifecycleEvent[];
4504
+ outcome?: SkillEvaluationOutcome;
4505
+ }>, thresholds?: SkillEvaluationThresholds): Promise<SkillEvaluationReport>;
4506
+ declare function renderSkillEvaluationReport(report: SkillEvaluationReport): string;
4507
+ declare function writeSkillEvaluationReport(report: SkillEvaluationReport, jsonPath: string, markdownPath?: string): void;
4508
+
4509
+ /** Render a body-free diagnostic report suitable for commands, logs, and SDK consumers. */
4510
+ declare function buildSkillReport(snapshot: SkillRegistrySnapshot | undefined): string;
4511
+
4512
+ interface EvaluationFixture {
4513
+ id: string;
4514
+ cohort: 'decomposable' | 'ambiguous' | 'sequential';
4515
+ prompt: string;
4516
+ }
4517
+ interface EvaluationRunResult {
4518
+ passed: boolean;
4519
+ validationCorrect?: boolean;
4520
+ wallTimeMs: number;
4521
+ totalTokens?: number;
4522
+ costUsd?: number;
4523
+ spawnCount?: number;
4524
+ route?: AgentTopology;
4525
+ issueQuality?: IssueQuality;
4526
+ conflicts?: number;
4527
+ interruptions?: number;
4528
+ unvalidatedApplications?: number;
4529
+ }
4530
+ interface EvaluationMetric extends Omit<EvaluationRunResult, 'route' | 'issueQuality'> {
4531
+ fixtureId: string;
4532
+ promptHash: string;
4533
+ cohort: EvaluationFixture['cohort'];
4534
+ mode: Extract<AgentMode, 'adaptive' | 'off'>;
4535
+ route?: AgentTopology;
4536
+ issueQuality?: IssueQuality;
4537
+ }
4538
+ /** Run each fixture with the single-agent control and adaptive treatment. */
4539
+ declare function runPairedEvaluation(fixtures: EvaluationFixture[], execute: (fixture: EvaluationFixture, mode: Extract<AgentMode, 'adaptive' | 'off'>) => Promise<EvaluationRunResult>, outputPath: string): Promise<EvaluationMetric[]>;
4540
+ declare function evaluateSuccess(metrics: EvaluationMetric[]): {
4541
+ successful: boolean;
4542
+ reasons: string[];
4543
+ };
4544
+
4545
+ /** Book Agent SDK — programmatic API for embedding Book as a library. */
4546
+
4547
+ type QueryEvent = AgentEvent;
4548
+ interface QueryOptions {
4549
+ workspace?: string;
4550
+ model?: string;
4551
+ permissionMode?: string;
4552
+ maxTurns?: number;
4553
+ settingsPath?: string;
4554
+ noSettings?: boolean;
4555
+ persistSession?: boolean;
4556
+ sessionId?: string;
4557
+ sessionStore?: SessionStoreInterface;
4558
+ signal?: AbortSignal;
4559
+ onUserQuestionRequired?: UserQuestionHandler;
4560
+ /**
4561
+ * Answer MCP form elicitations. Supplying it declares the elicitation
4562
+ * capability to connected servers; without it they fail such requests
4563
+ * themselves rather than blocking on a prompt no one can see.
4564
+ */
4565
+ onElicit?: ElicitationHandler;
4566
+ agents?: 'adaptive' | 'manual' | 'off';
4567
+ /** Forward high-volume managed-agent text deltas. Defaults to false. */
4568
+ forwardSubagentText?: boolean;
4569
+ }
4570
+ declare function query(prompt: string, options?: QueryOptions): AsyncGenerator<QueryEvent, void, undefined>;
4571
+
4572
+ declare function createAgentManager(config: AgentConfig): AgentManager;
4573
+
4574
+ export { type AgentActivity, type AgentApplyResult, type AgentCompletion, type AgentCompletionNotification, type AgentEvent, AgentManager, type AgentModelIdentity, type AgentModelIdentityStatus, type AgentPlanRecord, type AgentProfile, type AgentRecord, type AgentRunAccounting, type AgentRunAmbientSnapshot, type AgentRunContext, type AgentRunMetrics, type AgentRunResult, type AgentRunSource, type AgentRuntimeEvent, type AgentSessionSnapshot, type AgentSessionStatus, type AgentSnapshot, type AgentSpawnRequest, type AgentSummary, type AgentTerminalOutcome, type AgentTerminalReason, type AgentTerminalStatus, DEFAULT_SKILL_EVALUATION_FIXTURES, DEFAULT_SKILL_EVALUATION_THRESHOLDS, type DiscoverSkillsOptions, type ElicitationField, type ElicitationHandler, type ElicitationRequest, type ElicitationResponse, type ElicitationValue, type EvaluationFixture, type EvaluationMetric, type EvaluationRunResult, type EvidenceItem, type JsonSchemaObject, type LoadConfigOptions, type LoadedSkillBody, type QueryEvent, type QueryOptions, SKILL_EVALUATION_CATEGORIES, SessionRuntime, type ShadowedSkill, type Skill, type SkillActivationFrame, type SkillActivationFrameSummary, type SkillActivationReason, type SkillEvaluationCategory, type SkillEvaluationFixture, type SkillEvaluationObservation, type SkillEvaluationOutcome, type SkillEvaluationPromptFixture, type SkillEvaluationReport, type SkillEvaluationThresholds, type SkillIssue, type SkillLifecycleEvent, type SkillLifecycleEventType, type SkillListingResult, SkillRegistry, SkillRegistryError, type SkillRegistrySnapshot, type SkillResource, type SkillRoot, type SkillRootKind, type SkillSource, type ToolCatalogMetadata, type ToolCategory, type ToolDefinition, type ToolDiscoveryContext, type ToolDiscoverySettings, type ToolEffect, type ToolPolicy, type ToolResult, type ToolResultArtifacts, type ToolResultError, type ToolResultPresentation, type ToolResultStatus, type ToolSearchMatch, type UserQuestion, type UserQuestionHandler, type UserQuestionOption, type UserQuestionRequest, type UserQuestionResponse, type UserQuestionSource, applySkillOverrides, buildSkillListing, buildSkillReport, createAgentManager, createAgentSessionSnapshot, createDefaultRegistry, createRegistry, createToolSurface, discoverSkills, evaluateSkillActivation, evaluateSuccess, explicitSkillMentions, generateSkillListing, loadConfig, loadSkillBody, observeSkillEvaluation, query, reduceAgentSessionSnapshot, renderSkillEvaluationReport, runPairedEvaluation, runSkillActivationEvaluation, skillRoots, toolFailure, toolSuccess, writeSkillEvaluationReport };