@letrquan/book 0.2.0 → 0.4.0-beta.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.
- package/CHANGELOG.md +3771 -40
- package/README.md +135 -1311
- package/dist/additional-roots-VAWRQ625.js +32 -0
- package/dist/{app-MOXXWEOY.js → app-J7BVPSUH.js} +9648 -5509
- package/dist/app-J7BVPSUH.js.map +1 -0
- package/dist/chunk-2AAOF5B5.js +28 -0
- package/dist/chunk-2AAOF5B5.js.map +1 -0
- package/dist/chunk-32WBCUAN.js +660 -0
- package/dist/chunk-32WBCUAN.js.map +1 -0
- package/dist/chunk-4DKHLZRI.js +120 -0
- package/dist/chunk-4DKHLZRI.js.map +1 -0
- package/dist/{chunk-CAGOB2N4.js → chunk-62EEAUID.js} +6 -4
- package/dist/chunk-62EEAUID.js.map +1 -0
- package/dist/chunk-AHC7OU75.js +1124 -0
- package/dist/chunk-AHC7OU75.js.map +1 -0
- package/dist/{chunk-E75IPST5.js → chunk-AJZWLKLQ.js} +73 -70
- package/dist/chunk-AJZWLKLQ.js.map +1 -0
- package/dist/chunk-B5FC3MYZ.js +395 -0
- package/dist/chunk-B5FC3MYZ.js.map +1 -0
- package/dist/{chunk-KWHQ2XXL.js → chunk-DNFQTSMJ.js} +55 -13
- package/dist/chunk-DNFQTSMJ.js.map +1 -0
- package/dist/{chunk-J7MIZ5CA.js → chunk-E5WLQSKA.js} +719 -89
- package/dist/chunk-E5WLQSKA.js.map +1 -0
- package/dist/chunk-ECLC4CIJ.js +1181 -0
- package/dist/chunk-ECLC4CIJ.js.map +1 -0
- package/dist/chunk-EEX6QNXV.js +127 -0
- package/dist/chunk-EEX6QNXV.js.map +1 -0
- package/dist/chunk-EFXVSQ5J.js +931 -0
- package/dist/chunk-EFXVSQ5J.js.map +1 -0
- package/dist/{chunk-V4BOYR52.js → chunk-ENLB7NYV.js} +1304 -326
- package/dist/chunk-ENLB7NYV.js.map +1 -0
- package/dist/chunk-FPR7SKIE.js +6926 -0
- package/dist/chunk-FPR7SKIE.js.map +1 -0
- package/dist/{chunk-DGMQEI6Y.js → chunk-HFB74CLM.js} +30 -9
- package/dist/chunk-HFB74CLM.js.map +1 -0
- package/dist/{chunk-5RDRAO4B.js → chunk-HOZXKOE3.js} +37 -1
- package/dist/chunk-HOZXKOE3.js.map +1 -0
- package/dist/chunk-IDYWN37T.js +36 -0
- package/dist/chunk-IDYWN37T.js.map +1 -0
- package/dist/chunk-J4F2QLM2.js +273 -0
- package/dist/chunk-J4F2QLM2.js.map +1 -0
- package/dist/chunk-JGF5HLSI.js +1065 -0
- package/dist/chunk-JGF5HLSI.js.map +1 -0
- package/dist/{chunk-XIFFTATU.js → chunk-KDNWMKXV.js} +7166 -4237
- package/dist/chunk-KDNWMKXV.js.map +1 -0
- package/dist/chunk-KXAYUIKP.js +90 -0
- package/dist/chunk-KXAYUIKP.js.map +1 -0
- package/dist/{chunk-3U5IBM24.js → chunk-MEOB2DZA.js} +12 -2
- package/dist/chunk-MEOB2DZA.js.map +1 -0
- package/dist/{chunk-WKBO5R4O.js → chunk-MV5PQNIA.js} +2 -2
- package/dist/chunk-MV5PQNIA.js.map +1 -0
- package/dist/chunk-N2G4Q7ME.js +2672 -0
- package/dist/chunk-N2G4Q7ME.js.map +1 -0
- package/dist/chunk-OJBMDM5H.js +299 -0
- package/dist/chunk-OJBMDM5H.js.map +1 -0
- package/dist/{chunk-URLQAXTR.js → chunk-PTDC7ZUP.js} +2 -2
- package/dist/chunk-PTDC7ZUP.js.map +1 -0
- package/dist/{chunk-574C6MQR.js → chunk-REASC2QG.js} +173 -64
- package/dist/chunk-REASC2QG.js.map +1 -0
- package/dist/{chunk-7N4J557C.js → chunk-RT5SATF7.js} +1 -1
- package/dist/chunk-RT5SATF7.js.map +1 -0
- package/dist/chunk-SBTU5BEC.js +114 -0
- package/dist/chunk-SBTU5BEC.js.map +1 -0
- package/dist/{chunk-5KLVH3PY.js → chunk-SWPDKLNM.js} +5 -3
- package/dist/chunk-SWPDKLNM.js.map +1 -0
- package/dist/chunk-UCFJ43OS.js +520 -0
- package/dist/chunk-UCFJ43OS.js.map +1 -0
- package/dist/{chunk-VZQXX3WW.js → chunk-W7SLYS3M.js} +80 -16
- package/dist/chunk-W7SLYS3M.js.map +1 -0
- package/dist/{chunk-ANSJPYGI.js → chunk-WV3O5PGX.js} +3 -3
- package/dist/chunk-WV3O5PGX.js.map +1 -0
- package/dist/chunk-XHAO3K7I.js +18 -0
- package/dist/chunk-XHAO3K7I.js.map +1 -0
- package/dist/chunk-XJRJVXJR.js +1050 -0
- package/dist/chunk-XJRJVXJR.js.map +1 -0
- package/dist/chunk-YIL6HTJX.js +663 -0
- package/dist/chunk-YIL6HTJX.js.map +1 -0
- package/dist/{chunk-24AXE6SP.js → chunk-ZPSZZUWF.js} +1 -1
- package/dist/chunk-ZPSZZUWF.js.map +1 -0
- package/dist/{command-approvals-6Y57VCWZ.js → command-approvals-ZRY66LCW.js} +7 -5
- package/dist/context-C3RAP6K5.js +41 -0
- package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-DPQD5H65.js} +6 -5
- package/dist/index.js +947 -203
- package/dist/index.js.map +1 -1
- package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-73AQO5WP.js} +18 -9
- package/dist/interactive-assets-73AQO5WP.js.map +1 -0
- package/dist/job-runner.js +166 -111
- package/dist/job-runner.js.map +1 -1
- package/dist/job-supervisor.d.ts +2 -0
- package/dist/job-supervisor.js +121 -0
- package/dist/job-supervisor.js.map +1 -0
- package/dist/loader-DQNPDKOX.js +27 -0
- package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-27LJYADQ.js} +6 -5
- package/dist/{mcp-config-YIJIES2C.js → mcp-config-GXSODNCM.js} +4 -3
- package/dist/memory-store-I6Z54VUN.js +51 -0
- package/dist/{model-window-store-YFVNXCIP.js → model-window-store-6VKXXTPJ.js} +9 -4
- package/dist/openai-compatible-IPUNBJ53.js +16 -0
- package/dist/permission-approvals-Q5IMFEZI.js +15 -0
- package/dist/permissions-7Y35X6HZ.js +62 -0
- package/dist/provider-approvals-5AGH3RVT.js +76 -0
- package/dist/sandbox-N2RY72GN.js +42 -0
- package/dist/{scrollback-LUKLIBVI.js → scrollback-GD76GNM4.js} +44 -26
- package/dist/scrollback-GD76GNM4.js.map +1 -0
- package/dist/sdk.d.ts +2739 -2069
- package/dist/sdk.js +77 -42
- package/dist/sdk.js.map +1 -1
- package/dist/settings-loader-KDBH45AQ.js +43 -0
- package/dist/settings-loader-KDBH45AQ.js.map +1 -0
- package/dist/{settings-removed-JWWPT4WA.js → settings-removed-3ATWJEF3.js} +2 -2
- package/dist/settings-removed-3ATWJEF3.js.map +1 -0
- package/dist/shell-selection-Z5WORZU4.js +31 -0
- package/dist/shell-selection-Z5WORZU4.js.map +1 -0
- package/package.json +25 -24
- package/dist/app-MOXXWEOY.js.map +0 -1
- package/dist/chunk-24AXE6SP.js.map +0 -1
- package/dist/chunk-3U5IBM24.js.map +0 -1
- package/dist/chunk-574C6MQR.js.map +0 -1
- package/dist/chunk-5GDZ22YP.js +0 -449
- package/dist/chunk-5GDZ22YP.js.map +0 -1
- package/dist/chunk-5KLVH3PY.js.map +0 -1
- package/dist/chunk-5RDRAO4B.js.map +0 -1
- package/dist/chunk-7N4J557C.js.map +0 -1
- package/dist/chunk-A7JLBK2Y.js +0 -3506
- package/dist/chunk-A7JLBK2Y.js.map +0 -1
- package/dist/chunk-ANSJPYGI.js.map +0 -1
- package/dist/chunk-BTZZUCLI.js +0 -264
- package/dist/chunk-BTZZUCLI.js.map +0 -1
- package/dist/chunk-C7D4NJ2D.js +0 -217
- package/dist/chunk-C7D4NJ2D.js.map +0 -1
- package/dist/chunk-CAGOB2N4.js.map +0 -1
- package/dist/chunk-DGMQEI6Y.js.map +0 -1
- package/dist/chunk-E75IPST5.js.map +0 -1
- package/dist/chunk-EYGD6X2T.js +0 -180
- package/dist/chunk-EYGD6X2T.js.map +0 -1
- package/dist/chunk-GTVB4HEM.js +0 -765
- package/dist/chunk-GTVB4HEM.js.map +0 -1
- package/dist/chunk-J7MIZ5CA.js.map +0 -1
- package/dist/chunk-KWHQ2XXL.js.map +0 -1
- package/dist/chunk-L6LK2QIQ.js +0 -657
- package/dist/chunk-L6LK2QIQ.js.map +0 -1
- package/dist/chunk-MAQXCUR4.js +0 -1007
- package/dist/chunk-MAQXCUR4.js.map +0 -1
- package/dist/chunk-S4XL7HOM.js +0 -402
- package/dist/chunk-S4XL7HOM.js.map +0 -1
- package/dist/chunk-URLQAXTR.js.map +0 -1
- package/dist/chunk-V4BOYR52.js.map +0 -1
- package/dist/chunk-VZQXX3WW.js.map +0 -1
- package/dist/chunk-WKBO5R4O.js.map +0 -1
- package/dist/chunk-XIFFTATU.js.map +0 -1
- package/dist/context-ZC5IFEAX.js +0 -25
- package/dist/interactive-assets-CCAAWVKH.js.map +0 -1
- package/dist/loader-4IDS2RIE.js +0 -15
- package/dist/openai-compatible-RHZUBHV6.js +0 -10
- package/dist/permission-approvals-76PVREFD.js +0 -14
- package/dist/permissions-CHCKTFQQ.js +0 -28
- package/dist/sandbox-CNVAQDA7.js +0 -23
- package/dist/scrollback-LUKLIBVI.js.map +0 -1
- package/dist/settings-loader-M2QP5TX2.js +0 -21
- package/dist/shell-selection-V6QAYXPX.js +0 -17
- package/patches/ink+6.8.0.patch +0 -13
- package/scripts/apply-ink-patch.mjs +0 -29
- package/scripts/ink-patch.mjs +0 -59
- package/scripts/verify-ink-patch.mjs +0 -11
- /package/dist/{command-approvals-6Y57VCWZ.js.map → additional-roots-VAWRQ625.js.map} +0 -0
- /package/dist/{context-ZC5IFEAX.js.map → command-approvals-ZRY66LCW.js.map} +0 -0
- /package/dist/{hook-approvals-CTUFKEJT.js.map → context-C3RAP6K5.js.map} +0 -0
- /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-DPQD5H65.js.map} +0 -0
- /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-DQNPDKOX.js.map} +0 -0
- /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-27LJYADQ.js.map} +0 -0
- /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-GXSODNCM.js.map} +0 -0
- /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-I6Z54VUN.js.map} +0 -0
- /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-6VKXXTPJ.js.map} +0 -0
- /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-IPUNBJ53.js.map} +0 -0
- /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-Q5IMFEZI.js.map} +0 -0
- /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-7Y35X6HZ.js.map} +0 -0
- /package/dist/{settings-removed-JWWPT4WA.js.map → provider-approvals-5AGH3RVT.js.map} +0 -0
- /package/dist/{shell-selection-V6QAYXPX.js.map → sandbox-N2RY72GN.js.map} +0 -0
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
projectFileWriteRefusal
|
|
3
|
+
} from "./chunk-IDYWN37T.js";
|
|
4
|
+
|
|
1
5
|
// src/settings.ts
|
|
2
6
|
import { z } from "zod";
|
|
3
7
|
var permissionRuleSchema = z.string().min(1);
|
|
8
|
+
function unboundedInt() {
|
|
9
|
+
return z.number().check((ctx) => {
|
|
10
|
+
if (!Number.isInteger(ctx.value)) {
|
|
11
|
+
ctx.issues.push({ code: "invalid_type", expected: "int", input: ctx.value });
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
4
15
|
var sandboxSchema = z.object({
|
|
5
16
|
enabled: z.boolean().default(false),
|
|
6
17
|
failIfUnavailable: z.boolean().default(false),
|
|
@@ -10,12 +21,25 @@ var sandboxSchema = z.object({
|
|
|
10
21
|
filesystem: z.object({
|
|
11
22
|
allowWrite: z.array(z.string()).default([]),
|
|
12
23
|
denyWrite: z.array(z.string()).default([]),
|
|
13
|
-
denyRead: z.array(z.string()).default([])
|
|
14
|
-
|
|
24
|
+
denyRead: z.array(z.string()).default([]),
|
|
25
|
+
/**
|
|
26
|
+
* Off (the default) binds every git directory of the workspace read-only inside
|
|
27
|
+
* the sandbox, which also closes the pointer-file redirect a writable git dir
|
|
28
|
+
* leaves open (issue 373): a sandboxed command can create `.git/commondir` in a
|
|
29
|
+
* plain repository, and the host's git reads it on its next command. Sandboxed
|
|
30
|
+
* git writes — commit, checkout, add, stash, fetch — fail while it is off, and
|
|
31
|
+
* the failed output names this setting among the remedies. On restores the
|
|
32
|
+
* previous shape (git directories writable, their hooks, config and pointer
|
|
33
|
+
* files read-only, the top-level `.git` pinned) and reopens the pointer-file
|
|
34
|
+
* gap. Only a trusted layer may set it: a workspace file may tighten it to
|
|
35
|
+
* `false` and never back to `true`.
|
|
36
|
+
*/
|
|
37
|
+
allowGitWrites: z.boolean().default(false)
|
|
38
|
+
}).prefault({}),
|
|
15
39
|
network: z.object({
|
|
16
40
|
allowedDomains: z.array(z.string()).default([]),
|
|
17
41
|
deniedDomains: z.array(z.string()).default([])
|
|
18
|
-
}).
|
|
42
|
+
}).prefault({})
|
|
19
43
|
});
|
|
20
44
|
var projectAllowRuleChoiceSchema = z.enum(["approved", "rejected"]);
|
|
21
45
|
var permissionsSchema = z.object({
|
|
@@ -26,7 +50,7 @@ var permissionsSchema = z.object({
|
|
|
26
50
|
* Decisions about `allow` rules the project layer declared. A project rule
|
|
27
51
|
* with no `approved` entry here never reaches the resolved allow list.
|
|
28
52
|
*/
|
|
29
|
-
projectAllowRules: z.record(projectAllowRuleChoiceSchema).default({})
|
|
53
|
+
projectAllowRules: z.record(z.string(), projectAllowRuleChoiceSchema).default({})
|
|
30
54
|
});
|
|
31
55
|
var hookEntrySchema = z.object({
|
|
32
56
|
/** Tool(specifier) pattern to filter which events trigger this hook. */
|
|
@@ -34,7 +58,7 @@ var hookEntrySchema = z.object({
|
|
|
34
58
|
/** Shell command to run (passed through system shell). */
|
|
35
59
|
command: z.string().min(1),
|
|
36
60
|
/** Extra environment variables for this hook. */
|
|
37
|
-
env: z.record(z.string()).default({})
|
|
61
|
+
env: z.record(z.string(), z.string()).default({})
|
|
38
62
|
});
|
|
39
63
|
var projectHookChoiceSchema = z.enum(["approved", "rejected"]);
|
|
40
64
|
var HOOK_EVENTS = [
|
|
@@ -66,7 +90,7 @@ var hooksSchema = z.object({
|
|
|
66
90
|
* entry whose fingerprint has no `approved` record here never reaches the
|
|
67
91
|
* resolved hooks.
|
|
68
92
|
*/
|
|
69
|
-
projectEntries: z.record(projectHookChoiceSchema).default({})
|
|
93
|
+
projectEntries: z.record(z.string(), projectHookChoiceSchema).default({})
|
|
70
94
|
});
|
|
71
95
|
var retrySettingsSchema = z.object({
|
|
72
96
|
maxAttempts: z.number().int().min(0).max(15).default(10),
|
|
@@ -85,15 +109,31 @@ var retrySettingsSchema = z.object({
|
|
|
85
109
|
* timeout to it cancels a healthy request and reports `stream_stall`.
|
|
86
110
|
*/
|
|
87
111
|
thinkingStallTimeoutMs: z.number().int().min(1e4).max(18e5).default(9e5),
|
|
112
|
+
/**
|
|
113
|
+
* First-delta ceiling for a model that may think silently (#379).
|
|
114
|
+
*
|
|
115
|
+
* A router that buffers the model's whole thinking block sends a role-only
|
|
116
|
+
* first chunk and then nothing until the thinking is done — up to about 80 s —
|
|
117
|
+
* which the chat ceiling reads as a fault. While a stream has shown no
|
|
118
|
+
* content, tool call or reasoning delta yet, and neither `reasoning_effort`
|
|
119
|
+
* nor the model's catalog `effort` entry settles what it is, silence is
|
|
120
|
+
* judged by this ceiling instead. The first meaningful delta hands the stream
|
|
121
|
+
* back to the per-phase ceilings.
|
|
122
|
+
*/
|
|
123
|
+
firstDeltaStallTimeoutMs: z.number().int().min(5e3).max(18e5).default(12e4),
|
|
88
124
|
toolRetries: z.number().int().min(0).max(3).default(1),
|
|
89
125
|
watchdog: z.boolean().default(false),
|
|
90
126
|
/**
|
|
91
127
|
* How many times a turn may be re-sent after a transport fault that left the
|
|
92
|
-
* work intact — a stalled stream, a dropped socket
|
|
128
|
+
* work intact — a stalled stream, a dropped socket — and how many times an
|
|
129
|
+
* empty completion may be re-asked. The two are counted separately per turn;
|
|
130
|
+
* this only says how many of each a turn gets.
|
|
93
131
|
*
|
|
94
132
|
* `maxAttempts` covers connection setup only; once a 200 response is streaming
|
|
95
133
|
* it is out of scope, so without this a single 20-second provider silence ended
|
|
96
|
-
* the run. Set to 0 to restore that behavior exactly.
|
|
134
|
+
* the run. Set to 0 to restore that behavior exactly for transport faults. An
|
|
135
|
+
* empty completion still gets one retry, the floor every earlier version spent,
|
|
136
|
+
* so a run does not end on the first blank answer.
|
|
97
137
|
*/
|
|
98
138
|
streamReissueAttempts: z.number().int().min(0).max(10).default(3),
|
|
99
139
|
/**
|
|
@@ -106,8 +146,14 @@ var retrySettingsSchema = z.object({
|
|
|
106
146
|
var effortLevelSchema = z.enum(["low", "medium", "high", "xhigh", "max"]);
|
|
107
147
|
var providerModelSchema = z.object({
|
|
108
148
|
label: z.string().optional(),
|
|
109
|
-
contextWindow:
|
|
110
|
-
|
|
149
|
+
contextWindow: unboundedInt().positive().optional(),
|
|
150
|
+
/**
|
|
151
|
+
* The input limit the endpoint's model list reported. Written by discovery and overwritten on
|
|
152
|
+
* every refresh; ranks below a declared `contextWindow` and a learned ceiling, so a figure that
|
|
153
|
+
* is too large can still be corrected.
|
|
154
|
+
*/
|
|
155
|
+
reportedContextWindow: unboundedInt().positive().optional(),
|
|
156
|
+
maxOutputTokens: unboundedInt().positive().optional(),
|
|
111
157
|
/** Whether this model accepts image input. Unknown models remain optimistic. */
|
|
112
158
|
vision: z.boolean().optional(),
|
|
113
159
|
/**
|
|
@@ -126,12 +172,13 @@ var providerModelSchema = z.object({
|
|
|
126
172
|
})
|
|
127
173
|
]).optional()
|
|
128
174
|
});
|
|
175
|
+
var ADVISOR_OFF_VALUE = "off";
|
|
129
176
|
var providerConfigSchema = z.object({
|
|
130
177
|
type: z.enum(["openai", "anthropic"]).default("openai"),
|
|
131
178
|
baseURL: z.string().min(1).optional(),
|
|
132
179
|
baseUrl: z.string().min(1).optional(),
|
|
133
180
|
apiKey: z.string().min(1).optional(),
|
|
134
|
-
models: z.record(providerModelSchema).default({})
|
|
181
|
+
models: z.record(z.string(), providerModelSchema).default({})
|
|
135
182
|
});
|
|
136
183
|
var continuationSettingsSchema = z.object({
|
|
137
184
|
enabled: z.boolean().default(false),
|
|
@@ -150,9 +197,10 @@ var continuationSettingsSchema = z.object({
|
|
|
150
197
|
* predates continuation and needs no continuation to happen: a headless run in
|
|
151
198
|
* the default permission mode answers every prompt `deny`, so the model can
|
|
152
199
|
* re-issue a refused call forever while `toolCalls.length > 0` keeps the
|
|
153
|
-
* turn-end gate from ever firing. None of the existing anti-loop signals
|
|
154
|
-
* either — the repeated-failure breaker
|
|
155
|
-
* and `toolCallStats.failures` excludes
|
|
200
|
+
* turn-end gate from ever firing. None of the existing anti-loop signals stop it
|
|
201
|
+
* either — the repeated-failure breaker only annotates a repeated call and never
|
|
202
|
+
* sees the loop's own permission refusals, and `toolCallStats.failures` excludes
|
|
203
|
+
* `blocked` by construction.
|
|
156
204
|
*
|
|
157
205
|
* 0 disables the guard.
|
|
158
206
|
*/
|
|
@@ -160,12 +208,12 @@ var continuationSettingsSchema = z.object({
|
|
|
160
208
|
/** Turns between host-authored work-state messages; 0 disables them. */
|
|
161
209
|
planRefreshTurns: z.number().int().min(0).max(500).default(25),
|
|
162
210
|
/** Wall-clock ceiling for one continued run; 0 means no ceiling. */
|
|
163
|
-
maxWallClockMs:
|
|
211
|
+
maxWallClockMs: unboundedInt().min(0).default(0)
|
|
164
212
|
});
|
|
165
213
|
var memorySettingsSchema = z.object({
|
|
166
214
|
enabled: z.boolean().default(true),
|
|
167
|
-
|
|
168
|
-
|
|
215
|
+
/** False makes memory read-only: it still loads, and the model cannot write it. */
|
|
216
|
+
autoSave: z.boolean().default(true)
|
|
169
217
|
});
|
|
170
218
|
var agentSettingsSchema = z.object({
|
|
171
219
|
mode: z.enum(["adaptive", "manual", "off"]).default("adaptive"),
|
|
@@ -176,7 +224,7 @@ var agentSettingsSchema = z.object({
|
|
|
176
224
|
includeUntrackedInSnapshot: z.boolean().default(true),
|
|
177
225
|
telemetry: z.boolean().default(true),
|
|
178
226
|
retentionDays: z.number().int().min(1).max(3650).default(30),
|
|
179
|
-
checks: z.record(z.union([z.string().min(1), z.array(z.string().min(1)).min(1)])).default({}),
|
|
227
|
+
checks: z.record(z.string(), z.union([z.string().min(1), z.array(z.string().min(1)).min(1)])).default({}),
|
|
180
228
|
/**
|
|
181
229
|
* Wall-clock ceiling for one `Check` run. The 120s default suits a focused
|
|
182
230
|
* suite; a full `npm test` on a large repository routinely exceeds it, and a
|
|
@@ -184,6 +232,8 @@ var agentSettingsSchema = z.object({
|
|
|
184
232
|
* reported as a failing suite. Raise it per project rather than per invocation.
|
|
185
233
|
*/
|
|
186
234
|
checkTimeoutMs: z.number().int().min(1e3).max(72e5).default(12e4),
|
|
235
|
+
/** Ceiling on a foreground delegation via the Task tool; overrides the 1800s default. */
|
|
236
|
+
taskTimeoutMs: unboundedInt().min(1e3).optional(),
|
|
187
237
|
/**
|
|
188
238
|
* Simultaneous agent worktrees per repository; 0 disables the check.
|
|
189
239
|
*
|
|
@@ -209,20 +259,21 @@ var agentSettingsSchema = z.object({
|
|
|
209
259
|
* Worktrees share the filesystem with the workspace, so exhausting it breaks
|
|
210
260
|
* the root agent's own Edit and Bash, not just the child's.
|
|
211
261
|
*/
|
|
212
|
-
minFreeDiskBytes:
|
|
262
|
+
minFreeDiskBytes: unboundedInt().min(0).default(2 * 1024 * 1024 * 1024),
|
|
213
263
|
profiles: z.record(
|
|
264
|
+
z.string(),
|
|
214
265
|
z.object({
|
|
215
266
|
model: z.string().min(1).optional(),
|
|
216
267
|
effort: effortLevelSchema.optional(),
|
|
217
|
-
maxTurns:
|
|
268
|
+
maxTurns: unboundedInt().min(1).optional(),
|
|
218
269
|
color: z.string().optional()
|
|
219
270
|
})
|
|
220
271
|
).default({}),
|
|
221
|
-
ui: z.object({ enabled: z.boolean().default(true) }).
|
|
272
|
+
ui: z.object({ enabled: z.boolean().default(true) }).prefault({}),
|
|
222
273
|
routing: z.object({
|
|
223
274
|
inlineSearchBudget: z.number().int().min(1).max(20).default(3),
|
|
224
275
|
exploreReminder: z.boolean().default(true)
|
|
225
|
-
}).
|
|
276
|
+
}).prefault({}),
|
|
226
277
|
forwardTextEvents: z.boolean().default(false)
|
|
227
278
|
});
|
|
228
279
|
var toolDiscoverySettingsSchema = z.object({
|
|
@@ -256,7 +307,7 @@ var mcpSettingsSchema = z.object({
|
|
|
256
307
|
* Per-server trust decisions for workspace `.mcp.json` declarations.
|
|
257
308
|
* User-global servers (<BOOK_HOME>/.book/mcp.json) never require approval.
|
|
258
309
|
*/
|
|
259
|
-
projectServers: z.record(mcpProjectServerChoiceSchema).default({})
|
|
310
|
+
projectServers: z.record(z.string(), mcpProjectServerChoiceSchema).default({})
|
|
260
311
|
});
|
|
261
312
|
var projectCommandChoiceSchema = z.object({
|
|
262
313
|
/** Digest of the shell the body substitutes at decision time; a mismatch re-prompts. */
|
|
@@ -271,7 +322,12 @@ var commandSettingsSchema = z.object({
|
|
|
271
322
|
* needs a decision before it runs. User-global commands
|
|
272
323
|
* (<BOOK_HOME>/commands) were written by the user and never require one.
|
|
273
324
|
*/
|
|
274
|
-
projectCommands: z.record(projectCommandChoiceSchema).default({})
|
|
325
|
+
projectCommands: z.record(z.string(), projectCommandChoiceSchema).default({})
|
|
326
|
+
});
|
|
327
|
+
var projectDirectoryChoiceSchema = z.enum(["approved", "rejected"]);
|
|
328
|
+
var workspaceProviderChoiceSchema = z.object({
|
|
329
|
+
fingerprint: z.string().min(1),
|
|
330
|
+
choice: z.enum(["approved", "rejected"])
|
|
275
331
|
});
|
|
276
332
|
var uiSettingsSchema = z.object({
|
|
277
333
|
/** Show provider-native and embedded model reasoning in the interactive transcript. */
|
|
@@ -285,9 +341,9 @@ var skillSettingsSchema = z.object({
|
|
|
285
341
|
/** Emergency switch that removes skill prompt and runtime effects without deleting packages. */
|
|
286
342
|
enabled: z.boolean().default(true),
|
|
287
343
|
/** Per-skill visibility overrides keyed by the skill's declared name. */
|
|
288
|
-
overrides: z.record(skillActivationSchema).default({}),
|
|
344
|
+
overrides: z.record(z.string(), skillActivationSchema).default({}),
|
|
289
345
|
/** Per-skill activation consent policy. Tool calls still use the normal permission system. */
|
|
290
|
-
execution: z.record(skillExecutionSchema).default({})
|
|
346
|
+
execution: z.record(z.string(), skillExecutionSchema).default({})
|
|
291
347
|
});
|
|
292
348
|
var compactStrategySchema = z.literal("summary");
|
|
293
349
|
var bookSettingsSchema = z.object({
|
|
@@ -296,11 +352,19 @@ var bookSettingsSchema = z.object({
|
|
|
296
352
|
compactStrategy: compactStrategySchema.optional(),
|
|
297
353
|
/** Optional model used only to generate historical conversation checkpoints. */
|
|
298
354
|
compactModel: z.string().min(1).optional(),
|
|
355
|
+
/** Reasoning effort for the compaction reducer; defaults to the session effort capped at medium. */
|
|
356
|
+
compactEffort: effortLevelSchema.optional(),
|
|
357
|
+
/**
|
|
358
|
+
* A stronger model the agent may consult through the `Advisor` tool (`/advisor`).
|
|
359
|
+
* Unset, the tool is not offered; {@link ADVISOR_OFF_VALUE} is the explicit "off" that a higher
|
|
360
|
+
* layer writes to outrank a lower layer's advisor.
|
|
361
|
+
*/
|
|
362
|
+
advisorModel: z.string().min(1).optional(),
|
|
299
363
|
/** Max agent turns per user message. Omit for unlimited. */
|
|
300
|
-
maxTurns:
|
|
301
|
-
maxTokens:
|
|
364
|
+
maxTurns: unboundedInt().min(1).optional(),
|
|
365
|
+
maxTokens: unboundedInt().min(1e3).optional(),
|
|
302
366
|
effort: effortLevelSchema.optional(),
|
|
303
|
-
/** TUI color theme:
|
|
367
|
+
/** TUI color theme: rubric (default), folio, apple, or a custom theme filename. */
|
|
304
368
|
theme: z.string().min(1).optional(),
|
|
305
369
|
/**
|
|
306
370
|
* The shell the Bash tool spawns: `bash`, `pwsh`, `powershell`, `cmd`, `sh`,
|
|
@@ -309,27 +373,46 @@ var bookSettingsSchema = z.object({
|
|
|
309
373
|
* the program every command is handed to.
|
|
310
374
|
*/
|
|
311
375
|
shell: z.string().min(1).optional(),
|
|
312
|
-
ui: uiSettingsSchema.
|
|
313
|
-
skills: skillSettingsSchema.
|
|
376
|
+
ui: uiSettingsSchema.prefault({}),
|
|
377
|
+
skills: skillSettingsSchema.prefault({}),
|
|
314
378
|
autoCompactEnabled: z.boolean().optional(),
|
|
315
379
|
/** Permission mode used by each host when no invocation-specific mode is supplied. */
|
|
316
380
|
defaultMode: z.enum(["default", "acceptEdits", "plan", "auto", "dontAsk", "bypassPermissions"]).optional(),
|
|
317
381
|
disableBypassPermissionsMode: z.boolean().optional(),
|
|
382
|
+
/**
|
|
383
|
+
* Extra roots the file tools serve, for paths outside the workspace. An honored entry is a root
|
|
384
|
+
* for reads *and* writes: `Write`, `Edit`, `MultiEdit`, `ApplyPatch` and `NotebookEdit` accept an
|
|
385
|
+
* absolute path inside one and treat the entry as they would the workspace, while a relative path
|
|
386
|
+
* stays anchored to the workspace. A project-declared entry is withheld until `book trust dir`.
|
|
387
|
+
*/
|
|
318
388
|
additionalDirectories: z.array(z.string()).default([]),
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
389
|
+
/**
|
|
390
|
+
* Decisions about `additionalDirectories` the project layer declared, keyed by the directory's
|
|
391
|
+
* real path (the declared text is only what the repository wrote; the path is what the file
|
|
392
|
+
* tools would serve). A project directory with no `approved` entry here is withheld, exactly
|
|
393
|
+
* as a project allow rule with no entry is.
|
|
394
|
+
*/
|
|
395
|
+
projectDirectories: z.record(z.string(), projectDirectoryChoiceSchema).default({}),
|
|
396
|
+
/**
|
|
397
|
+
* Decisions about providers only a workspace layer declares, keyed by provider id (and by
|
|
398
|
+
* `.bookrc.json` for its legacy `baseUrl`). A workspace-declared provider with no matching
|
|
399
|
+
* `approved` entry is withheld: it is absent from `provider`, and nothing it names is resolved.
|
|
400
|
+
*/
|
|
401
|
+
workspaceProviders: z.record(z.string(), workspaceProviderChoiceSchema).default({}),
|
|
402
|
+
env: z.record(z.string(), z.string()).default({}),
|
|
403
|
+
provider: z.record(z.string(), providerConfigSchema).default({}),
|
|
404
|
+
permissions: permissionsSchema.prefault({}),
|
|
405
|
+
sandbox: sandboxSchema.prefault({}),
|
|
406
|
+
hooks: hooksSchema.prefault({}),
|
|
407
|
+
retry: retrySettingsSchema.prefault({}),
|
|
408
|
+
memory: memorySettingsSchema.prefault({}),
|
|
409
|
+
continuation: continuationSettingsSchema.prefault({}),
|
|
410
|
+
agents: agentSettingsSchema.prefault({}),
|
|
411
|
+
toolDiscovery: toolDiscoverySettingsSchema.prefault({}),
|
|
412
|
+
toolExecution: toolExecutionSettingsSchema.prefault({}),
|
|
413
|
+
observability: observabilitySettingsSchema.prefault({}),
|
|
414
|
+
mcp: mcpSettingsSchema.prefault({}),
|
|
415
|
+
commands: commandSettingsSchema.prefault({})
|
|
333
416
|
});
|
|
334
417
|
var DEFAULT_SETTINGS = {
|
|
335
418
|
compactStrategy: "summary",
|
|
@@ -340,7 +423,7 @@ var DEFAULT_SETTINGS = {
|
|
|
340
423
|
autoAllowBashIfSandboxed: true,
|
|
341
424
|
excludedCommands: [],
|
|
342
425
|
allowUnsandboxedCommands: true,
|
|
343
|
-
filesystem: { allowWrite: [], denyWrite: [], denyRead: [] },
|
|
426
|
+
filesystem: { allowWrite: [], denyWrite: [], denyRead: [], allowGitWrites: false },
|
|
344
427
|
network: { allowedDomains: [], deniedDomains: [] }
|
|
345
428
|
},
|
|
346
429
|
hooks: {
|
|
@@ -358,6 +441,8 @@ var DEFAULT_SETTINGS = {
|
|
|
358
441
|
projectEntries: {}
|
|
359
442
|
},
|
|
360
443
|
additionalDirectories: [],
|
|
444
|
+
projectDirectories: {},
|
|
445
|
+
workspaceProviders: {},
|
|
361
446
|
env: {},
|
|
362
447
|
provider: {},
|
|
363
448
|
ui: { showThinking: true, startupAnimation: true },
|
|
@@ -370,6 +455,7 @@ var DEFAULT_SETTINGS = {
|
|
|
370
455
|
requestTimeoutMs: 6e5,
|
|
371
456
|
streamStallTimeoutMs: 2e4,
|
|
372
457
|
thinkingStallTimeoutMs: 9e5,
|
|
458
|
+
firstDeltaStallTimeoutMs: 12e4,
|
|
373
459
|
toolRetries: 1,
|
|
374
460
|
watchdog: false,
|
|
375
461
|
streamReissueAttempts: 3,
|
|
@@ -385,8 +471,7 @@ var DEFAULT_SETTINGS = {
|
|
|
385
471
|
},
|
|
386
472
|
memory: {
|
|
387
473
|
enabled: true,
|
|
388
|
-
autoSave: true
|
|
389
|
-
requireApproval: true
|
|
474
|
+
autoSave: true
|
|
390
475
|
},
|
|
391
476
|
agents: {
|
|
392
477
|
mode: "adaptive",
|
|
@@ -429,21 +514,6 @@ var DEFAULT_SETTINGS = {
|
|
|
429
514
|
}
|
|
430
515
|
};
|
|
431
516
|
|
|
432
|
-
// src/settings-repository.ts
|
|
433
|
-
import {
|
|
434
|
-
closeSync,
|
|
435
|
-
existsSync,
|
|
436
|
-
fsyncSync,
|
|
437
|
-
mkdirSync,
|
|
438
|
-
openSync,
|
|
439
|
-
readFileSync,
|
|
440
|
-
renameSync,
|
|
441
|
-
statSync,
|
|
442
|
-
unlinkSync,
|
|
443
|
-
writeSync
|
|
444
|
-
} from "fs";
|
|
445
|
-
import { basename, dirname, join } from "path";
|
|
446
|
-
|
|
447
517
|
// src/cli/utils.ts
|
|
448
518
|
function getNestedValue(obj, path) {
|
|
449
519
|
const parts = path.split(".");
|
|
@@ -507,6 +577,35 @@ function parseNumericFlag(raw, flag, opts = {}) {
|
|
|
507
577
|
}
|
|
508
578
|
return value;
|
|
509
579
|
}
|
|
580
|
+
function resolvePrintPrompt(print, positional) {
|
|
581
|
+
if (positional === void 0) return { print };
|
|
582
|
+
if (print === void 0) {
|
|
583
|
+
return {
|
|
584
|
+
error: `unexpected argument '${positional}'. A prompt on the command line needs -p/--print (book -p "${positional}"); run book --help for the subcommands.`
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
if (typeof print === "string") {
|
|
588
|
+
return {
|
|
589
|
+
error: `the prompt was given twice: --print '${print}' and the argument '${positional}'. Pass it once, either as the --print value or as the argument.`
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
return { print: positional };
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/settings-repository.ts
|
|
596
|
+
import {
|
|
597
|
+
closeSync,
|
|
598
|
+
existsSync,
|
|
599
|
+
fsyncSync,
|
|
600
|
+
mkdirSync,
|
|
601
|
+
openSync,
|
|
602
|
+
readFileSync,
|
|
603
|
+
renameSync,
|
|
604
|
+
statSync,
|
|
605
|
+
unlinkSync,
|
|
606
|
+
writeSync
|
|
607
|
+
} from "fs";
|
|
608
|
+
import { basename, dirname, join } from "path";
|
|
510
609
|
|
|
511
610
|
// src/settings-redaction.ts
|
|
512
611
|
var REDACTED_SECRET = "*** (stored)";
|
|
@@ -626,6 +725,10 @@ var SettingsRepository = class {
|
|
|
626
725
|
return this.mutate(mutation, displayValues);
|
|
627
726
|
}
|
|
628
727
|
mutate(mutation, values) {
|
|
728
|
+
const refusal = projectFileWriteRefusal(this.path);
|
|
729
|
+
if (refusal) {
|
|
730
|
+
return { ok: false, path: this.path, diagnostics: [{ path: this.path, message: refusal }] };
|
|
731
|
+
}
|
|
629
732
|
let lock;
|
|
630
733
|
try {
|
|
631
734
|
lock = this.acquireLock();
|
|
@@ -719,18 +822,24 @@ function formatSettingsDiagnostics(diagnostics) {
|
|
|
719
822
|
}
|
|
720
823
|
|
|
721
824
|
export {
|
|
825
|
+
unboundedInt,
|
|
722
826
|
projectAllowRuleChoiceSchema,
|
|
723
827
|
projectHookChoiceSchema,
|
|
724
828
|
HOOK_EVENTS,
|
|
725
829
|
effortLevelSchema,
|
|
830
|
+
ADVISOR_OFF_VALUE,
|
|
726
831
|
providerConfigSchema,
|
|
727
832
|
mcpProjectServerChoiceSchema,
|
|
728
833
|
projectCommandChoiceSchema,
|
|
834
|
+
projectDirectoryChoiceSchema,
|
|
835
|
+
workspaceProviderChoiceSchema,
|
|
729
836
|
bookSettingsSchema,
|
|
730
837
|
DEFAULT_SETTINGS,
|
|
731
838
|
getNestedValue,
|
|
732
839
|
setNestedValue,
|
|
840
|
+
deleteNestedValue,
|
|
733
841
|
parseNumericFlag,
|
|
842
|
+
resolvePrintPrompt,
|
|
734
843
|
redactSettingValue,
|
|
735
844
|
redactSettingsForDisplay,
|
|
736
845
|
SETTINGS_TOP_LEVEL_KEYS,
|
|
@@ -740,4 +849,4 @@ export {
|
|
|
740
849
|
SettingsRepository,
|
|
741
850
|
formatSettingsDiagnostics
|
|
742
851
|
};
|
|
743
|
-
//# sourceMappingURL=chunk-
|
|
852
|
+
//# sourceMappingURL=chunk-REASC2QG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/settings.ts","../src/cli/utils.ts","../src/settings-repository.ts","../src/settings-redaction.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Permission rule: \"Tool\" or \"Tool(specifier)\" where specifier is a glob pattern\n * matched against the tool's primary argument (e.g. bash command, file path).\n */\nexport const permissionRuleSchema = z.string().min(1);\n\n// An object schema defaulted to `{}` uses `.prefault({})`, not `.default({})`: Zod 4's `.default()`\n// returns its value without parsing it, so the object's own field defaults would never be applied.\n// Records and arrays keep `.default({})` / `.default([])`, whose values are already final.\n\n/**\n * An integer setting with no ceiling of its own.\n *\n * Zod 4's `.int()` also rejects anything above `Number.MAX_SAFE_INTEGER`, which Zod 3 accepted. A\n * settings file that loaded before the upgrade, such as one holding an \"effectively unlimited\"\n * `maxTurns`, must keep loading: an invalid document stops Book from starting. So these settings\n * check integrality themselves, reporting the same `invalid_type` issue `.int()` does. Bounded\n * ones keep `.int()`, since their `.max()` is far lower.\n */\nexport function unboundedInt() {\n return z.number().check((ctx) => {\n if (!Number.isInteger(ctx.value)) {\n ctx.issues.push({ code: 'invalid_type', expected: 'int', input: ctx.value });\n }\n });\n}\n\n/**\n * Bash sandbox configuration (matches CC's sandbox.* keys).\n */\nexport const sandboxSchema = z.object({\n enabled: z.boolean().default(false),\n failIfUnavailable: z.boolean().default(false),\n autoAllowBashIfSandboxed: z.boolean().default(true),\n excludedCommands: z.array(z.string()).default([]),\n allowUnsandboxedCommands: z.boolean().default(true),\n filesystem: z\n .object({\n allowWrite: z.array(z.string()).default([]),\n denyWrite: z.array(z.string()).default([]),\n denyRead: z.array(z.string()).default([]),\n /**\n * Off (the default) binds every git directory of the workspace read-only inside\n * the sandbox, which also closes the pointer-file redirect a writable git dir\n * leaves open (issue 373): a sandboxed command can create `.git/commondir` in a\n * plain repository, and the host's git reads it on its next command. Sandboxed\n * git writes — commit, checkout, add, stash, fetch — fail while it is off, and\n * the failed output names this setting among the remedies. On restores the\n * previous shape (git directories writable, their hooks, config and pointer\n * files read-only, the top-level `.git` pinned) and reopens the pointer-file\n * gap. Only a trusted layer may set it: a workspace file may tighten it to\n * `false` and never back to `true`.\n */\n allowGitWrites: z.boolean().default(false),\n })\n .prefault({}),\n network: z\n .object({\n allowedDomains: z.array(z.string()).default([]),\n deniedDomains: z.array(z.string()).default([]),\n })\n .prefault({}),\n});\n\n/**\n * Permission configuration (matches CC's permissions key).\n */\n/**\n * Per-rule trust decisions for `permissions.allow` entries declared by the\n * checked-in project layer.\n *\n * Unlike an MCP server, a permission rule *is* its own identity: change the\n * rule and it is a different key, so no fingerprint is needed to detect drift.\n * Decisions are stored per workspace in `.book/settings.local.json`, which the\n * repository cannot write.\n */\nexport const projectAllowRuleChoiceSchema = z.enum(['approved', 'rejected']);\n\nexport type ProjectAllowRuleChoice = z.infer<typeof projectAllowRuleChoiceSchema>;\n\nexport const permissionsSchema = z.object({\n allow: z.array(permissionRuleSchema).default([]),\n ask: z.array(permissionRuleSchema).default([]),\n deny: z.array(permissionRuleSchema).default([]),\n /**\n * Decisions about `allow` rules the project layer declared. A project rule\n * with no `approved` entry here never reaches the resolved allow list.\n */\n projectAllowRules: z.record(z.string(), projectAllowRuleChoiceSchema).default({}),\n});\n\n/**\n * Hook entry: a shell command that runs at a lifecycle event.\n * Matches Claude Code's hook configuration format.\n */\nexport const hookEntrySchema = z.object({\n /** Tool(specifier) pattern to filter which events trigger this hook. */\n matcher: z.string().optional(),\n /** Shell command to run (passed through system shell). */\n command: z.string().min(1),\n /** Extra environment variables for this hook. */\n env: z.record(z.string(), z.string()).default({}),\n});\n\nexport type HookEntry = z.infer<typeof hookEntrySchema>;\n\n/**\n * Per-entry trust decisions for hook entries declared by the checked-in project\n * layer, keyed by a fingerprint of `{ event, matcher, command, env }`.\n *\n * A hook is a shell command Book executes at lifecycle events, so a project\n * hook with no `approved` entry here never reaches the resolved hooks. Unlike\n * an MCP server record, a hook carries no name — the fingerprint is the key.\n */\nexport const projectHookChoiceSchema = z.enum(['approved', 'rejected']);\n\nexport type ProjectHookChoice = z.infer<typeof projectHookChoiceSchema>;\n\n/** Hook events supported by book. */\nexport const HOOK_EVENTS = [\n 'SessionStart',\n 'SessionEnd',\n 'UserPromptSubmit',\n 'PreToolUse',\n 'PostToolUse',\n 'Stop',\n 'PreCompact',\n 'PostCompact',\n 'SubagentStart',\n 'SubagentStop',\n /**\n * Something a human should know about while nobody is watching: a run parked on\n * a rejected credential, a budget crossing its ceiling, a refused spawn because\n * the disk is nearly full. Wire ntfy/Slack/SMS here as an ordinary shell hook.\n *\n * Only `severity: 'alarm'` is meant to wake anyone; everything else is a line to\n * tail. `hooksSchema` is generated from this array, so the schema cost is one\n * entry.\n */\n 'Notification',\n] as const;\n\nexport type HookEvent = (typeof HOOK_EVENTS)[number];\n\n/**\n * Hooks configuration — a map from event name to an array of hook entries.\n */\nexport const hooksSchema = z.object({\n ...(Object.fromEntries(HOOK_EVENTS.map((e) => [e, z.array(hookEntrySchema).default([])])) as {\n [K in HookEvent]: z.ZodDefault<z.ZodArray<typeof hookEntrySchema>>;\n }),\n /**\n * Decisions about hook entries the checked-in project layer declared. An\n * entry whose fingerprint has no `approved` record here never reaches the\n * resolved hooks.\n */\n projectEntries: z.record(z.string(), projectHookChoiceSchema).default({}),\n});\n\nexport type HooksConfig = z.infer<typeof hooksSchema>;\n\n/**\n * Retry configuration schema — mirrors Claude Code's retry tunables.\n */\nexport const retrySettingsSchema = z.object({\n maxAttempts: z.number().int().min(0).max(15).default(10),\n baseDelayMs: z.number().int().min(100).max(60000).default(1000),\n maxDelayMs: z.number().int().min(100).max(300000).default(30000),\n totalBudgetMs: z.number().int().min(0).max(600000).default(0),\n requestTimeoutMs: z.number().int().min(5000).max(600000).default(600000),\n streamStallTimeoutMs: z.number().int().min(5000).max(120000).default(20000),\n /**\n * Stall ceiling while the model is thinking, which is a different regime.\n *\n * `streamStallTimeoutMs` is tuned for a chat: 20s of silence means something\n * broke. But with adaptive thinking on — the default for every Opus and Sonnet\n * model here, at `high` effort unless told otherwise — a long quiet stretch\n * before the first token is the model working, not a fault. Applying the chat\n * timeout to it cancels a healthy request and reports `stream_stall`.\n */\n thinkingStallTimeoutMs: z.number().int().min(10_000).max(1_800_000).default(900_000),\n /**\n * First-delta ceiling for a model that may think silently (#379).\n *\n * A router that buffers the model's whole thinking block sends a role-only\n * first chunk and then nothing until the thinking is done — up to about 80 s —\n * which the chat ceiling reads as a fault. While a stream has shown no\n * content, tool call or reasoning delta yet, and neither `reasoning_effort`\n * nor the model's catalog `effort` entry settles what it is, silence is\n * judged by this ceiling instead. The first meaningful delta hands the stream\n * back to the per-phase ceilings.\n */\n firstDeltaStallTimeoutMs: z.number().int().min(5_000).max(1_800_000).default(120_000),\n toolRetries: z.number().int().min(0).max(3).default(1),\n watchdog: z.boolean().default(false),\n /**\n * How many times a turn may be re-sent after a transport fault that left the\n * work intact — a stalled stream, a dropped socket — and how many times an\n * empty completion may be re-asked. The two are counted separately per turn;\n * this only says how many of each a turn gets.\n *\n * `maxAttempts` covers connection setup only; once a 200 response is streaming\n * it is out of scope, so without this a single 20-second provider silence ended\n * the run. Set to 0 to restore that behavior exactly for transport faults. An\n * empty completion still gets one retry, the floor every earlier version spent,\n * so a run does not end on the first blank answer.\n */\n streamReissueAttempts: z.number().int().min(0).max(10).default(3),\n /**\n * Separate allowance for continuing after the provider's output cap. A large\n * generated file legitimately hits the cap on consecutive turns, and sharing\n * the transport budget would leave nothing for a real socket drop afterwards.\n */\n outputCapContinuations: z.number().int().min(0).max(50).default(10),\n});\n\nexport type RetrySettings = z.infer<typeof retrySettingsSchema>;\n\nexport const effortLevelSchema = z.enum(['low', 'medium', 'high', 'xhigh', 'max']);\n\nexport const providerModelSchema = z.object({\n label: z.string().optional(),\n contextWindow: unboundedInt().positive().optional(),\n /**\n * The input limit the endpoint's model list reported. Written by discovery and overwritten on\n * every refresh; ranks below a declared `contextWindow` and a learned ceiling, so a figure that\n * is too large can still be corrected.\n */\n reportedContextWindow: unboundedInt().positive().optional(),\n maxOutputTokens: unboundedInt().positive().optional(),\n /** Whether this model accepts image input. Unknown models remain optimistic. */\n vision: z.boolean().optional(),\n /**\n * Entered by hand rather than returned by the endpoint's model-list API.\n * Refreshing the catalog keeps these, since they exist precisely because\n * discovery does not list them.\n */\n manual: z.boolean().optional(),\n /** Mutation-tool preference for this model; defaults to a family-level prior. */\n editFormat: z.enum(['patch', 'replace', 'whole']).optional(),\n effort: z\n .union([\n z.literal(false),\n z.object({\n default: effortLevelSchema.optional(),\n levels: z.array(effortLevelSchema).optional(),\n }),\n ])\n .optional(),\n});\n\n/**\n * The `advisorModel` value that means \"no advisor\". `/advisor off` removes the user layer's key,\n * but a checked-in `.book/settings.json` that declares an advisor would come back at the next\n * launch, so the local layer (which outranks it) gets this instead (#418).\n */\nexport const ADVISOR_OFF_VALUE = 'off';\n\nexport const providerConfigSchema = z.object({\n type: z.enum(['openai', 'anthropic']).default('openai'),\n baseURL: z.string().min(1).optional(),\n baseUrl: z.string().min(1).optional(),\n apiKey: z.string().min(1).optional(),\n models: z.record(z.string(), providerModelSchema).default({}),\n});\n\n/**\n * Continuation: whether a turn that stops with no tool calls may be re-driven.\n *\n * `runAgentLoop` ends as soon as a turn produces no tool calls, so one user\n * message is the whole run and a model that writes \"I've finished\" stops there.\n * Enabling this lets the loop append a host-authored user turn and keep going\n * while there is demonstrably work left.\n *\n * Off by default: it changes when a run ends, and continuation without the\n * no-progress brake below would let a stalled run spin and bill silently, where\n * today it stops and a human notices.\n */\nexport const continuationSettingsSchema = z.object({\n enabled: z.boolean().default(false),\n /** Consecutive host-authored continuations before the run stops regardless. */\n maxConsecutive: z.number().int().min(1).max(1000).default(50),\n /**\n * Identical progress witnesses in a row before the run stops as no-progress.\n * Minimum 1: a 0 here would fire on the first boundary, before any witness\n * exists, and label a run that never got a chance to spin as spinning.\n */\n noProgressLimit: z.number().int().min(1).max(20).default(3),\n /**\n * Consecutive turns in which EVERY tool call was refused before the run stops.\n *\n * Enforced even when `enabled` is false, deliberately. The spin it catches\n * predates continuation and needs no continuation to happen: a headless run in\n * the default permission mode answers every prompt `deny`, so the model can\n * re-issue a refused call forever while `toolCalls.length > 0` keeps the\n * turn-end gate from ever firing. None of the existing anti-loop signals stop it\n * either — the repeated-failure breaker only annotates a repeated call and never\n * sees the loop's own permission refusals, and `toolCallStats.failures` excludes\n * `blocked` by construction.\n *\n * 0 disables the guard.\n */\n blockedToolTurnLimit: z.number().int().min(0).max(100).default(3),\n /** Turns between host-authored work-state messages; 0 disables them. */\n planRefreshTurns: z.number().int().min(0).max(500).default(25),\n /** Wall-clock ceiling for one continued run; 0 means no ceiling. */\n maxWallClockMs: unboundedInt().min(0).default(0),\n});\n\nexport type ContinuationSettings = z.infer<typeof continuationSettingsSchema>;\n\nexport const memorySettingsSchema = z.object({\n enabled: z.boolean().default(true),\n /** False makes memory read-only: it still loads, and the model cannot write it. */\n autoSave: z.boolean().default(true),\n});\n\nexport const agentSettingsSchema = z.object({\n mode: z.enum(['adaptive', 'manual', 'off']).default('adaptive'),\n maxConcurrent: z.number().int().min(1).max(16).default(3),\n maxSpawned: z.number().int().min(1).max(64).default(8),\n maxDepth: z.literal(1).default(1),\n persist: z.boolean().default(true),\n includeUntrackedInSnapshot: z.boolean().default(true),\n telemetry: z.boolean().default(true),\n retentionDays: z.number().int().min(1).max(3650).default(30),\n checks: z\n .record(z.string(), z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]))\n .default({}),\n /**\n * Wall-clock ceiling for one `Check` run. The 120s default suits a focused\n * suite; a full `npm test` on a large repository routinely exceeds it, and a\n * check that always times out is worse than no check because the timeout was\n * reported as a failing suite. Raise it per project rather than per invocation.\n */\n checkTimeoutMs: z.number().int().min(1_000).max(7_200_000).default(120_000),\n /** Ceiling on a foreground delegation via the Task tool; overrides the 1800s default. */\n taskTimeoutMs: unboundedInt().min(1000).optional(),\n /**\n * Simultaneous agent worktrees per repository; 0 disables the check.\n *\n * Nothing reclaims a worktree automatically outside the TUI, and the store's\n * retention sweep runs once at startup with a 30-day default — so on a long\n * unattended run the count only grows. Sized above `maxConcurrent` so ordinary\n * fan-out is unaffected and only accumulation trips it.\n */\n maxWorktrees: z.number().int().min(0).max(256).default(24),\n /**\n * Re-drive agents that were interrupted by process death on the next start.\n *\n * A restart otherwise converts the whole pending backlog into terminal records\n * nothing picks up, silently losing hours of child work on a wide fan-out. The\n * re-drive is contained: explorers are read-only and patchers/validators run in\n * their own worktree, so nothing reaches the parent workspace without the usual\n * evidence gate. Only `process_exit` interruptions qualify — a user stop stays\n * stopped.\n */\n resumeInterrupted: z.boolean().default(true),\n /**\n * Refuse a new worktree when free disk would fall below this; 0 disables.\n * Worktrees share the filesystem with the workspace, so exhausting it breaks\n * the root agent's own Edit and Bash, not just the child's.\n */\n minFreeDiskBytes: unboundedInt()\n .min(0)\n .default(2 * 1024 * 1024 * 1024),\n profiles: z\n .record(\n z.string(),\n z.object({\n model: z.string().min(1).optional(),\n effort: effortLevelSchema.optional(),\n maxTurns: unboundedInt().min(1).optional(),\n color: z.string().optional(),\n }),\n )\n .default({}),\n ui: z.object({ enabled: z.boolean().default(true) }).prefault({}),\n routing: z\n .object({\n inlineSearchBudget: z.number().int().min(1).max(20).default(3),\n exploreReminder: z.boolean().default(true),\n })\n .prefault({}),\n forwardTextEvents: z.boolean().default(false),\n});\n\nexport const toolDiscoverySettingsSchema = z.object({\n mode: z.enum(['auto', 'eager', 'deferred']).default('auto'),\n eagerToolCount: z.number().int().min(1).max(100).default(10),\n schemaTokenBudget: z.number().int().min(1000).max(100000).default(8000),\n maxLoadedTools: z.number().int().min(1).max(100).default(15),\n searchLimit: z.number().int().min(1).max(5).default(5),\n});\n\nexport type ToolDiscoverySettings = z.infer<typeof toolDiscoverySettingsSchema>;\n\nexport const toolExecutionSettingsSchema = z.object({\n /** Shared across the root loop and managed children for parallel-safe tools. */\n maxConcurrent: z.number().int().min(1).max(8).default(4),\n});\n\nexport type ToolExecutionSettings = z.infer<typeof toolExecutionSettingsSchema>;\n\nexport const observabilitySettingsSchema = z.object({\n /** Persist a per-tool-call JSONL record for `book tool-stats`. */\n toolTelemetry: z.boolean().default(true),\n /**\n * Default reporting window (in days) for `book tool-stats` and the target for\n * `book tool-stats --prune`. Disk use is bounded by size-based log rotation;\n * records are only deleted when they rotate out or are explicitly pruned.\n */\n toolTelemetryRetentionDays: z.number().int().min(1).max(3650).default(30),\n});\n\nexport type ObservabilitySettings = z.infer<typeof observabilitySettingsSchema>;\n\nexport const mcpProjectServerChoiceSchema = z.object({\n /** Hash of the server's command/args/env at decision time; a mismatch re-prompts. */\n fingerprint: z.string().min(1),\n choice: z.enum(['approved', 'rejected']),\n});\n\nexport type McpProjectServerChoice = z.infer<typeof mcpProjectServerChoiceSchema>;\n\nexport const mcpSettingsSchema = z.object({\n /**\n * Per-server trust decisions for workspace `.mcp.json` declarations.\n * User-global servers (<BOOK_HOME>/.book/mcp.json) never require approval.\n */\n projectServers: z.record(z.string(), mcpProjectServerChoiceSchema).default({}),\n});\n\nexport type McpSettings = z.infer<typeof mcpSettingsSchema>;\n\nexport const projectCommandChoiceSchema = z.object({\n /** Digest of the shell the body substitutes at decision time; a mismatch re-prompts. */\n fingerprint: z.string().min(1),\n choice: z.enum(['approved', 'rejected']),\n});\n\nexport type ProjectCommandChoice = z.infer<typeof projectCommandChoiceSchema>;\n\nexport const commandSettingsSchema = z.object({\n /**\n * Per-command trust decisions for `<workspace>/.book/commands/*.md` files\n * that substitute shell into their prompt. That substitution runs outside the\n * permission system and outside the sandbox, so a repository-controlled file\n * needs a decision before it runs. User-global commands\n * (<BOOK_HOME>/commands) were written by the user and never require one.\n */\n projectCommands: z.record(z.string(), projectCommandChoiceSchema).default({}),\n});\n\nexport type CommandSettings = z.infer<typeof commandSettingsSchema>;\n\n/**\n * Per-directory trust decisions for `additionalDirectories` declared by the checked-in project\n * layer, keyed by the directory's real path.\n *\n * `additionalDirectories` widens the set of roots the file tools serve, so honouring one from a\n * clone would hand that clone a read (and a write path) over whatever the path resolves to. A\n * project directory with no `approved` entry here is withheld, exactly as a project allow rule\n * is. Unlike a rule, the key cannot be the declared text: a repository controls that text and\n * could declare a link, so the decision is recorded against where the path really goes.\n */\nexport const projectDirectoryChoiceSchema = z.enum(['approved', 'rejected']);\n\nexport type ProjectDirectoryChoice = z.infer<typeof projectDirectoryChoiceSchema>;\n\n/**\n * One trust decision about a provider a workspace layer declared: where its requests go and which\n * credential they carry. The fingerprint digests the declared type, origin and `apiKey` reference\n * (see `provider-approvals.ts`), so changing either after the decision withholds the provider\n * again.\n */\nexport const workspaceProviderChoiceSchema = z.object({\n fingerprint: z.string().min(1),\n choice: z.enum(['approved', 'rejected']),\n});\n\nexport type WorkspaceProviderChoice = z.infer<typeof workspaceProviderChoiceSchema>;\n\nexport const uiSettingsSchema = z.object({\n /** Show provider-native and embedded model reasoning in the interactive transcript. */\n showThinking: z.boolean().default(true),\n /** Play the full-screen fire sequence on a newly created interactive startup session. */\n startupAnimation: z.boolean().default(true),\n});\n\nexport type UiSettings = z.infer<typeof uiSettingsSchema>;\n\nexport const skillActivationSchema = z.enum(['auto', 'name-only', 'manual', 'off']);\nexport type SkillActivation = z.infer<typeof skillActivationSchema>;\n\nexport const skillExecutionSchema = z.enum(['inherit', 'ask', 'deny']);\nexport type SkillExecution = z.infer<typeof skillExecutionSchema>;\n\nexport const skillSettingsSchema = z.object({\n /** Emergency switch that removes skill prompt and runtime effects without deleting packages. */\n enabled: z.boolean().default(true),\n /** Per-skill visibility overrides keyed by the skill's declared name. */\n overrides: z.record(z.string(), skillActivationSchema).default({}),\n /** Per-skill activation consent policy. Tool calls still use the normal permission system. */\n execution: z.record(z.string(), skillExecutionSchema).default({}),\n});\n\nexport type SkillSettings = z.infer<typeof skillSettingsSchema>;\n\nexport type ProviderModelConfig = z.infer<typeof providerModelSchema>;\nexport type ProviderConfig = z.infer<typeof providerConfigSchema>;\n\n/** The supported production strategy. */\nexport const compactStrategySchema = z.literal('summary');\nexport type CompactStrategy = z.infer<typeof compactStrategySchema>;\n\n/**\n * Full settings.json schema — all keys that book supports.\n * New keys added in later milestones extend this schema.\n */\nexport const bookSettingsSchema = z.object({\n model: z.string().optional(),\n /** Default strategy used to reduce historical conversation context. */\n compactStrategy: compactStrategySchema.optional(),\n /** Optional model used only to generate historical conversation checkpoints. */\n compactModel: z.string().min(1).optional(),\n /** Reasoning effort for the compaction reducer; defaults to the session effort capped at medium. */\n compactEffort: effortLevelSchema.optional(),\n /**\n * A stronger model the agent may consult through the `Advisor` tool (`/advisor`).\n * Unset, the tool is not offered; {@link ADVISOR_OFF_VALUE} is the explicit \"off\" that a higher\n * layer writes to outrank a lower layer's advisor.\n */\n advisorModel: z.string().min(1).optional(),\n /** Max agent turns per user message. Omit for unlimited. */\n maxTurns: unboundedInt().min(1).optional(),\n maxTokens: unboundedInt().min(1000).optional(),\n effort: effortLevelSchema.optional(),\n /** TUI color theme: rubric (default), folio, apple, or a custom theme filename. */\n theme: z.string().min(1).optional(),\n /**\n * The shell the Bash tool spawns: `bash`, `pwsh`, `powershell`, `cmd`, `sh`,\n * or an executable path. Honoured from the user-global layer, an explicit\n * `--settings` document, or `BOOK_SHELL` only: a workspace file cannot pick\n * the program every command is handed to.\n */\n shell: z.string().min(1).optional(),\n ui: uiSettingsSchema.prefault({}),\n skills: skillSettingsSchema.prefault({}),\n autoCompactEnabled: z.boolean().optional(),\n /** Permission mode used by each host when no invocation-specific mode is supplied. */\n defaultMode: z\n .enum(['default', 'acceptEdits', 'plan', 'auto', 'dontAsk', 'bypassPermissions'])\n .optional(),\n disableBypassPermissionsMode: z.boolean().optional(),\n /**\n * Extra roots the file tools serve, for paths outside the workspace. An honored entry is a root\n * for reads *and* writes: `Write`, `Edit`, `MultiEdit`, `ApplyPatch` and `NotebookEdit` accept an\n * absolute path inside one and treat the entry as they would the workspace, while a relative path\n * stays anchored to the workspace. A project-declared entry is withheld until `book trust dir`.\n */\n additionalDirectories: z.array(z.string()).default([]),\n /**\n * Decisions about `additionalDirectories` the project layer declared, keyed by the directory's\n * real path (the declared text is only what the repository wrote; the path is what the file\n * tools would serve). A project directory with no `approved` entry here is withheld, exactly\n * as a project allow rule with no entry is.\n */\n projectDirectories: z.record(z.string(), projectDirectoryChoiceSchema).default({}),\n /**\n * Decisions about providers only a workspace layer declares, keyed by provider id (and by\n * `.bookrc.json` for its legacy `baseUrl`). A workspace-declared provider with no matching\n * `approved` entry is withheld: it is absent from `provider`, and nothing it names is resolved.\n */\n workspaceProviders: z.record(z.string(), workspaceProviderChoiceSchema).default({}),\n env: z.record(z.string(), z.string()).default({}),\n provider: z.record(z.string(), providerConfigSchema).default({}),\n permissions: permissionsSchema.prefault({}),\n sandbox: sandboxSchema.prefault({}),\n hooks: hooksSchema.prefault({}),\n retry: retrySettingsSchema.prefault({}),\n memory: memorySettingsSchema.prefault({}),\n continuation: continuationSettingsSchema.prefault({}),\n agents: agentSettingsSchema.prefault({}),\n toolDiscovery: toolDiscoverySettingsSchema.prefault({}),\n toolExecution: toolExecutionSettingsSchema.prefault({}),\n observability: observabilitySettingsSchema.prefault({}),\n mcp: mcpSettingsSchema.prefault({}),\n commands: commandSettingsSchema.prefault({}),\n});\n\nexport type BookSettings = z.infer<typeof bookSettingsSchema>;\n\n/**\n * Resolved settings after layering (all defaults filled).\n */\nexport type ResolvedSettings = Required<\n Omit<\n BookSettings,\n | 'model'\n | 'compactModel'\n | 'compactEffort'\n | 'advisorModel'\n | 'maxTurns'\n | 'maxTokens'\n | 'effort'\n | 'theme'\n | 'shell'\n | 'autoCompactEnabled'\n | 'defaultMode'\n | 'disableBypassPermissionsMode'\n >\n> &\n Pick<\n BookSettings,\n | 'model'\n | 'compactModel'\n | 'compactEffort'\n | 'advisorModel'\n | 'maxTurns'\n | 'maxTokens'\n | 'effort'\n | 'theme'\n | 'shell'\n | 'autoCompactEnabled'\n | 'defaultMode'\n | 'disableBypassPermissionsMode'\n >;\n\n/**\n * Default settings used as the base layer before user/project/local override.\n */\nexport const DEFAULT_SETTINGS: ResolvedSettings = {\n compactStrategy: 'summary',\n permissions: { allow: [], ask: [], deny: [], projectAllowRules: {} },\n sandbox: {\n enabled: false,\n failIfUnavailable: false,\n autoAllowBashIfSandboxed: true,\n excludedCommands: [],\n allowUnsandboxedCommands: true,\n filesystem: { allowWrite: [], denyWrite: [], denyRead: [], allowGitWrites: false },\n network: { allowedDomains: [], deniedDomains: [] },\n },\n hooks: {\n SessionStart: [],\n SessionEnd: [],\n UserPromptSubmit: [],\n PreToolUse: [],\n PostToolUse: [],\n Stop: [],\n PreCompact: [],\n PostCompact: [],\n SubagentStart: [],\n SubagentStop: [],\n Notification: [],\n projectEntries: {},\n },\n additionalDirectories: [],\n projectDirectories: {},\n workspaceProviders: {},\n env: {},\n provider: {},\n ui: { showThinking: true, startupAnimation: true },\n skills: { enabled: true, overrides: {}, execution: {} },\n retry: {\n maxAttempts: 10,\n baseDelayMs: 1000,\n maxDelayMs: 30000,\n totalBudgetMs: 0,\n requestTimeoutMs: 600000,\n streamStallTimeoutMs: 20000,\n thinkingStallTimeoutMs: 900_000,\n firstDeltaStallTimeoutMs: 120_000,\n toolRetries: 1,\n watchdog: false,\n streamReissueAttempts: 3,\n outputCapContinuations: 10,\n },\n continuation: {\n enabled: false,\n maxConsecutive: 50,\n noProgressLimit: 3,\n blockedToolTurnLimit: 3,\n planRefreshTurns: 25,\n maxWallClockMs: 0,\n },\n memory: {\n enabled: true,\n autoSave: true,\n },\n agents: {\n mode: 'adaptive',\n maxConcurrent: 3,\n maxSpawned: 8,\n maxDepth: 1,\n persist: true,\n includeUntrackedInSnapshot: true,\n telemetry: true,\n retentionDays: 30,\n checks: {},\n checkTimeoutMs: 120_000,\n maxWorktrees: 24,\n resumeInterrupted: true,\n minFreeDiskBytes: 2 * 1024 * 1024 * 1024,\n profiles: {},\n ui: { enabled: true },\n routing: { inlineSearchBudget: 3, exploreReminder: true },\n forwardTextEvents: false,\n },\n toolDiscovery: {\n mode: 'auto',\n eagerToolCount: 10,\n schemaTokenBudget: 8000,\n maxLoadedTools: 15,\n searchLimit: 5,\n },\n toolExecution: {\n maxConcurrent: 4,\n },\n observability: {\n toolTelemetry: true,\n toolTelemetryRetentionDays: 30,\n },\n mcp: {\n projectServers: {},\n },\n commands: {\n projectCommands: {},\n },\n};\n","/** Deeply nested value access by dot-separated key path. */\nexport function getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\n/** Deeply nested value setter, creating intermediate objects as needed. */\nexport function setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.');\n let current = obj;\n for (let i = 0; i < parts.length - 1; i++) {\n if (\n !(parts[i] in current) ||\n typeof current[parts[i]] !== 'object' ||\n current[parts[i]] === null\n ) {\n current[parts[i]] = {};\n }\n current = current[parts[i]] as Record<string, unknown>;\n }\n current[parts[parts.length - 1]] = value;\n}\n\n/** Delete a dot-path and prune only empty ancestor objects on that path. */\nexport function deleteNestedValue(\n obj: Record<string, unknown>,\n path: string | readonly string[],\n): boolean {\n const parts = typeof path === 'string' ? path.split('.') : [...path];\n const ancestors: Array<{ parent: Record<string, unknown>; key: string }> = [];\n let current = obj;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const key = parts[i];\n if (!Object.prototype.hasOwnProperty.call(current, key)) return false;\n const next = current[key];\n if (typeof next !== 'object' || next === null || Array.isArray(next)) return false;\n ancestors.push({ parent: current, key });\n current = next as Record<string, unknown>;\n }\n\n const leaf = parts[parts.length - 1];\n if (!Object.prototype.hasOwnProperty.call(current, leaf)) return false;\n delete current[leaf];\n\n for (let i = ancestors.length - 1; i >= 0; i--) {\n const { parent, key } = ancestors[i];\n const child = parent[key];\n if (\n typeof child === 'object' &&\n child !== null &&\n !Array.isArray(child) &&\n Object.keys(child).length === 0\n ) {\n delete parent[key];\n } else {\n break;\n }\n }\n return true;\n}\n\n/**\n * Parse a numeric CLI flag, refusing anything that is not a usable number.\n *\n * `parseInt('none', 10)` is `NaN`, and the old guard was truthiness — `'none'` is\n * truthy, so the typo sailed through. Downstream, `NaN` loses every comparison it\n * takes part in, which is the worst possible failure for a limit: `--max-turns`\n * ran zero turns and reported `completed`, and `--max-budget-usd` read as\n * *configured* while permitting unbounded spend. Reject it at the boundary, and\n * test presence with `!== undefined` so an explicit `0` is a real zero rather than\n * a missing flag.\n */\nexport function parseNumericFlag(\n raw: unknown,\n flag: string,\n opts: { integer?: boolean; min?: number } = {},\n): number | undefined {\n if (raw === undefined || raw === null || raw === '') return undefined;\n const text = String(raw).trim();\n const value = opts.integer ? Number.parseInt(text, 10) : Number.parseFloat(text);\n // Number.parse* stops at the first bad character, so '12abc' would otherwise\n // silently become 12.\n if (!Number.isFinite(value) || !/^[+-]?(\\d+\\.?\\d*|\\.\\d+)$/.test(text)) {\n throw new Error(`${flag} expects a number, but received \"${text}\".`);\n }\n // `Number.parseInt('2.5', 10)` is 2, so testing the parsed value would never\n // catch a fractional input for an integer flag - it would silently truncate.\n if (opts.integer && text.includes('.')) {\n throw new Error(`${flag} expects a whole number, but received \"${text}\".`);\n }\n const min = opts.min ?? 0;\n if (value < min) {\n throw new Error(`${flag} must be at least ${min}, but received \"${text}\".`);\n }\n return value;\n}\n\n/**\n * Merge the root positional argument into `-p/--print [prompt]`.\n *\n * `--print` carries its prompt as its own optional value, so a prompt written\n * after a later flag (`book -p --model m \"fix it\"`) is a stray positional that\n * commander rejected with \"too many arguments\" (#226). Claude Code accepts the\n * prompt in either place, and so does Book now: the positional is the prompt\n * when the flag has no value of its own. Both at once is an error rather than a\n * silent choice, and a positional without `--print` is an error too, because the\n * TUI has nowhere to put it — and it is the shape a mistyped subcommand takes.\n *\n * Returns the resolved `--print` value, or `{ error }` for commander to report.\n */\nexport function resolvePrintPrompt(\n print: string | boolean | undefined,\n positional: string | undefined,\n): { print: string | boolean | undefined } | { error: string } {\n if (positional === undefined) return { print };\n if (print === undefined) {\n return {\n error: `unexpected argument '${positional}'. A prompt on the command line needs -p/--print (book -p \"${positional}\"); run book --help for the subcommands.`,\n };\n }\n if (typeof print === 'string') {\n return {\n error: `the prompt was given twice: --print '${print}' and the argument '${positional}'. Pass it once, either as the --print value or as the argument.`,\n };\n }\n return { print: positional };\n}\n","import {\n closeSync,\n existsSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n statSync,\n unlinkSync,\n writeSync,\n} from 'fs';\nimport { basename, dirname, join } from 'path';\nimport type { core } from 'zod';\nimport { deleteNestedValue, setNestedValue } from './cli/utils.js';\nimport { bookSettingsSchema } from './settings.js';\nimport { redactSettingValue } from './settings-redaction.js';\nimport { projectFileWriteRefusal } from './workspace-files.js';\n\nexport type SettingsDocumentReadResult =\n | { status: 'absent'; path: string }\n | { status: 'valid'; path: string; document: Record<string, unknown> }\n | { status: 'malformed'; path: string; error: string }\n | { status: 'non-object'; path: string; error: string };\n\nexport interface SettingsDiagnostic {\n path: string;\n issuePath?: string;\n message: string;\n}\n\nexport type SettingsMutationResult =\n | {\n ok: true;\n path: string;\n document: Record<string, unknown>;\n values: Record<string, unknown>;\n changed: boolean;\n }\n | { ok: false; path: string; diagnostics: SettingsDiagnostic[] };\n\nexport interface SettingsRepositoryOptions {\n writeAtomic?: (path: string, contents: string) => void;\n}\n\nexport const SETTINGS_TOP_LEVEL_KEYS = Object.freeze(Object.keys(bookSettingsSchema.shape).sort());\n\nexport function formatSettingsKeyHelp(heading = 'Supported top-level settings:'): string {\n return `${heading}\\n${SETTINGS_TOP_LEVEL_KEYS.map((key) => ` ${key}`).join('\\n')}`;\n}\n\nfunction issueDiagnostic(path: string, issue: core.$ZodIssue): SettingsDiagnostic {\n return {\n path,\n issuePath: issue.path.join('.'),\n message: issue.message,\n };\n}\n\nfunction safeMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function readSettingsDocument(path: string): SettingsDocumentReadResult {\n if (!existsSync(path)) return { status: 'absent', path };\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, 'utf-8'));\n } catch (error) {\n return { status: 'malformed', path, error: safeMessage(error) };\n }\n\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return { status: 'non-object', path, error: 'Settings must contain a JSON object.' };\n }\n\n return { status: 'valid', path, document: parsed as Record<string, unknown> };\n}\n\nexport function writeFileAtomic(path: string, contents: string): void {\n const directory = dirname(path);\n mkdirSync(directory, { recursive: true });\n const temporaryPath = join(\n directory,\n `.${basename(path)}.${process.pid}.${crypto.randomUUID()}.tmp`,\n );\n let descriptor: number | undefined;\n\n try {\n descriptor = openSync(temporaryPath, 'wx', 0o600);\n writeSync(descriptor, contents, undefined, 'utf-8');\n fsyncSync(descriptor);\n closeSync(descriptor);\n descriptor = undefined;\n renameSync(temporaryPath, path);\n } catch (error) {\n if (descriptor !== undefined) closeSync(descriptor);\n try {\n unlinkSync(temporaryPath);\n } catch {\n // The temporary file may not have been created.\n }\n throw error;\n }\n}\n\nfunction validateTopLevelKeys(\n path: string,\n document: Record<string, unknown>,\n): SettingsDiagnostic[] {\n const supported = new Set(SETTINGS_TOP_LEVEL_KEYS);\n return Object.keys(document)\n .filter((key) => !supported.has(key))\n .map((key) => ({\n path,\n issuePath: key,\n message: `Unknown top-level setting. Supported keys: ${SETTINGS_TOP_LEVEL_KEYS.join(', ')}`,\n }));\n}\n\nexport class SettingsRepository {\n private readonly writeAtomic: (path: string, contents: string) => void;\n\n constructor(\n readonly path: string,\n options: SettingsRepositoryOptions = {},\n ) {\n this.writeAtomic = options.writeAtomic ?? writeFileAtomic;\n }\n\n read(): SettingsDocumentReadResult {\n return readSettingsDocument(this.path);\n }\n\n set(values: Record<string, unknown>): SettingsMutationResult {\n return this.mutate((candidate) => {\n for (const [key, value] of Object.entries(values)) setNestedValue(candidate, key, value);\n }, values);\n }\n\n remove(paths: string[]): SettingsMutationResult {\n return this.mutate((candidate) => {\n for (const path of paths) deleteNestedValue(candidate, path.split('.'));\n }, {});\n }\n\n update(\n mutation: (candidate: Record<string, unknown>) => void,\n displayValues: Record<string, unknown> = {},\n ): SettingsMutationResult {\n return this.mutate(mutation, displayValues);\n }\n\n private mutate(\n mutation: (candidate: Record<string, unknown>) => void,\n values: Record<string, unknown>,\n ): SettingsMutationResult {\n const refusal = projectFileWriteRefusal(this.path);\n if (refusal) {\n return { ok: false, path: this.path, diagnostics: [{ path: this.path, message: refusal }] };\n }\n let lock: number | undefined;\n try {\n lock = this.acquireLock();\n return this.mutateLocked(mutation, values);\n } catch (error) {\n return {\n ok: false,\n path: this.path,\n diagnostics: [{ path: this.path, message: safeMessage(error) }],\n };\n } finally {\n if (lock !== undefined) this.releaseLock(lock);\n }\n }\n\n private acquireLock(): number {\n mkdirSync(dirname(this.path), { recursive: true });\n const lockPath = `${this.path}.lock`;\n for (let attempt = 0; attempt < 50; attempt++) {\n try {\n return openSync(lockPath, 'wx', 0o600);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n try {\n if (Date.now() - statSync(lockPath).mtimeMs > 10_000) unlinkSync(lockPath);\n } catch {\n // Another process may have released or replaced the lock.\n }\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);\n }\n }\n throw new Error(`Timed out waiting for settings lock: ${lockPath}`);\n }\n\n private releaseLock(descriptor: number): void {\n closeSync(descriptor);\n try {\n unlinkSync(`${this.path}.lock`);\n } catch {\n // A stale-lock recovery may already have removed it.\n }\n }\n\n private mutateLocked(\n mutation: (candidate: Record<string, unknown>) => void,\n values: Record<string, unknown>,\n ): SettingsMutationResult {\n const source = this.read();\n if (source.status === 'malformed' || source.status === 'non-object') {\n return {\n ok: false,\n path: this.path,\n diagnostics: [{ path: this.path, message: source.error }],\n };\n }\n\n const candidate = structuredClone(source.status === 'valid' ? source.document : {});\n try {\n mutation(candidate);\n } catch (error) {\n return {\n ok: false,\n path: this.path,\n diagnostics: [{ path: this.path, message: safeMessage(error) }],\n };\n }\n\n const keyDiagnostics = validateTopLevelKeys(this.path, candidate);\n const validation = bookSettingsSchema.safeParse(candidate);\n const diagnostics = [\n ...keyDiagnostics,\n ...(validation.success\n ? []\n : validation.error.issues.map((issue) => issueDiagnostic(this.path, issue))),\n ];\n if (diagnostics.length > 0) return { ok: false, path: this.path, diagnostics };\n\n try {\n const changed =\n source.status !== 'valid' || JSON.stringify(source.document) !== JSON.stringify(candidate);\n if (changed) this.writeAtomic(this.path, JSON.stringify(candidate, null, 2) + '\\n');\n return {\n ok: true,\n path: this.path,\n document: candidate,\n changed,\n values: Object.fromEntries(\n Object.entries(values).map(([key, value]) => [key, redactSettingValue(key, value)]),\n ),\n };\n } catch (error) {\n return {\n ok: false,\n path: this.path,\n diagnostics: [{ path: this.path, message: safeMessage(error) }],\n };\n }\n }\n}\n\nexport function formatSettingsDiagnostics(diagnostics: SettingsDiagnostic[]): string {\n return diagnostics\n .map((diagnostic) => {\n const location = diagnostic.issuePath\n ? `${diagnostic.path} (${diagnostic.issuePath})`\n : diagnostic.path;\n return `${location}: ${diagnostic.message}`;\n })\n .join('\\n');\n}\n","const REDACTED_SECRET = '*** (stored)';\n\nfunction redactProviderRecord(value: unknown): unknown {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return value;\n const clone = structuredClone(value) as Record<string, unknown>;\n if ('apiKey' in clone) clone.apiKey = REDACTED_SECRET;\n return clone;\n}\n\nfunction redactProviderMap(value: unknown): unknown {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return value;\n const clone = structuredClone(value) as Record<string, unknown>;\n for (const [id, provider] of Object.entries(clone)) clone[id] = redactProviderRecord(provider);\n return clone;\n}\n\n/**\n * Removed blocks that may still carry a credential in a real settings file.\n *\n * Subscription authentication is gone, but `auth.profiles.<id>.clientSecret`\n * and the free-form `headers` that rode along with the bearer token are still\n * on disk wherever it was configured — and `book config list` prints the *raw*\n * document, not the validated one, so those keys survive into terminal\n * scrollback and pasted bug reports. Validation discarding a block is not the\n * same as a display path being safe, so the whole subtree is masked rather than\n * its individual leaves: nothing reads it any more, so there is nothing in it\n * worth showing.\n */\nconst REMOVED_SECRET_BEARING_BLOCKS = ['auth'];\n\nexport function redactSettingValue(path: string, value: unknown): unknown {\n const parts = path.split('.');\n if (parts[0] && REMOVED_SECRET_BEARING_BLOCKS.includes(parts[0])) return REDACTED_SECRET;\n if (parts[0] !== 'provider') return value;\n if (parts.length === 1) return redactProviderMap(value);\n if (parts.length === 2) return redactProviderRecord(value);\n return parts[parts.length - 1] === 'apiKey' ? REDACTED_SECRET : value;\n}\n\nexport function redactSettingsForDisplay<T>(value: T): T {\n if (!value || typeof value !== 'object') return value;\n const clone = structuredClone(value) as Record<string, unknown>;\n clone.provider = redactProviderMap(clone.provider);\n for (const key of REMOVED_SECRET_BEARING_BLOCKS) {\n if (clone[key] !== undefined) clone[key] = REDACTED_SECRET;\n }\n return clone as T;\n}\n"],"mappings":";;;;;AAAA,SAAS,SAAS;AAMX,IAAM,uBAAuB,EAAE,OAAO,EAAE,IAAI,CAAC;AAe7C,SAAS,eAAe;AAC7B,SAAO,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ;AAC/B,QAAI,CAAC,OAAO,UAAU,IAAI,KAAK,GAAG;AAChC,UAAI,OAAO,KAAK,EAAE,MAAM,gBAAgB,UAAU,OAAO,OAAO,IAAI,MAAM,CAAC;AAAA,IAC7E;AAAA,EACF,CAAC;AACH;AAKO,IAAM,gBAAgB,EAAE,OAAO;AAAA,EACpC,SAAS,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EAClC,mBAAmB,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EAC5C,0BAA0B,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EAClD,kBAAkB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EAChD,0BAA0B,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EAClD,YAAY,EACT,OAAO;AAAA,IACN,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,IAC1C,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,IACzC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAaxC,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EAC3C,CAAC,EACA,SAAS,CAAC,CAAC;AAAA,EACd,SAAS,EACN,OAAO;AAAA,IACN,gBAAgB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,IAC9C,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC/C,CAAC,EACA,SAAS,CAAC,CAAC;AAChB,CAAC;AAcM,IAAM,+BAA+B,EAAE,KAAK,CAAC,YAAY,UAAU,CAAC;AAIpE,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,OAAO,EAAE,MAAM,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC/C,KAAK,EAAE,MAAM,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC7C,MAAM,EAAE,MAAM,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAK9C,mBAAmB,EAAE,OAAO,EAAE,OAAO,GAAG,4BAA4B,EAAE,QAAQ,CAAC,CAAC;AAClF,CAAC;AAMM,IAAM,kBAAkB,EAAE,OAAO;AAAA;AAAA,EAEtC,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAE7B,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA;AAAA,EAEzB,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC;AAYM,IAAM,0BAA0B,EAAE,KAAK,CAAC,YAAY,UAAU,CAAC;AAK/D,IAAM,cAAc;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA;AACF;AAOO,IAAM,cAAc,EAAE,OAAO;AAAA,EAClC,GAAI,OAAO,YAAY,YAAY,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQxF,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,uBAAuB,EAAE,QAAQ,CAAC,CAAC;AAC1E,CAAC;AAOM,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE;AAAA,EACvD,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,IAAI,GAAK,EAAE,QAAQ,GAAI;AAAA,EAC9D,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,IAAI,GAAM,EAAE,QAAQ,GAAK;AAAA,EAC/D,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAM,EAAE,QAAQ,CAAC;AAAA,EAC5D,kBAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAI,EAAE,IAAI,GAAM,EAAE,QAAQ,GAAM;AAAA,EACvE,sBAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAI,EAAE,IAAI,IAAM,EAAE,QAAQ,GAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU1E,wBAAwB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAM,EAAE,IAAI,IAAS,EAAE,QAAQ,GAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYnF,0BAA0B,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAK,EAAE,IAAI,IAAS,EAAE,QAAQ,IAAO;AAAA,EACpF,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EACrD,UAAU,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAanC,uBAAuB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMhE,wBAAwB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE;AACpE,CAAC;AAIM,IAAM,oBAAoB,EAAE,KAAK,CAAC,OAAO,UAAU,QAAQ,SAAS,KAAK,CAAC;AAE1E,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,eAAe,aAAa,EAAE,SAAS,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMlD,uBAAuB,aAAa,EAAE,SAAS,EAAE,SAAS;AAAA,EAC1D,iBAAiB,aAAa,EAAE,SAAS,EAAE,SAAS;AAAA;AAAA,EAEpD,QAAQ,EAAE,QAAQ,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,QAAQ,EAAE,QAAQ,EAAE,SAAS;AAAA;AAAA,EAE7B,YAAY,EAAE,KAAK,CAAC,SAAS,WAAW,OAAO,CAAC,EAAE,SAAS;AAAA,EAC3D,QAAQ,EACL,MAAM;AAAA,IACL,EAAE,QAAQ,KAAK;AAAA,IACf,EAAE,OAAO;AAAA,MACP,SAAS,kBAAkB,SAAS;AAAA,MACpC,QAAQ,EAAE,MAAM,iBAAiB,EAAE,SAAS;AAAA,IAC9C,CAAC;AAAA,EACH,CAAC,EACA,SAAS;AACd,CAAC;AAOM,IAAM,oBAAoB;AAE1B,IAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,MAAM,EAAE,KAAK,CAAC,UAAU,WAAW,CAAC,EAAE,QAAQ,QAAQ;AAAA,EACtD,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACpC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACpC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACnC,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB,EAAE,QAAQ,CAAC,CAAC;AAC9D,CAAC;AAcM,IAAM,6BAA6B,EAAE,OAAO;AAAA,EACjD,SAAS,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA,EAElC,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI,EAAE,QAAQ,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM5D,iBAAiB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAe1D,sBAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,CAAC;AAAA;AAAA,EAEhE,kBAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA;AAAA,EAE7D,gBAAgB,aAAa,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AACjD,CAAC;AAIM,IAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,SAAS,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA,EAEjC,UAAU,EAAE,QAAQ,EAAE,QAAQ,IAAI;AACpC,CAAC;AAEM,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,MAAM,EAAE,KAAK,CAAC,YAAY,UAAU,KAAK,CAAC,EAAE,QAAQ,UAAU;AAAA,EAC9D,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC;AAAA,EACxD,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC;AAAA,EACrD,UAAU,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC;AAAA,EAChC,SAAS,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EACjC,4BAA4B,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EACpD,WAAW,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EACnC,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,QAAQ,EAAE;AAAA,EAC3D,QAAQ,EACL,OAAO,EAAE,OAAO,GAAG,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAClF,QAAQ,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOb,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAK,EAAE,IAAI,IAAS,EAAE,QAAQ,IAAO;AAAA;AAAA,EAE1E,eAAe,aAAa,EAAE,IAAI,GAAI,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASjD,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWzD,mBAAmB,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM3C,kBAAkB,aAAa,EAC5B,IAAI,CAAC,EACL,QAAQ,IAAI,OAAO,OAAO,IAAI;AAAA,EACjC,UAAU,EACP;AAAA,IACC,EAAE,OAAO;AAAA,IACT,EAAE,OAAO;AAAA,MACP,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,MAClC,QAAQ,kBAAkB,SAAS;AAAA,MACnC,UAAU,aAAa,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,MACzC,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,IAC7B,CAAC;AAAA,EACH,EACC,QAAQ,CAAC,CAAC;AAAA,EACb,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;AAAA,EAChE,SAAS,EACN,OAAO;AAAA,IACN,oBAAoB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC;AAAA,IAC7D,iBAAiB,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EAC3C,CAAC,EACA,SAAS,CAAC,CAAC;AAAA,EACd,mBAAmB,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAC9C,CAAC;AAEM,IAAM,8BAA8B,EAAE,OAAO;AAAA,EAClD,MAAM,EAAE,KAAK,CAAC,QAAQ,SAAS,UAAU,CAAC,EAAE,QAAQ,MAAM;AAAA,EAC1D,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EAC3D,mBAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAI,EAAE,IAAI,GAAM,EAAE,QAAQ,GAAI;AAAA,EACtE,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EAC3D,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AACvD,CAAC;AAIM,IAAM,8BAA8B,EAAE,OAAO;AAAA;AAAA,EAElD,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AACzD,CAAC;AAIM,IAAM,8BAA8B,EAAE,OAAO;AAAA;AAAA,EAElD,eAAe,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvC,4BAA4B,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,QAAQ,EAAE;AAC1E,CAAC;AAIM,IAAM,+BAA+B,EAAE,OAAO;AAAA;AAAA,EAEnD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,QAAQ,EAAE,KAAK,CAAC,YAAY,UAAU,CAAC;AACzC,CAAC;AAIM,IAAM,oBAAoB,EAAE,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxC,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,4BAA4B,EAAE,QAAQ,CAAC,CAAC;AAC/E,CAAC;AAIM,IAAM,6BAA6B,EAAE,OAAO;AAAA;AAAA,EAEjD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,QAAQ,EAAE,KAAK,CAAC,YAAY,UAAU,CAAC;AACzC,CAAC;AAIM,IAAM,wBAAwB,EAAE,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5C,iBAAiB,EAAE,OAAO,EAAE,OAAO,GAAG,0BAA0B,EAAE,QAAQ,CAAC,CAAC;AAC9E,CAAC;AAcM,IAAM,+BAA+B,EAAE,KAAK,CAAC,YAAY,UAAU,CAAC;AAUpE,IAAM,gCAAgC,EAAE,OAAO;AAAA,EACpD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,QAAQ,EAAE,KAAK,CAAC,YAAY,UAAU,CAAC;AACzC,CAAC;AAIM,IAAM,mBAAmB,EAAE,OAAO;AAAA;AAAA,EAEvC,cAAc,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA,EAEtC,kBAAkB,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAC5C,CAAC;AAIM,IAAM,wBAAwB,EAAE,KAAK,CAAC,QAAQ,aAAa,UAAU,KAAK,CAAC;AAG3E,IAAM,uBAAuB,EAAE,KAAK,CAAC,WAAW,OAAO,MAAM,CAAC;AAG9D,IAAM,sBAAsB,EAAE,OAAO;AAAA;AAAA,EAE1C,SAAS,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA,EAEjC,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,qBAAqB,EAAE,QAAQ,CAAC,CAAC;AAAA;AAAA,EAEjE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAClE,CAAC;AAQM,IAAM,wBAAwB,EAAE,QAAQ,SAAS;AAOjD,IAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAE3B,iBAAiB,sBAAsB,SAAS;AAAA;AAAA,EAEhD,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA;AAAA,EAEzC,eAAe,kBAAkB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM1C,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA;AAAA,EAEzC,UAAU,aAAa,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACzC,WAAW,aAAa,EAAE,IAAI,GAAI,EAAE,SAAS;AAAA,EAC7C,QAAQ,kBAAkB,SAAS;AAAA;AAAA,EAEnC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAClC,IAAI,iBAAiB,SAAS,CAAC,CAAC;AAAA,EAChC,QAAQ,oBAAoB,SAAS,CAAC,CAAC;AAAA,EACvC,oBAAoB,EAAE,QAAQ,EAAE,SAAS;AAAA;AAAA,EAEzC,aAAa,EACV,KAAK,CAAC,WAAW,eAAe,QAAQ,QAAQ,WAAW,mBAAmB,CAAC,EAC/E,SAAS;AAAA,EACZ,8BAA8B,EAAE,QAAQ,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOnD,uBAAuB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrD,oBAAoB,EAAE,OAAO,EAAE,OAAO,GAAG,4BAA4B,EAAE,QAAQ,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjF,oBAAoB,EAAE,OAAO,EAAE,OAAO,GAAG,6BAA6B,EAAE,QAAQ,CAAC,CAAC;AAAA,EAClF,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EAChD,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC/D,aAAa,kBAAkB,SAAS,CAAC,CAAC;AAAA,EAC1C,SAAS,cAAc,SAAS,CAAC,CAAC;AAAA,EAClC,OAAO,YAAY,SAAS,CAAC,CAAC;AAAA,EAC9B,OAAO,oBAAoB,SAAS,CAAC,CAAC;AAAA,EACtC,QAAQ,qBAAqB,SAAS,CAAC,CAAC;AAAA,EACxC,cAAc,2BAA2B,SAAS,CAAC,CAAC;AAAA,EACpD,QAAQ,oBAAoB,SAAS,CAAC,CAAC;AAAA,EACvC,eAAe,4BAA4B,SAAS,CAAC,CAAC;AAAA,EACtD,eAAe,4BAA4B,SAAS,CAAC,CAAC;AAAA,EACtD,eAAe,4BAA4B,SAAS,CAAC,CAAC;AAAA,EACtD,KAAK,kBAAkB,SAAS,CAAC,CAAC;AAAA,EAClC,UAAU,sBAAsB,SAAS,CAAC,CAAC;AAC7C,CAAC;AA2CM,IAAM,mBAAqC;AAAA,EAChD,iBAAiB;AAAA,EACjB,aAAa,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,GAAG,MAAM,CAAC,GAAG,mBAAmB,CAAC,EAAE;AAAA,EACnE,SAAS;AAAA,IACP,SAAS;AAAA,IACT,mBAAmB;AAAA,IACnB,0BAA0B;AAAA,IAC1B,kBAAkB,CAAC;AAAA,IACnB,0BAA0B;AAAA,IAC1B,YAAY,EAAE,YAAY,CAAC,GAAG,WAAW,CAAC,GAAG,UAAU,CAAC,GAAG,gBAAgB,MAAM;AAAA,IACjF,SAAS,EAAE,gBAAgB,CAAC,GAAG,eAAe,CAAC,EAAE;AAAA,EACnD;AAAA,EACA,OAAO;AAAA,IACL,cAAc,CAAC;AAAA,IACf,YAAY,CAAC;AAAA,IACb,kBAAkB,CAAC;AAAA,IACnB,YAAY,CAAC;AAAA,IACb,aAAa,CAAC;AAAA,IACd,MAAM,CAAC;AAAA,IACP,YAAY,CAAC;AAAA,IACb,aAAa,CAAC;AAAA,IACd,eAAe,CAAC;AAAA,IAChB,cAAc,CAAC;AAAA,IACf,cAAc,CAAC;AAAA,IACf,gBAAgB,CAAC;AAAA,EACnB;AAAA,EACA,uBAAuB,CAAC;AAAA,EACxB,oBAAoB,CAAC;AAAA,EACrB,oBAAoB,CAAC;AAAA,EACrB,KAAK,CAAC;AAAA,EACN,UAAU,CAAC;AAAA,EACX,IAAI,EAAE,cAAc,MAAM,kBAAkB,KAAK;AAAA,EACjD,QAAQ,EAAE,SAAS,MAAM,WAAW,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,EACtD,OAAO;AAAA,IACL,aAAa;AAAA,IACb,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,sBAAsB;AAAA,IACtB,wBAAwB;AAAA,IACxB,0BAA0B;AAAA,IAC1B,aAAa;AAAA,IACb,UAAU;AAAA,IACV,uBAAuB;AAAA,IACvB,wBAAwB;AAAA,EAC1B;AAAA,EACA,cAAc;AAAA,IACZ,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,sBAAsB;AAAA,IACtB,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,EAClB;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,SAAS;AAAA,IACT,4BAA4B;AAAA,IAC5B,WAAW;AAAA,IACX,eAAe;AAAA,IACf,QAAQ,CAAC;AAAA,IACT,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,kBAAkB,IAAI,OAAO,OAAO;AAAA,IACpC,UAAU,CAAC;AAAA,IACX,IAAI,EAAE,SAAS,KAAK;AAAA,IACpB,SAAS,EAAE,oBAAoB,GAAG,iBAAiB,KAAK;AAAA,IACxD,mBAAmB;AAAA,EACrB;AAAA,EACA,eAAe;AAAA,IACb,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,eAAe;AAAA,IACb,eAAe;AAAA,EACjB;AAAA,EACA,eAAe;AAAA,IACb,eAAe;AAAA,IACf,4BAA4B;AAAA,EAC9B;AAAA,EACA,KAAK;AAAA,IACH,gBAAgB,CAAC;AAAA,EACnB;AAAA,EACA,UAAU;AAAA,IACR,iBAAiB,CAAC;AAAA,EACpB;AACF;;;AC3tBO,SAAS,eAAe,KAA8B,MAAuB;AAClF,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,UAAmB;AACvB,aAAW,QAAQ,OAAO;AACxB,QAAI,YAAY,QAAQ,YAAY,UAAa,OAAO,YAAY,SAAU,QAAO;AACrF,cAAW,QAAoC,IAAI;AAAA,EACrD;AACA,SAAO;AACT;AAGO,SAAS,eAAe,KAA8B,MAAc,OAAsB;AAC/F,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,UAAU;AACd,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AACzC,QACE,EAAE,MAAM,CAAC,KAAK,YACd,OAAO,QAAQ,MAAM,CAAC,CAAC,MAAM,YAC7B,QAAQ,MAAM,CAAC,CAAC,MAAM,MACtB;AACA,cAAQ,MAAM,CAAC,CAAC,IAAI,CAAC;AAAA,IACvB;AACA,cAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC5B;AACA,UAAQ,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;AACrC;AAGO,SAAS,kBACd,KACA,MACS;AACT,QAAM,QAAQ,OAAO,SAAS,WAAW,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI;AACnE,QAAM,YAAqE,CAAC;AAC5E,MAAI,UAAU;AAEd,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AACzC,UAAM,MAAM,MAAM,CAAC;AACnB,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,SAAS,GAAG,EAAG,QAAO;AAChE,UAAM,OAAO,QAAQ,GAAG;AACxB,QAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,QAAQ,IAAI,EAAG,QAAO;AAC7E,cAAU,KAAK,EAAE,QAAQ,SAAS,IAAI,CAAC;AACvC,cAAU;AAAA,EACZ;AAEA,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,MAAI,CAAC,OAAO,UAAU,eAAe,KAAK,SAAS,IAAI,EAAG,QAAO;AACjE,SAAO,QAAQ,IAAI;AAEnB,WAAS,IAAI,UAAU,SAAS,GAAG,KAAK,GAAG,KAAK;AAC9C,UAAM,EAAE,QAAQ,IAAI,IAAI,UAAU,CAAC;AACnC,UAAM,QAAQ,OAAO,GAAG;AACxB,QACE,OAAO,UAAU,YACjB,UAAU,QACV,CAAC,MAAM,QAAQ,KAAK,KACpB,OAAO,KAAK,KAAK,EAAE,WAAW,GAC9B;AACA,aAAO,OAAO,GAAG;AAAA,IACnB,OAAO;AACL;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAaO,SAAS,iBACd,KACA,MACA,OAA4C,CAAC,GACzB;AACpB,MAAI,QAAQ,UAAa,QAAQ,QAAQ,QAAQ,GAAI,QAAO;AAC5D,QAAM,OAAO,OAAO,GAAG,EAAE,KAAK;AAC9B,QAAM,QAAQ,KAAK,UAAU,OAAO,SAAS,MAAM,EAAE,IAAI,OAAO,WAAW,IAAI;AAG/E,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,CAAC,2BAA2B,KAAK,IAAI,GAAG;AACrE,UAAM,IAAI,MAAM,GAAG,IAAI,oCAAoC,IAAI,IAAI;AAAA,EACrE;AAGA,MAAI,KAAK,WAAW,KAAK,SAAS,GAAG,GAAG;AACtC,UAAM,IAAI,MAAM,GAAG,IAAI,0CAA0C,IAAI,IAAI;AAAA,EAC3E;AACA,QAAM,MAAM,KAAK,OAAO;AACxB,MAAI,QAAQ,KAAK;AACf,UAAM,IAAI,MAAM,GAAG,IAAI,qBAAqB,GAAG,mBAAmB,IAAI,IAAI;AAAA,EAC5E;AACA,SAAO;AACT;AAeO,SAAS,mBACd,OACA,YAC6D;AAC7D,MAAI,eAAe,OAAW,QAAO,EAAE,MAAM;AAC7C,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,MACL,OAAO,wBAAwB,UAAU,8DAA8D,UAAU;AAAA,IACnH;AAAA,EACF;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,MACL,OAAO,wCAAwC,KAAK,uBAAuB,UAAU;AAAA,IACvF;AAAA,EACF;AACA,SAAO,EAAE,OAAO,WAAW;AAC7B;;;ACpIA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,UAAU,SAAS,YAAY;;;ACZxC,IAAM,kBAAkB;AAExB,SAAS,qBAAqB,OAAyB;AACrD,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,QAAM,QAAQ,gBAAgB,KAAK;AACnC,MAAI,YAAY,MAAO,OAAM,SAAS;AACtC,SAAO;AACT;AAEA,SAAS,kBAAkB,OAAyB;AAClD,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,QAAM,QAAQ,gBAAgB,KAAK;AACnC,aAAW,CAAC,IAAI,QAAQ,KAAK,OAAO,QAAQ,KAAK,EAAG,OAAM,EAAE,IAAI,qBAAqB,QAAQ;AAC7F,SAAO;AACT;AAcA,IAAM,gCAAgC,CAAC,MAAM;AAEtC,SAAS,mBAAmB,MAAc,OAAyB;AACxE,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,MAAM,CAAC,KAAK,8BAA8B,SAAS,MAAM,CAAC,CAAC,EAAG,QAAO;AACzE,MAAI,MAAM,CAAC,MAAM,WAAY,QAAO;AACpC,MAAI,MAAM,WAAW,EAAG,QAAO,kBAAkB,KAAK;AACtD,MAAI,MAAM,WAAW,EAAG,QAAO,qBAAqB,KAAK;AACzD,SAAO,MAAM,MAAM,SAAS,CAAC,MAAM,WAAW,kBAAkB;AAClE;AAEO,SAAS,yBAA4B,OAAa;AACvD,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,QAAQ,gBAAgB,KAAK;AACnC,QAAM,WAAW,kBAAkB,MAAM,QAAQ;AACjD,aAAW,OAAO,+BAA+B;AAC/C,QAAI,MAAM,GAAG,MAAM,OAAW,OAAM,GAAG,IAAI;AAAA,EAC7C;AACA,SAAO;AACT;;;ADFO,IAAM,0BAA0B,OAAO,OAAO,OAAO,KAAK,mBAAmB,KAAK,EAAE,KAAK,CAAC;AAE1F,SAAS,sBAAsB,UAAU,iCAAyC;AACvF,SAAO,GAAG,OAAO;AAAA,EAAK,wBAAwB,IAAI,CAAC,QAAQ,KAAK,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC;AACnF;AAEA,SAAS,gBAAgB,MAAc,OAA2C;AAChF,SAAO;AAAA,IACL;AAAA,IACA,WAAW,MAAM,KAAK,KAAK,GAAG;AAAA,IAC9B,SAAS,MAAM;AAAA,EACjB;AACF;AAEA,SAAS,YAAY,OAAwB;AAC3C,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEO,SAAS,qBAAqB,MAA0C;AAC7E,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO,EAAE,QAAQ,UAAU,KAAK;AAEvD,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;AAAA,EACjD,SAAS,OAAO;AACd,WAAO,EAAE,QAAQ,aAAa,MAAM,OAAO,YAAY,KAAK,EAAE;AAAA,EAChE;AAEA,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E,WAAO,EAAE,QAAQ,cAAc,MAAM,OAAO,uCAAuC;AAAA,EACrF;AAEA,SAAO,EAAE,QAAQ,SAAS,MAAM,UAAU,OAAkC;AAC9E;AAEO,SAAS,gBAAgB,MAAc,UAAwB;AACpE,QAAM,YAAY,QAAQ,IAAI;AAC9B,YAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACxC,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA,IAAI,SAAS,IAAI,CAAC,IAAI,QAAQ,GAAG,IAAI,OAAO,WAAW,CAAC;AAAA,EAC1D;AACA,MAAI;AAEJ,MAAI;AACF,iBAAa,SAAS,eAAe,MAAM,GAAK;AAChD,cAAU,YAAY,UAAU,QAAW,OAAO;AAClD,cAAU,UAAU;AACpB,cAAU,UAAU;AACpB,iBAAa;AACb,eAAW,eAAe,IAAI;AAAA,EAChC,SAAS,OAAO;AACd,QAAI,eAAe,OAAW,WAAU,UAAU;AAClD,QAAI;AACF,iBAAW,aAAa;AAAA,IAC1B,QAAQ;AAAA,IAER;AACA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,qBACP,MACA,UACsB;AACtB,QAAM,YAAY,IAAI,IAAI,uBAAuB;AACjD,SAAO,OAAO,KAAK,QAAQ,EACxB,OAAO,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,CAAC,EACnC,IAAI,CAAC,SAAS;AAAA,IACb;AAAA,IACA,WAAW;AAAA,IACX,SAAS,8CAA8C,wBAAwB,KAAK,IAAI,CAAC;AAAA,EAC3F,EAAE;AACN;AAEO,IAAM,qBAAN,MAAyB;AAAA,EAG9B,YACW,MACT,UAAqC,CAAC,GACtC;AAFS;AAGT,SAAK,cAAc,QAAQ,eAAe;AAAA,EAC5C;AAAA,EAJW;AAAA,EAHM;AAAA,EASjB,OAAmC;AACjC,WAAO,qBAAqB,KAAK,IAAI;AAAA,EACvC;AAAA,EAEA,IAAI,QAAyD;AAC3D,WAAO,KAAK,OAAO,CAAC,cAAc;AAChC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,EAAG,gBAAe,WAAW,KAAK,KAAK;AAAA,IACzF,GAAG,MAAM;AAAA,EACX;AAAA,EAEA,OAAO,OAAyC;AAC9C,WAAO,KAAK,OAAO,CAAC,cAAc;AAChC,iBAAW,QAAQ,MAAO,mBAAkB,WAAW,KAAK,MAAM,GAAG,CAAC;AAAA,IACxE,GAAG,CAAC,CAAC;AAAA,EACP;AAAA,EAEA,OACE,UACA,gBAAyC,CAAC,GAClB;AACxB,WAAO,KAAK,OAAO,UAAU,aAAa;AAAA,EAC5C;AAAA,EAEQ,OACN,UACA,QACwB;AACxB,UAAM,UAAU,wBAAwB,KAAK,IAAI;AACjD,QAAI,SAAS;AACX,aAAO,EAAE,IAAI,OAAO,MAAM,KAAK,MAAM,aAAa,CAAC,EAAE,MAAM,KAAK,MAAM,SAAS,QAAQ,CAAC,EAAE;AAAA,IAC5F;AACA,QAAI;AACJ,QAAI;AACF,aAAO,KAAK,YAAY;AACxB,aAAO,KAAK,aAAa,UAAU,MAAM;AAAA,IAC3C,SAAS,OAAO;AACd,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM,KAAK;AAAA,QACX,aAAa,CAAC,EAAE,MAAM,KAAK,MAAM,SAAS,YAAY,KAAK,EAAE,CAAC;AAAA,MAChE;AAAA,IACF,UAAE;AACA,UAAI,SAAS,OAAW,MAAK,YAAY,IAAI;AAAA,IAC/C;AAAA,EACF;AAAA,EAEQ,cAAsB;AAC5B,cAAU,QAAQ,KAAK,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACjD,UAAM,WAAW,GAAG,KAAK,IAAI;AAC7B,aAAS,UAAU,GAAG,UAAU,IAAI,WAAW;AAC7C,UAAI;AACF,eAAO,SAAS,UAAU,MAAM,GAAK;AAAA,MACvC,SAAS,OAAO;AACd,YAAK,MAAgC,SAAS,SAAU,OAAM;AAC9D,YAAI;AACF,cAAI,KAAK,IAAI,IAAI,SAAS,QAAQ,EAAE,UAAU,IAAQ,YAAW,QAAQ;AAAA,QAC3E,QAAQ;AAAA,QAER;AACA,gBAAQ,KAAK,IAAI,WAAW,IAAI,kBAAkB,CAAC,CAAC,GAAG,GAAG,GAAG,EAAE;AAAA,MACjE;AAAA,IACF;AACA,UAAM,IAAI,MAAM,wCAAwC,QAAQ,EAAE;AAAA,EACpE;AAAA,EAEQ,YAAY,YAA0B;AAC5C,cAAU,UAAU;AACpB,QAAI;AACF,iBAAW,GAAG,KAAK,IAAI,OAAO;AAAA,IAChC,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,aACN,UACA,QACwB;AACxB,UAAM,SAAS,KAAK,KAAK;AACzB,QAAI,OAAO,WAAW,eAAe,OAAO,WAAW,cAAc;AACnE,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM,KAAK;AAAA,QACX,aAAa,CAAC,EAAE,MAAM,KAAK,MAAM,SAAS,OAAO,MAAM,CAAC;AAAA,MAC1D;AAAA,IACF;AAEA,UAAM,YAAY,gBAAgB,OAAO,WAAW,UAAU,OAAO,WAAW,CAAC,CAAC;AAClF,QAAI;AACF,eAAS,SAAS;AAAA,IACpB,SAAS,OAAO;AACd,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM,KAAK;AAAA,QACX,aAAa,CAAC,EAAE,MAAM,KAAK,MAAM,SAAS,YAAY,KAAK,EAAE,CAAC;AAAA,MAChE;AAAA,IACF;AAEA,UAAM,iBAAiB,qBAAqB,KAAK,MAAM,SAAS;AAChE,UAAM,aAAa,mBAAmB,UAAU,SAAS;AACzD,UAAM,cAAc;AAAA,MAClB,GAAG;AAAA,MACH,GAAI,WAAW,UACX,CAAC,IACD,WAAW,MAAM,OAAO,IAAI,CAAC,UAAU,gBAAgB,KAAK,MAAM,KAAK,CAAC;AAAA,IAC9E;AACA,QAAI,YAAY,SAAS,EAAG,QAAO,EAAE,IAAI,OAAO,MAAM,KAAK,MAAM,YAAY;AAE7E,QAAI;AACF,YAAM,UACJ,OAAO,WAAW,WAAW,KAAK,UAAU,OAAO,QAAQ,MAAM,KAAK,UAAU,SAAS;AAC3F,UAAI,QAAS,MAAK,YAAY,KAAK,MAAM,KAAK,UAAU,WAAW,MAAM,CAAC,IAAI,IAAI;AAClF,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM,KAAK;AAAA,QACX,UAAU;AAAA,QACV;AAAA,QACA,QAAQ,OAAO;AAAA,UACb,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,mBAAmB,KAAK,KAAK,CAAC,CAAC;AAAA,QACpF;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM,KAAK;AAAA,QACX,aAAa,CAAC,EAAE,MAAM,KAAK,MAAM,SAAS,YAAY,KAAK,EAAE,CAAC;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,0BAA0B,aAA2C;AACnF,SAAO,YACJ,IAAI,CAAC,eAAe;AACnB,UAAM,WAAW,WAAW,YACxB,GAAG,WAAW,IAAI,KAAK,WAAW,SAAS,MAC3C,WAAW;AACf,WAAO,GAAG,QAAQ,KAAK,WAAW,OAAO;AAAA,EAC3C,CAAC,EACA,KAAK,IAAI;AACd;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/clock.ts"],"sourcesContent":["/**\n * Time that measures, separated from time that labels.\n *\n * `Date.now()` is the settable wall clock. NTP steps it, a VM resumes with it\n * wrong and then corrects, an operator fixes a drifted host, DST changes the\n * offset. None of that matters over a five-minute chat, and all of it is\n * near-certain over the multi-day runs Book is built for — which is why every\n * *duration* decision here reads a monotonic source instead.\n *\n * What the two are for:\n *\n * - **`monotonicNowMs()`** — \"how long has this been going?\" Retry budgets,\n * flush deadlines, kill timeouts, elapsed run time. It only ever moves\n * forward, at a rate nothing can adjust, and its origin is arbitrary, so a\n * reading is meaningless on its own and comparable only to another reading\n * *from the same process*.\n * - **`wallNowMs()`** — \"when did this happen?\" Anything written to a file, put\n * on screen, or compared against a stamp another process wrote. Still\n * `Date.now()`; the name exists so the choice is visible at the call site\n * rather than implied by its absence.\n *\n * **The origin is per-process, and that is the whole limit.** Two processes'\n * monotonic clocks share no zero, so a monotonic reading cannot be persisted,\n * sent, or compared across a process boundary. Cross-process liveness — the\n * background-shell heartbeat, the run-status file `book status` reads, the\n * retention sweeps — therefore stays on the wall clock by necessity, not by\n * oversight. A backwards step there reads as \"unexpectedly fresh\" and a forward\n * step can declare a live run stale; fixing that needs a sequence counter in the\n * file, not a better clock. See `MILESTONES.md`.\n *\n * Injected rather than imported directly at the decision sites, per the\n * module-level-mutable-state rule: a caller that needs to control time passes a\n * `Clock`, and tests do exactly that instead of reaching for fake timers.\n *\n * One trap worth knowing when writing those tests: vitest's default\n * `vi.useFakeTimers()` fakes `performance` too, so the monotonic clock stops\n * with the fake wall clock and a test cannot tell the two apart. Use\n * `vi.useFakeTimers({ toFake: ['Date'] })` to move the wall clock while the\n * monotonic one keeps its own time — that divergence is the thing under test.\n */\n\n/** A monotonic reading, in milliseconds. Only comparable within one process. */\nexport type MonotonicMs = number;\n\nexport interface Clock {\n /** Milliseconds since an arbitrary fixed origin. Never goes backwards. */\n monotonicNowMs(): MonotonicMs;\n /** Milliseconds since the Unix epoch. Settable; use only for labels. */\n wallNowMs(): number;\n}\n\n/**\n * `performance.now()` is monotonic in Node — it reads `uv_hrtime`, which is\n * unaffected by `settimeofday` — and is millisecond-resolution floating point,\n * which is finer than any deadline here needs.\n */\nexport const systemClock: Clock = {\n monotonicNowMs: () => performance.now(),\n wallNowMs: () => Date.now(),\n};\n\n/**\n * A clock a test drives by hand. Both hands start at the given values and move\n * only when told, so a test can advance one without the other — which is the\n * only way to show a duration decision is immune to a wall-clock step.\n */\nexport function createTestClock(options?: { monotonicMs?: number; wallMs?: number }): Clock & {\n advanceMonotonic(ms: number): void;\n setWall(ms: number): void;\n} {\n let monotonic = options?.monotonicMs ?? 0;\n let wall = options?.wallMs ?? 1_700_000_000_000;\n return {\n monotonicNowMs: () => monotonic,\n wallNowMs: () => wall,\n advanceMonotonic(ms: number) {\n // Guard the invariant the production clock has for free: a test that\n // rewinds this is testing something that cannot happen.\n if (ms < 0) throw new Error('monotonic time cannot move backwards');\n monotonic += ms;\n },\n setWall(ms: number) {\n wall = ms;\n },\n };\n}\n"],"mappings":";AAwDO,IAAM,cAAqB;AAAA,EAChC,gBAAgB,MAAM,YAAY,IAAI;AAAA,EACtC,WAAW,MAAM,KAAK,IAAI;AAC5B;","names":[]}
|