@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.
Files changed (177) hide show
  1. package/CHANGELOG.md +3771 -40
  2. package/README.md +135 -1311
  3. package/dist/additional-roots-VAWRQ625.js +32 -0
  4. package/dist/{app-MOXXWEOY.js → app-J7BVPSUH.js} +9648 -5509
  5. package/dist/app-J7BVPSUH.js.map +1 -0
  6. package/dist/chunk-2AAOF5B5.js +28 -0
  7. package/dist/chunk-2AAOF5B5.js.map +1 -0
  8. package/dist/chunk-32WBCUAN.js +660 -0
  9. package/dist/chunk-32WBCUAN.js.map +1 -0
  10. package/dist/chunk-4DKHLZRI.js +120 -0
  11. package/dist/chunk-4DKHLZRI.js.map +1 -0
  12. package/dist/{chunk-CAGOB2N4.js → chunk-62EEAUID.js} +6 -4
  13. package/dist/chunk-62EEAUID.js.map +1 -0
  14. package/dist/chunk-AHC7OU75.js +1124 -0
  15. package/dist/chunk-AHC7OU75.js.map +1 -0
  16. package/dist/{chunk-E75IPST5.js → chunk-AJZWLKLQ.js} +73 -70
  17. package/dist/chunk-AJZWLKLQ.js.map +1 -0
  18. package/dist/chunk-B5FC3MYZ.js +395 -0
  19. package/dist/chunk-B5FC3MYZ.js.map +1 -0
  20. package/dist/{chunk-KWHQ2XXL.js → chunk-DNFQTSMJ.js} +55 -13
  21. package/dist/chunk-DNFQTSMJ.js.map +1 -0
  22. package/dist/{chunk-J7MIZ5CA.js → chunk-E5WLQSKA.js} +719 -89
  23. package/dist/chunk-E5WLQSKA.js.map +1 -0
  24. package/dist/chunk-ECLC4CIJ.js +1181 -0
  25. package/dist/chunk-ECLC4CIJ.js.map +1 -0
  26. package/dist/chunk-EEX6QNXV.js +127 -0
  27. package/dist/chunk-EEX6QNXV.js.map +1 -0
  28. package/dist/chunk-EFXVSQ5J.js +931 -0
  29. package/dist/chunk-EFXVSQ5J.js.map +1 -0
  30. package/dist/{chunk-V4BOYR52.js → chunk-ENLB7NYV.js} +1304 -326
  31. package/dist/chunk-ENLB7NYV.js.map +1 -0
  32. package/dist/chunk-FPR7SKIE.js +6926 -0
  33. package/dist/chunk-FPR7SKIE.js.map +1 -0
  34. package/dist/{chunk-DGMQEI6Y.js → chunk-HFB74CLM.js} +30 -9
  35. package/dist/chunk-HFB74CLM.js.map +1 -0
  36. package/dist/{chunk-5RDRAO4B.js → chunk-HOZXKOE3.js} +37 -1
  37. package/dist/chunk-HOZXKOE3.js.map +1 -0
  38. package/dist/chunk-IDYWN37T.js +36 -0
  39. package/dist/chunk-IDYWN37T.js.map +1 -0
  40. package/dist/chunk-J4F2QLM2.js +273 -0
  41. package/dist/chunk-J4F2QLM2.js.map +1 -0
  42. package/dist/chunk-JGF5HLSI.js +1065 -0
  43. package/dist/chunk-JGF5HLSI.js.map +1 -0
  44. package/dist/{chunk-XIFFTATU.js → chunk-KDNWMKXV.js} +7166 -4237
  45. package/dist/chunk-KDNWMKXV.js.map +1 -0
  46. package/dist/chunk-KXAYUIKP.js +90 -0
  47. package/dist/chunk-KXAYUIKP.js.map +1 -0
  48. package/dist/{chunk-3U5IBM24.js → chunk-MEOB2DZA.js} +12 -2
  49. package/dist/chunk-MEOB2DZA.js.map +1 -0
  50. package/dist/{chunk-WKBO5R4O.js → chunk-MV5PQNIA.js} +2 -2
  51. package/dist/chunk-MV5PQNIA.js.map +1 -0
  52. package/dist/chunk-N2G4Q7ME.js +2672 -0
  53. package/dist/chunk-N2G4Q7ME.js.map +1 -0
  54. package/dist/chunk-OJBMDM5H.js +299 -0
  55. package/dist/chunk-OJBMDM5H.js.map +1 -0
  56. package/dist/{chunk-URLQAXTR.js → chunk-PTDC7ZUP.js} +2 -2
  57. package/dist/chunk-PTDC7ZUP.js.map +1 -0
  58. package/dist/{chunk-574C6MQR.js → chunk-REASC2QG.js} +173 -64
  59. package/dist/chunk-REASC2QG.js.map +1 -0
  60. package/dist/{chunk-7N4J557C.js → chunk-RT5SATF7.js} +1 -1
  61. package/dist/chunk-RT5SATF7.js.map +1 -0
  62. package/dist/chunk-SBTU5BEC.js +114 -0
  63. package/dist/chunk-SBTU5BEC.js.map +1 -0
  64. package/dist/{chunk-5KLVH3PY.js → chunk-SWPDKLNM.js} +5 -3
  65. package/dist/chunk-SWPDKLNM.js.map +1 -0
  66. package/dist/chunk-UCFJ43OS.js +520 -0
  67. package/dist/chunk-UCFJ43OS.js.map +1 -0
  68. package/dist/{chunk-VZQXX3WW.js → chunk-W7SLYS3M.js} +80 -16
  69. package/dist/chunk-W7SLYS3M.js.map +1 -0
  70. package/dist/{chunk-ANSJPYGI.js → chunk-WV3O5PGX.js} +3 -3
  71. package/dist/chunk-WV3O5PGX.js.map +1 -0
  72. package/dist/chunk-XHAO3K7I.js +18 -0
  73. package/dist/chunk-XHAO3K7I.js.map +1 -0
  74. package/dist/chunk-XJRJVXJR.js +1050 -0
  75. package/dist/chunk-XJRJVXJR.js.map +1 -0
  76. package/dist/chunk-YIL6HTJX.js +663 -0
  77. package/dist/chunk-YIL6HTJX.js.map +1 -0
  78. package/dist/{chunk-24AXE6SP.js → chunk-ZPSZZUWF.js} +1 -1
  79. package/dist/chunk-ZPSZZUWF.js.map +1 -0
  80. package/dist/{command-approvals-6Y57VCWZ.js → command-approvals-ZRY66LCW.js} +7 -5
  81. package/dist/context-C3RAP6K5.js +41 -0
  82. package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-DPQD5H65.js} +6 -5
  83. package/dist/index.js +947 -203
  84. package/dist/index.js.map +1 -1
  85. package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-73AQO5WP.js} +18 -9
  86. package/dist/interactive-assets-73AQO5WP.js.map +1 -0
  87. package/dist/job-runner.js +166 -111
  88. package/dist/job-runner.js.map +1 -1
  89. package/dist/job-supervisor.d.ts +2 -0
  90. package/dist/job-supervisor.js +121 -0
  91. package/dist/job-supervisor.js.map +1 -0
  92. package/dist/loader-DQNPDKOX.js +27 -0
  93. package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-27LJYADQ.js} +6 -5
  94. package/dist/{mcp-config-YIJIES2C.js → mcp-config-GXSODNCM.js} +4 -3
  95. package/dist/memory-store-I6Z54VUN.js +51 -0
  96. package/dist/{model-window-store-YFVNXCIP.js → model-window-store-6VKXXTPJ.js} +9 -4
  97. package/dist/openai-compatible-IPUNBJ53.js +16 -0
  98. package/dist/permission-approvals-Q5IMFEZI.js +15 -0
  99. package/dist/permissions-7Y35X6HZ.js +62 -0
  100. package/dist/provider-approvals-5AGH3RVT.js +76 -0
  101. package/dist/sandbox-N2RY72GN.js +42 -0
  102. package/dist/{scrollback-LUKLIBVI.js → scrollback-GD76GNM4.js} +44 -26
  103. package/dist/scrollback-GD76GNM4.js.map +1 -0
  104. package/dist/sdk.d.ts +2739 -2069
  105. package/dist/sdk.js +77 -42
  106. package/dist/sdk.js.map +1 -1
  107. package/dist/settings-loader-KDBH45AQ.js +43 -0
  108. package/dist/settings-loader-KDBH45AQ.js.map +1 -0
  109. package/dist/{settings-removed-JWWPT4WA.js → settings-removed-3ATWJEF3.js} +2 -2
  110. package/dist/settings-removed-3ATWJEF3.js.map +1 -0
  111. package/dist/shell-selection-Z5WORZU4.js +31 -0
  112. package/dist/shell-selection-Z5WORZU4.js.map +1 -0
  113. package/package.json +25 -24
  114. package/dist/app-MOXXWEOY.js.map +0 -1
  115. package/dist/chunk-24AXE6SP.js.map +0 -1
  116. package/dist/chunk-3U5IBM24.js.map +0 -1
  117. package/dist/chunk-574C6MQR.js.map +0 -1
  118. package/dist/chunk-5GDZ22YP.js +0 -449
  119. package/dist/chunk-5GDZ22YP.js.map +0 -1
  120. package/dist/chunk-5KLVH3PY.js.map +0 -1
  121. package/dist/chunk-5RDRAO4B.js.map +0 -1
  122. package/dist/chunk-7N4J557C.js.map +0 -1
  123. package/dist/chunk-A7JLBK2Y.js +0 -3506
  124. package/dist/chunk-A7JLBK2Y.js.map +0 -1
  125. package/dist/chunk-ANSJPYGI.js.map +0 -1
  126. package/dist/chunk-BTZZUCLI.js +0 -264
  127. package/dist/chunk-BTZZUCLI.js.map +0 -1
  128. package/dist/chunk-C7D4NJ2D.js +0 -217
  129. package/dist/chunk-C7D4NJ2D.js.map +0 -1
  130. package/dist/chunk-CAGOB2N4.js.map +0 -1
  131. package/dist/chunk-DGMQEI6Y.js.map +0 -1
  132. package/dist/chunk-E75IPST5.js.map +0 -1
  133. package/dist/chunk-EYGD6X2T.js +0 -180
  134. package/dist/chunk-EYGD6X2T.js.map +0 -1
  135. package/dist/chunk-GTVB4HEM.js +0 -765
  136. package/dist/chunk-GTVB4HEM.js.map +0 -1
  137. package/dist/chunk-J7MIZ5CA.js.map +0 -1
  138. package/dist/chunk-KWHQ2XXL.js.map +0 -1
  139. package/dist/chunk-L6LK2QIQ.js +0 -657
  140. package/dist/chunk-L6LK2QIQ.js.map +0 -1
  141. package/dist/chunk-MAQXCUR4.js +0 -1007
  142. package/dist/chunk-MAQXCUR4.js.map +0 -1
  143. package/dist/chunk-S4XL7HOM.js +0 -402
  144. package/dist/chunk-S4XL7HOM.js.map +0 -1
  145. package/dist/chunk-URLQAXTR.js.map +0 -1
  146. package/dist/chunk-V4BOYR52.js.map +0 -1
  147. package/dist/chunk-VZQXX3WW.js.map +0 -1
  148. package/dist/chunk-WKBO5R4O.js.map +0 -1
  149. package/dist/chunk-XIFFTATU.js.map +0 -1
  150. package/dist/context-ZC5IFEAX.js +0 -25
  151. package/dist/interactive-assets-CCAAWVKH.js.map +0 -1
  152. package/dist/loader-4IDS2RIE.js +0 -15
  153. package/dist/openai-compatible-RHZUBHV6.js +0 -10
  154. package/dist/permission-approvals-76PVREFD.js +0 -14
  155. package/dist/permissions-CHCKTFQQ.js +0 -28
  156. package/dist/sandbox-CNVAQDA7.js +0 -23
  157. package/dist/scrollback-LUKLIBVI.js.map +0 -1
  158. package/dist/settings-loader-M2QP5TX2.js +0 -21
  159. package/dist/shell-selection-V6QAYXPX.js +0 -17
  160. package/patches/ink+6.8.0.patch +0 -13
  161. package/scripts/apply-ink-patch.mjs +0 -29
  162. package/scripts/ink-patch.mjs +0 -59
  163. package/scripts/verify-ink-patch.mjs +0 -11
  164. /package/dist/{command-approvals-6Y57VCWZ.js.map → additional-roots-VAWRQ625.js.map} +0 -0
  165. /package/dist/{context-ZC5IFEAX.js.map → command-approvals-ZRY66LCW.js.map} +0 -0
  166. /package/dist/{hook-approvals-CTUFKEJT.js.map → context-C3RAP6K5.js.map} +0 -0
  167. /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-DPQD5H65.js.map} +0 -0
  168. /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-DQNPDKOX.js.map} +0 -0
  169. /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-27LJYADQ.js.map} +0 -0
  170. /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-GXSODNCM.js.map} +0 -0
  171. /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-I6Z54VUN.js.map} +0 -0
  172. /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-6VKXXTPJ.js.map} +0 -0
  173. /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-IPUNBJ53.js.map} +0 -0
  174. /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-Q5IMFEZI.js.map} +0 -0
  175. /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-7Y35X6HZ.js.map} +0 -0
  176. /package/dist/{settings-removed-JWWPT4WA.js.map → provider-approvals-5AGH3RVT.js.map} +0 -0
  177. /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
- }).default({}),
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
- }).default({})
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: z.number().int().positive().optional(),
110
- maxOutputTokens: z.number().int().positive().optional(),
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 see it
154
- * either — the repeated-failure breaker ignores anything that is not `error`,
155
- * and `toolCallStats.failures` excludes `blocked` by construction.
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: z.number().int().min(0).default(0)
211
+ maxWallClockMs: unboundedInt().min(0).default(0)
164
212
  });
165
213
  var memorySettingsSchema = z.object({
166
214
  enabled: z.boolean().default(true),
167
- autoSave: z.boolean().default(true),
168
- requireApproval: z.boolean().default(true)
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: z.number().int().min(0).default(2 * 1024 * 1024 * 1024),
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: z.number().int().min(1).optional(),
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) }).default({}),
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
- }).default({}),
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: z.number().int().min(1).optional(),
301
- maxTokens: z.number().int().min(1e3).optional(),
364
+ maxTurns: unboundedInt().min(1).optional(),
365
+ maxTokens: unboundedInt().min(1e3).optional(),
302
366
  effort: effortLevelSchema.optional(),
303
- /** TUI color theme: apple (default), dark, light, auto, a bundled palette, or a custom theme filename. */
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.default({}),
313
- skills: skillSettingsSchema.default({}),
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
- env: z.record(z.string()).default({}),
320
- provider: z.record(providerConfigSchema).default({}),
321
- permissions: permissionsSchema.default({}),
322
- sandbox: sandboxSchema.default({}),
323
- hooks: hooksSchema.default({}),
324
- retry: retrySettingsSchema.default({}),
325
- memory: memorySettingsSchema.default({}),
326
- continuation: continuationSettingsSchema.default({}),
327
- agents: agentSettingsSchema.default({}),
328
- toolDiscovery: toolDiscoverySettingsSchema.default({}),
329
- toolExecution: toolExecutionSettingsSchema.default({}),
330
- observability: observabilitySettingsSchema.default({}),
331
- mcp: mcpSettingsSchema.default({}),
332
- commands: commandSettingsSchema.default({})
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-574C6MQR.js.map
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":"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","names":[]}
@@ -7,4 +7,4 @@ var systemClock = {
7
7
  export {
8
8
  systemClock
9
9
  };
10
- //# sourceMappingURL=chunk-7N4J557C.js.map
10
+ //# sourceMappingURL=chunk-RT5SATF7.js.map
@@ -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":[]}