@the-open-engine/zeroshot 6.38.1 → 6.39.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 (377) hide show
  1. package/cli/event-copy.js +8 -7
  2. package/cli/event-copy.ts +12 -0
  3. package/cli/index.js +7 -31
  4. package/cli/json-export.js +61 -64
  5. package/cli/json-export.ts +112 -0
  6. package/cli/message-formatter-utils.js +68 -56
  7. package/cli/message-formatter-utils.ts +121 -0
  8. package/cli/message-formatters-normal.js +124 -259
  9. package/cli/message-formatters-normal.ts +250 -0
  10. package/cli/message-formatters-watch.js +81 -84
  11. package/cli/message-formatters-watch.ts +189 -0
  12. package/lib/cluster/generated/protocol-schema.cjs +1 -1
  13. package/lib/cluster/generated/protocol-schema.mjs +1 -1
  14. package/lib/cluster/generated/protocol.d.cts +8 -0
  15. package/lib/cluster/generated/protocol.d.mts +8 -0
  16. package/lib/cluster/generated/protocol.d.ts +8 -0
  17. package/lib/clusters-registry.d.ts +23 -0
  18. package/lib/clusters-registry.js +17 -29
  19. package/lib/completion.d.ts +12 -0
  20. package/lib/completion.js +84 -94
  21. package/lib/compose-utils.d.ts +32 -0
  22. package/lib/compose-utils.js +57 -72
  23. package/lib/detached-startup.d.ts +83 -0
  24. package/lib/detached-startup.js +231 -302
  25. package/lib/docker-config.d.ts +43 -0
  26. package/lib/docker-config.js +150 -325
  27. package/lib/docker-env-auth.d.ts +17 -0
  28. package/lib/docker-env-auth.js +75 -0
  29. package/lib/git-remote-utils.d.ts +48 -0
  30. package/lib/git-remote-utils.js +147 -213
  31. package/lib/id-detector.d.ts +15 -0
  32. package/lib/id-detector.js +37 -41
  33. package/lib/path-check.d.ts +30 -0
  34. package/lib/path-check.js +42 -45
  35. package/lib/process-liveness.d.ts +16 -0
  36. package/lib/process-liveness.js +19 -12
  37. package/lib/provider-credential-path.d.ts +16 -0
  38. package/lib/provider-credential-path.js +31 -29
  39. package/lib/provider-defaults.d.ts +30 -0
  40. package/lib/provider-defaults.js +47 -42
  41. package/lib/provider-detection.d.ts +11 -0
  42. package/lib/provider-detection.js +91 -54
  43. package/lib/provider-names.d.ts +38 -0
  44. package/lib/provider-names.js +56 -64
  45. package/lib/repo-settings.d.ts +28 -0
  46. package/lib/repo-settings.js +83 -59
  47. package/lib/run-mode.d.ts +21 -0
  48. package/lib/run-mode.js +21 -20
  49. package/lib/run-plan.d.ts +28 -0
  50. package/lib/run-plan.js +17 -16
  51. package/lib/settings/claude-auth.d.ts +35 -0
  52. package/lib/settings/claude-auth.js +30 -47
  53. package/lib/settings-defaults.d.ts +20 -0
  54. package/lib/settings-defaults.js +149 -0
  55. package/lib/settings-error.d.ts +7 -0
  56. package/lib/settings-error.js +8 -0
  57. package/lib/settings-issue-providers.d.ts +10 -0
  58. package/lib/settings-issue-providers.js +12 -0
  59. package/lib/settings-models.d.ts +22 -0
  60. package/lib/settings-models.js +93 -0
  61. package/lib/settings-storage.d.ts +23 -0
  62. package/lib/settings-storage.js +205 -0
  63. package/lib/settings-validation.d.ts +7 -0
  64. package/lib/settings-validation.js +182 -0
  65. package/lib/settings.d.ts +29 -0
  66. package/lib/settings.js +41 -736
  67. package/lib/setup-apply-types.d.ts +66 -0
  68. package/lib/setup-apply-types.js +2 -0
  69. package/lib/setup-apply.d.ts +39 -0
  70. package/lib/setup-apply.js +267 -264
  71. package/lib/setup-journal.d.ts +41 -0
  72. package/lib/setup-journal.js +119 -76
  73. package/lib/setup-plan-types.d.ts +102 -0
  74. package/lib/setup-plan-types.js +2 -0
  75. package/lib/setup-plan.d.ts +30 -0
  76. package/lib/setup-plan.js +300 -324
  77. package/lib/setup-undo.d.ts +44 -0
  78. package/lib/setup-undo.js +61 -74
  79. package/lib/start-cluster-config.d.ts +26 -0
  80. package/lib/start-cluster-config.js +56 -0
  81. package/lib/start-cluster-environment.d.ts +40 -0
  82. package/lib/start-cluster-environment.js +134 -0
  83. package/lib/start-cluster-run-options.d.ts +53 -0
  84. package/lib/start-cluster-run-options.js +147 -0
  85. package/lib/start-cluster.d.ts +64 -0
  86. package/lib/start-cluster.js +98 -454
  87. package/lib/stream-json-parser.d.ts +17 -0
  88. package/lib/stream-json-parser.js +51 -52
  89. package/npm-shrinkwrap.json +2 -2
  90. package/package.json +12 -6
  91. package/scripts/postinstall.js +27 -0
  92. package/scripts/rust-distribution.js +23 -14
  93. package/src/agent/agent-command-proofs-context.js +35 -45
  94. package/src/agent/agent-command-proofs-context.ts +87 -0
  95. package/src/agent/agent-config-settings.js +19 -0
  96. package/src/agent/agent-config-settings.ts +41 -0
  97. package/src/agent/agent-config.js +122 -231
  98. package/src/agent/agent-config.ts +285 -0
  99. package/src/agent/agent-context-builder.js +38 -273
  100. package/src/agent/agent-context-builder.ts +51 -0
  101. package/src/agent/agent-context-environment-sections.js +105 -0
  102. package/src/agent/agent-context-environment-sections.ts +134 -0
  103. package/src/agent/agent-context-packs.js +165 -0
  104. package/src/agent/agent-context-packs.ts +296 -0
  105. package/src/agent/agent-context-prompt-sections.js +100 -0
  106. package/src/agent/agent-context-prompt-sections.ts +148 -0
  107. package/src/agent/agent-context-sections.js +25 -334
  108. package/src/agent/agent-context-sections.ts +36 -0
  109. package/src/agent/agent-context-sources.js +96 -149
  110. package/src/agent/agent-context-sources.ts +253 -0
  111. package/src/agent/agent-context-types.js +2 -0
  112. package/src/agent/agent-context-types.ts +132 -0
  113. package/src/agent/agent-context-validation-sections.js +73 -0
  114. package/src/agent/agent-context-validation-sections.ts +129 -0
  115. package/src/agent/agent-input-injector.js +99 -118
  116. package/src/agent/agent-input-injector.ts +195 -0
  117. package/src/agent/agent-liveness-poll.js +74 -78
  118. package/src/agent/agent-liveness-poll.ts +163 -0
  119. package/src/agent/agent-quality-gate-schema.js +77 -86
  120. package/src/agent/agent-quality-gate-schema.ts +135 -0
  121. package/src/agent/agent-quality-gates-context.js +44 -50
  122. package/src/agent/agent-quality-gates-context.ts +91 -0
  123. package/src/agent/agent-stuck-detector.js +171 -239
  124. package/src/agent/agent-stuck-detector.ts +288 -0
  125. package/src/agent/agent-trigger-evaluator.js +29 -46
  126. package/src/agent/agent-trigger-evaluator.ts +98 -0
  127. package/src/agent/context-metrics.js +110 -141
  128. package/src/agent/context-metrics.ts +261 -0
  129. package/src/agent/context-pack-builder.js +72 -358
  130. package/src/agent/context-pack-builder.ts +105 -0
  131. package/src/agent/context-pack-core.js +70 -0
  132. package/src/agent/context-pack-core.ts +96 -0
  133. package/src/agent/context-pack-selection.js +119 -0
  134. package/src/agent/context-pack-selection.ts +187 -0
  135. package/src/agent/context-pack-truncation.js +139 -0
  136. package/src/agent/context-pack-truncation.ts +208 -0
  137. package/src/agent/context-pack-types.js +2 -0
  138. package/src/agent/context-pack-types.ts +98 -0
  139. package/src/agent/context-replay-policy.js +30 -39
  140. package/src/agent/context-replay-policy.ts +71 -0
  141. package/src/agent/critical-agent-policy.js +6 -10
  142. package/src/agent/critical-agent-policy.ts +20 -0
  143. package/src/agent/guidance-queue.js +74 -87
  144. package/src/agent/guidance-queue.ts +166 -0
  145. package/src/agent/output-extraction-error-detail.js +45 -0
  146. package/src/agent/output-extraction-error-detail.ts +49 -0
  147. package/src/agent/output-extraction-failures.js +125 -0
  148. package/src/agent/output-extraction-failures.ts +149 -0
  149. package/src/agent/output-extraction-json.js +234 -0
  150. package/src/agent/output-extraction-json.ts +235 -0
  151. package/src/agent/output-extraction-pi.js +124 -0
  152. package/src/agent/output-extraction-pi.ts +137 -0
  153. package/src/agent/output-extraction-types.js +2 -0
  154. package/src/agent/output-extraction-types.ts +49 -0
  155. package/src/agent/output-extraction.js +17 -617
  156. package/src/agent/output-extraction.ts +30 -0
  157. package/src/agent/output-reformatter-errors.js +83 -0
  158. package/src/agent/output-reformatter-errors.ts +106 -0
  159. package/src/agent/output-reformatter-types.js +2 -0
  160. package/src/agent/output-reformatter-types.ts +86 -0
  161. package/src/agent/output-reformatter.js +140 -185
  162. package/src/agent/output-reformatter.ts +238 -0
  163. package/src/agent/pi-terminal-lifecycle-types.js +2 -0
  164. package/src/agent/pi-terminal-lifecycle-types.ts +41 -0
  165. package/src/agent/pi-terminal-lifecycle.js +186 -206
  166. package/src/agent/pi-terminal-lifecycle.ts +297 -0
  167. package/src/agent/provider-control-plane.js +22 -21
  168. package/src/agent/provider-control-plane.ts +52 -0
  169. package/src/agent/provider-terminal-failure-types.js +2 -0
  170. package/src/agent/provider-terminal-failure-types.ts +60 -0
  171. package/src/agent/provider-terminal-failure.js +194 -191
  172. package/src/agent/provider-terminal-failure.ts +262 -0
  173. package/src/agent/rate-limit-backoff.js +31 -74
  174. package/src/agent/rate-limit-backoff.ts +71 -0
  175. package/src/agent/schema-utils.js +85 -125
  176. package/src/agent/schema-utils.ts +148 -0
  177. package/src/agent/structured-output-error.js +34 -33
  178. package/src/agent/structured-output-error.ts +105 -0
  179. package/src/agent/validation-platform.js +27 -27
  180. package/src/agent/validation-platform.ts +49 -0
  181. package/src/attach/attach-client.js +232 -405
  182. package/src/attach/attach-client.ts +300 -0
  183. package/src/attach/attach-server-cleanup.js +43 -0
  184. package/src/attach/attach-server-cleanup.ts +39 -0
  185. package/src/attach/attach-server-clients.js +117 -0
  186. package/src/attach/attach-server-clients.ts +122 -0
  187. package/src/attach/attach-server-events.js +38 -0
  188. package/src/attach/attach-server-events.ts +36 -0
  189. package/src/attach/attach-server-pty.js +45 -0
  190. package/src/attach/attach-server-pty.ts +64 -0
  191. package/src/attach/attach-server-runtime.js +73 -0
  192. package/src/attach/attach-server-runtime.ts +62 -0
  193. package/src/attach/attach-server-socket.js +28 -0
  194. package/src/attach/attach-server-socket.ts +23 -0
  195. package/src/attach/attach-server-types.js +2 -0
  196. package/src/attach/attach-server-types.ts +82 -0
  197. package/src/attach/attach-server.js +106 -523
  198. package/src/attach/attach-server.ts +158 -0
  199. package/src/attach/index.js +18 -35
  200. package/src/attach/index.ts +18 -0
  201. package/src/attach/protocol.js +95 -184
  202. package/src/attach/protocol.ts +181 -0
  203. package/src/attach/ring-buffer.js +76 -102
  204. package/src/attach/ring-buffer.ts +109 -0
  205. package/src/attach/send-input.js +64 -75
  206. package/src/attach/send-input.ts +94 -0
  207. package/src/attach/socket-discovery.js +164 -218
  208. package/src/attach/socket-discovery.ts +230 -0
  209. package/src/attach/socket-paths.js +42 -61
  210. package/src/attach/socket-paths.ts +81 -0
  211. package/src/claude-credentials.js +51 -54
  212. package/src/claude-credentials.ts +89 -0
  213. package/src/cluster/generated/protocol-schema.ts +1 -1
  214. package/src/cluster/generated/protocol.ts +2 -1
  215. package/src/command-proofs.js +110 -141
  216. package/src/command-proofs.ts +216 -0
  217. package/src/config-router.js +81 -73
  218. package/src/config-router.ts +131 -0
  219. package/src/copy-worker.js +53 -29
  220. package/src/copy-worker.ts +75 -0
  221. package/src/darwin-keychain-boundary.js +80 -111
  222. package/src/darwin-keychain-boundary.ts +148 -0
  223. package/src/guidance-topics.js +4 -5
  224. package/src/guidance-topics.ts +10 -0
  225. package/src/input-helpers.js +33 -54
  226. package/src/input-helpers.ts +58 -0
  227. package/src/ledger-sequence.js +45 -48
  228. package/src/ledger-sequence.ts +63 -0
  229. package/src/legacy-lib/clusters-registry.ts +40 -0
  230. package/src/legacy-lib/completion.ts +148 -0
  231. package/src/legacy-lib/compose-utils.ts +102 -0
  232. package/src/legacy-lib/detached-startup.ts +449 -0
  233. package/src/legacy-lib/docker-config.ts +282 -0
  234. package/src/legacy-lib/docker-env-auth.ts +116 -0
  235. package/src/legacy-lib/git-remote-utils.ts +244 -0
  236. package/src/legacy-lib/id-detector.ts +57 -0
  237. package/src/legacy-lib/js-yaml.d.ts +3 -0
  238. package/src/legacy-lib/omelette.d.ts +15 -0
  239. package/src/legacy-lib/path-check.ts +89 -0
  240. package/src/legacy-lib/process-liveness.ts +36 -0
  241. package/src/legacy-lib/provider-credential-path.ts +57 -0
  242. package/src/legacy-lib/provider-defaults.ts +114 -0
  243. package/src/legacy-lib/provider-detection.ts +67 -0
  244. package/src/legacy-lib/provider-names.ts +126 -0
  245. package/src/legacy-lib/repo-settings.ts +88 -0
  246. package/src/legacy-lib/run-mode.ts +58 -0
  247. package/src/legacy-lib/run-plan.ts +49 -0
  248. package/src/legacy-lib/settings/claude-auth.ts +80 -0
  249. package/src/legacy-lib/settings-defaults.ts +212 -0
  250. package/src/legacy-lib/settings-error.ts +8 -0
  251. package/src/legacy-lib/settings-issue-providers.ts +18 -0
  252. package/src/legacy-lib/settings-models.ts +124 -0
  253. package/src/legacy-lib/settings-storage.ts +277 -0
  254. package/src/legacy-lib/settings-validation.ts +220 -0
  255. package/src/legacy-lib/settings.ts +87 -0
  256. package/src/legacy-lib/setup-apply-types.ts +70 -0
  257. package/src/legacy-lib/setup-apply.ts +456 -0
  258. package/src/legacy-lib/setup-journal.ts +185 -0
  259. package/src/legacy-lib/setup-plan-types.ts +96 -0
  260. package/src/legacy-lib/setup-plan.ts +500 -0
  261. package/src/legacy-lib/setup-undo.ts +142 -0
  262. package/src/legacy-lib/start-cluster-config.ts +124 -0
  263. package/src/legacy-lib/start-cluster-environment.ts +165 -0
  264. package/src/legacy-lib/start-cluster-run-options.ts +279 -0
  265. package/src/legacy-lib/start-cluster.ts +237 -0
  266. package/src/legacy-lib/stream-json-parser.ts +88 -0
  267. package/src/message-buffer.js +55 -71
  268. package/src/message-buffer.ts +107 -0
  269. package/src/message-bus-bridge.js +92 -133
  270. package/src/message-bus-bridge.ts +149 -0
  271. package/src/name-generator-shared.js +220 -0
  272. package/src/name-generator-shared.ts +231 -0
  273. package/src/name-generator.js +8 -217
  274. package/src/name-generator.ts +27 -0
  275. package/src/omp-blob-root.js +83 -64
  276. package/src/omp-blob-root.ts +116 -0
  277. package/src/omp-config-overlay.js +65 -38
  278. package/src/omp-config-overlay.ts +110 -0
  279. package/src/omp-execution-fingerprint.js +38 -43
  280. package/src/omp-execution-fingerprint.ts +92 -0
  281. package/src/omp-session-limits.js +10 -15
  282. package/src/omp-session-limits.ts +53 -0
  283. package/src/omp-session-partition.js +2 -2
  284. package/src/omp-session-verifier.js +1 -1
  285. package/src/orchestrator.js +19 -4
  286. package/src/providers/anthropic/index.js +3 -1
  287. package/src/providers/anthropic/index.ts +13 -0
  288. package/src/providers/capabilities.js +19 -27
  289. package/src/providers/capabilities.ts +44 -0
  290. package/src/providers/google/index.js +3 -1
  291. package/src/providers/google/index.ts +13 -0
  292. package/src/providers/openai/index.js +3 -1
  293. package/src/providers/openai/index.ts +13 -0
  294. package/src/providers/opencode/index.js +3 -1
  295. package/src/providers/opencode/index.ts +13 -0
  296. package/src/quality-gates.js +67 -127
  297. package/src/quality-gates.ts +149 -0
  298. package/src/repo-settings-access.js +40 -0
  299. package/src/repo-settings-access.ts +64 -0
  300. package/src/schemas/sub-cluster.js +127 -183
  301. package/src/schemas/sub-cluster.ts +238 -0
  302. package/src/state-snapshot-normalization.js +224 -0
  303. package/src/state-snapshot-normalization.ts +222 -0
  304. package/src/state-snapshot.js +192 -359
  305. package/src/state-snapshot.ts +291 -0
  306. package/src/state-snapshotter.js +118 -130
  307. package/src/state-snapshotter.ts +167 -0
  308. package/src/sub-cluster-wrapper.js +4 -13
  309. package/src/task-run-model-args.js +87 -124
  310. package/src/task-run-model-args.ts +204 -0
  311. package/src/task-runner.js +5 -25
  312. package/src/task-runner.ts +28 -0
  313. package/src/task-startup-error.js +73 -58
  314. package/src/task-startup-error.ts +126 -0
  315. package/src/template-validation/consensus-gate-contracts.js +2 -0
  316. package/src/template-validation/consensus-gate-contracts.ts +70 -0
  317. package/src/template-validation/consensus-gate-scenarios.js +76 -0
  318. package/src/template-validation/consensus-gate-scenarios.ts +130 -0
  319. package/src/template-validation/consensus-gate-stage.js +34 -0
  320. package/src/template-validation/consensus-gate-stage.ts +56 -0
  321. package/src/template-validation/random-topology-contracts.js +2 -0
  322. package/src/template-validation/random-topology-contracts.ts +54 -0
  323. package/src/template-validation/random-topology-dispatch.js +25 -0
  324. package/src/template-validation/random-topology-dispatch.ts +33 -0
  325. package/src/template-validation/random-topology-message-loop.js +53 -0
  326. package/src/template-validation/random-topology-message-loop.ts +73 -0
  327. package/src/template-validation/random-topology-operations.js +79 -0
  328. package/src/template-validation/random-topology-operations.ts +101 -0
  329. package/src/template-validation/random-topology-runtime.js +25 -0
  330. package/src/template-validation/random-topology-runtime.ts +43 -0
  331. package/src/template-validation/random-topology-scenario.js +52 -0
  332. package/src/template-validation/random-topology-scenario.ts +57 -0
  333. package/src/template-validation/random-topology-schema.js +159 -0
  334. package/src/template-validation/random-topology-schema.ts +190 -0
  335. package/src/template-validation/random-topology-state.js +28 -0
  336. package/src/template-validation/random-topology-state.ts +30 -0
  337. package/src/template-validation/random-topology-trigger-evaluation.js +45 -0
  338. package/src/template-validation/random-topology-trigger-evaluation.ts +65 -0
  339. package/src/template-validation/random-topology-trigger.js +75 -0
  340. package/src/template-validation/random-topology-trigger.ts +88 -0
  341. package/src/template-validation/report-formatter.js +40 -46
  342. package/src/template-validation/report-formatter.ts +101 -0
  343. package/src/template-validation/simulate-consensus-gates.js +111 -302
  344. package/src/template-validation/simulate-consensus-gates.ts +196 -0
  345. package/src/template-validation/simulate-random-topology.js +26 -519
  346. package/src/template-validation/simulate-random-topology.ts +57 -0
  347. package/src/template-validation/simulate-two-stage-validation.js +41 -265
  348. package/src/template-validation/simulate-two-stage-validation.ts +55 -0
  349. package/src/template-validation/simulation-agent-runtime.js +25 -0
  350. package/src/template-validation/simulation-agent-runtime.ts +40 -0
  351. package/src/template-validation/simulation-agent.js +29 -0
  352. package/src/template-validation/simulation-agent.ts +73 -0
  353. package/src/template-validation/simulation-runtime.js +34 -0
  354. package/src/template-validation/simulation-runtime.ts +72 -0
  355. package/src/template-validation/two-stage-contracts.js +2 -0
  356. package/src/template-validation/two-stage-contracts.ts +41 -0
  357. package/src/template-validation/two-stage-inputs.js +21 -0
  358. package/src/template-validation/two-stage-inputs.ts +29 -0
  359. package/src/template-validation/two-stage-results.js +30 -0
  360. package/src/template-validation/two-stage-results.ts +40 -0
  361. package/src/template-validation/two-stage-scenario.js +86 -0
  362. package/src/template-validation/two-stage-scenario.ts +120 -0
  363. package/src/worktree-claude-config.js +182 -220
  364. package/src/worktree-claude-config.ts +272 -0
  365. package/src/worktree-tooling-env.js +103 -125
  366. package/src/worktree-tooling-env.ts +142 -0
  367. package/task-lib/completion.js +2 -3
  368. package/task-lib/completion.ts +9 -0
  369. package/task-lib/config.js +1 -4
  370. package/task-lib/config.ts +11 -0
  371. package/task-lib/name-generator.js +4 -226
  372. package/task-lib/name-generator.ts +11 -0
  373. package/task-lib/omp-session-cleanup.js +1 -1
  374. package/task-lib/omp-storage-root.js +8 -12
  375. package/task-lib/omp-storage-root.ts +62 -0
  376. package/task-lib/process-termination.js +143 -166
  377. package/task-lib/process-termination.ts +268 -0
package/lib/settings.js CHANGED
@@ -1,741 +1,46 @@
1
- /**
2
- * Settings management for zeroshot
3
- * Persistent user preferences stored in ~/.zeroshot/settings.json
4
- */
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
- const os = require('os');
9
- const crypto = require('crypto');
10
- const lockfile = require('proper-lockfile');
11
- const { validateMountConfig, validateEnvPassthrough } = require('./docker-config');
12
- const {
13
- VALID_PROVIDERS,
14
- getDefaultProviderId,
15
- normalizeProviderName,
16
- normalizeProviderSettings,
17
- } = require('./provider-names');
18
-
19
- const SETTINGS_LOCK_STALE_MS = 5000;
20
- const SETTINGS_LOCK_TIMEOUT_MS = 500;
21
- const SETTINGS_LOCK_RETRY_MS = 20;
22
- const SETTINGS_LOCK_SLEEP = new Int32Array(new SharedArrayBuffer(4));
23
-
24
- class SettingsValidationError extends Error {
25
- constructor(message) {
26
- super(message);
27
- this.name = 'SettingsValidationError';
28
- }
29
- }
30
-
31
- // Lazy-loaded to avoid circular dependency (issue-providers → settings → issue-providers)
32
- let _issueProviderFns = null;
33
- function getIssueProviderFns() {
34
- if (!_issueProviderFns) {
35
- _issueProviderFns = require('../src/issue-providers');
36
- }
37
- return _issueProviderFns;
38
- }
39
-
40
- /**
41
- * Get settings file path (dynamically reads env var for testing)
42
- * Using a getter ensures tests can override the path at runtime
43
- * @returns {string}
44
- */
45
- function getSettingsFile() {
46
- return (
47
- process.env.ZEROSHOT_SETTINGS_FILE || path.join(os.homedir(), '.zeroshot', 'settings.json')
48
- );
49
- }
50
-
51
- /**
52
- * Whether the global settings file exists on disk (vs. running on defaults).
53
- * @returns {boolean}
54
- */
55
- function settingsFileExists() {
56
- return fs.existsSync(getSettingsFile());
57
- }
58
-
59
- // Import provider defaults from separate module to avoid circular dependency
60
- const { getProviderDefaults, clearProviderDefaultsCache } = require('./provider-defaults');
61
-
62
- /**
63
- * Model hierarchy for cost ceiling validation
64
- * Higher number = more expensive/capable model
65
- */
66
- const MODEL_HIERARCHY = {
67
- opus: 3,
68
- fable: 3,
69
- sonnet: 2,
70
- haiku: 1,
71
- };
72
-
73
- const VALID_MODELS = Object.keys(MODEL_HIERARCHY);
74
- const LEVEL_RANKS = { level1: 1, level2: 2, level3: 3 };
75
-
76
- /**
77
- * Validate a requested model against the maxModel ceiling and minModel floor
78
- * @param {string} requestedModel - Model the agent wants to use
79
- * @param {string} maxModel - Maximum allowed model (cost ceiling)
80
- * @param {string|null} minModel - Minimum required model (cost floor)
81
- * @returns {string} The validated model
82
- * @throws {Error} If requested model exceeds ceiling or falls below floor
83
- */
84
- function validateModelAgainstMax(requestedModel, maxModel, minModel = null) {
85
- if (!requestedModel) return maxModel; // Default to ceiling if unspecified
86
-
87
- if (!VALID_MODELS.includes(requestedModel)) {
88
- throw new Error(`Invalid model "${requestedModel}". Valid: ${VALID_MODELS.join(', ')}`);
89
- }
90
- if (!VALID_MODELS.includes(maxModel)) {
91
- throw new Error(`Invalid maxModel "${maxModel}". Valid: ${VALID_MODELS.join(', ')}`);
92
- }
93
-
94
- if (MODEL_HIERARCHY[requestedModel] > MODEL_HIERARCHY[maxModel]) {
95
- throw new Error(
96
- `Agent requests "${requestedModel}" but maxModel is "${maxModel}". ` +
97
- `Either lower agent's model or raise maxModel.`
98
- );
99
- }
100
-
101
- if (minModel) {
102
- if (!VALID_MODELS.includes(minModel)) {
103
- throw new Error(`Invalid minModel "${minModel}". Valid: ${VALID_MODELS.join(', ')}`);
104
- }
105
- if (MODEL_HIERARCHY[minModel] > MODEL_HIERARCHY[maxModel]) {
106
- throw new Error(`minModel "${minModel}" cannot be higher than maxModel "${maxModel}".`);
107
- }
108
- if (MODEL_HIERARCHY[requestedModel] < MODEL_HIERARCHY[minModel]) {
109
- throw new Error(
110
- `Agent requests "${requestedModel}" but minModel is "${minModel}". ` +
111
- `Either raise agent's model or lower minModel.`
112
- );
113
- }
114
- }
115
-
116
- return requestedModel;
117
- }
118
-
119
- // Default settings (base - without issue provider settings which are added dynamically)
120
- const DEFAULT_SETTINGS_BASE = {
121
- maxModel: 'opus', // Cost ceiling - agents cannot use models above this
122
- minModel: null, // Cost floor - agents cannot use models below this (null = no minimum)
123
- defaultProvider: getDefaultProviderId(),
124
- get providerSettings() {
125
- // Dynamically build from providers on first access
126
- return getProviderDefaults();
127
- },
128
- defaultConfig: 'conductor-bootstrap',
129
- defaultIsolation: 'none', // 'none' | 'worktree' | 'docker'
130
- defaultDelivery: 'none', // 'none' | 'pr' | 'ship'
131
- setupVersion: null, // Internal marker written only after guided setup succeeds.
132
- strictSchema: true, // true = reliable json output (default), false = live streaming (may crash - see bold-meadow-11)
133
- logLevel: 'normal',
134
- // Automatic update notification cache
135
- autoCheckUpdates: true, // Check npm registry for newer versions
136
- lastUpdateCheckAt: null, // Unix timestamp of last automatic attempt (null = never checked)
137
- lastSeenVersion: null, // Compatibility key containing the cached valid npm-latest version
138
- lastUpdateCheckClaim: null, // Opaque ownership token for an in-flight automatic refresh
139
- // Claude command - customize how to invoke Claude CLI (default: 'claude')
140
- // Example: 'ccr code' for claude-code-router integration
141
- claudeCommand: 'claude',
142
- // Docker isolation mounts - preset names or {host, container, readonly?} objects
143
- // Valid presets: infrastructure presets plus provider ids from the provider registry
144
- dockerMounts: ['gh', 'git', 'ssh'],
145
- // Extra env vars to pass to Docker container (in addition to preset-implied ones)
146
- // Supports: VAR (if set), VAR_* (pattern), VAR=value (forced), VAR= (empty)
147
- dockerEnvPassthrough: [],
148
- // Container home directory - where $HOME resolves in container paths
149
- // Default: /home/node (matches zeroshot-cluster-base image)
150
- dockerContainerHome: '/home/node',
151
- // Retry/restart robustness defaults
152
- maxRetries: 3, // Agent task retries (per execution) for retryable errors
153
- maxRestartAttempts: 3, // Agent restarts since last TASK_COMPLETED
154
- maxTotalRestarts: 10, // Safety valve (never resets)
155
- staleWarningsBeforeKill: 2, // Consecutive stale warnings before restart
156
- backoffBaseMs: 2000, // Initial retry backoff
157
- backoffMaxMs: 30000, // Max retry backoff
158
- jitterFactor: 0.2, // Random jitter ±20%
159
- // Issue provider settings - defaultIssueSource is here, others come from providers
160
- defaultIssueSource: 'github', // 'github' | 'gitlab' | 'jira' | 'azure-devops'
161
- };
162
-
163
- // Cache for merged defaults (base + issue provider settings)
164
- let _defaultSettingsCache = null;
165
-
166
- /**
167
- * Get DEFAULT_SETTINGS with issue provider settings merged in
168
- * Lazy-loaded to avoid circular dependency at module load time
169
- */
170
- function getDefaultSettings() {
171
- if (!_defaultSettingsCache) {
172
- const issueProviderDefaults = getIssueProviderFns().getIssueProviderSettingsDefaults();
173
- _defaultSettingsCache = {
174
- ...DEFAULT_SETTINGS_BASE,
175
- ...issueProviderDefaults,
176
- };
177
- }
178
- return _defaultSettingsCache;
179
- }
180
-
181
- // For backward compatibility, export DEFAULT_SETTINGS as a getter
182
- const DEFAULT_SETTINGS = new Proxy(DEFAULT_SETTINGS_BASE, {
183
- get(target, prop) {
184
- // For known base properties, return from base
185
- if (prop in target) {
186
- return target[prop];
187
- }
188
- // For issue provider settings, get from merged defaults
189
- const merged = getDefaultSettings();
190
- return merged[prop];
191
- },
192
- has(target, prop) {
193
- if (prop in target) return true;
194
- const merged = getDefaultSettings();
195
- return prop in merged;
196
- },
197
- ownKeys() {
198
- const merged = getDefaultSettings();
199
- return Object.keys(merged);
200
- },
201
- getOwnPropertyDescriptor(target, prop) {
202
- const merged = getDefaultSettings();
203
- if (prop in merged) {
204
- return { enumerable: true, configurable: true, value: merged[prop] };
205
- }
206
- return undefined;
207
- },
208
- });
209
-
210
- function mapLegacyModelToLevel(model) {
211
- switch (model) {
212
- case 'haiku':
213
- return 'level1';
214
- case 'sonnet':
215
- return 'level2';
216
- case 'opus':
217
- return 'level3';
218
- default:
219
- return null;
220
- }
221
- }
222
-
223
- function mergeProviderSettings(current, overrides) {
224
- // Ensure current has all providers with their defaults
225
- const providerDefaults = getProviderDefaults();
226
- const merged = {};
227
-
228
- for (const provider of VALID_PROVIDERS) {
229
- merged[provider] = {
230
- ...(providerDefaults[provider] || {}),
231
- ...(current[provider] || {}),
232
- ...(overrides?.[provider] || {}),
233
- };
234
- if (!merged[provider].levelOverrides) {
235
- merged[provider].levelOverrides = {};
236
- }
237
- }
238
- return merged;
239
- }
240
-
241
- function applyLegacyModelBounds(settings) {
242
- if (!settings.providerSettings) return settings;
243
- const claude = settings.providerSettings.claude || {};
244
- const legacyMaxLevel = mapLegacyModelToLevel(settings.maxModel);
245
- const legacyMinLevel = mapLegacyModelToLevel(settings.minModel);
246
-
247
- if (legacyMaxLevel) {
248
- claude.maxLevel = legacyMaxLevel;
249
- }
250
-
251
- if (legacyMinLevel) {
252
- claude.minLevel = legacyMinLevel;
253
- }
254
-
255
- const minRank = LEVEL_RANKS[claude.minLevel] || LEVEL_RANKS.level1;
256
- const maxRank = LEVEL_RANKS[claude.maxLevel] || LEVEL_RANKS.level3;
257
- const defaultRank = LEVEL_RANKS[claude.defaultLevel] || LEVEL_RANKS.level2;
258
-
259
- if (minRank > maxRank) {
260
- claude.minLevel = 'level1';
261
- claude.maxLevel = 'level3';
262
- } else if (defaultRank < minRank) {
263
- claude.defaultLevel = claude.minLevel;
264
- } else if (defaultRank > maxRank) {
265
- claude.defaultLevel = claude.maxLevel;
266
- }
267
-
268
- settings.providerSettings.claude = claude;
269
- return settings;
270
- }
271
-
272
- function normalizeLoadedSettings(parsed) {
273
- const normalized = { ...parsed };
274
- if (!Object.hasOwn(parsed, 'defaultIsolation') && typeof parsed.defaultDocker === 'boolean') {
275
- normalized.defaultIsolation = parsed.defaultDocker ? 'docker' : 'none';
276
- }
277
- delete normalized.defaultDocker;
278
- if (parsed.defaultProvider) {
279
- normalized.defaultProvider = normalizeProviderName(parsed.defaultProvider);
280
- }
281
- if (parsed.providerSettings) {
282
- normalized.providerSettings = normalizeProviderSettings(parsed.providerSettings);
283
- }
284
- if (
285
- normalized.autoCheckUpdates === false ||
286
- typeof normalized.lastUpdateCheckClaim !== 'string' ||
287
- normalized.lastUpdateCheckClaim.trim().length === 0
288
- ) {
289
- normalized.lastUpdateCheckClaim = null;
290
- }
291
- return normalized;
292
- }
293
-
294
- function resolvedDefaultSettings() {
295
- return {
296
- ...DEFAULT_SETTINGS,
297
- providerSettings: mergeProviderSettings(getProviderDefaults()),
298
- };
299
- }
300
-
301
- function mergeLoadedSettings(parsed) {
302
- const normalized = normalizeLoadedSettings(parsed);
303
- const merged = { ...resolvedDefaultSettings(), ...normalized };
304
- merged.defaultProvider =
305
- normalizeProviderName(merged.defaultProvider) || DEFAULT_SETTINGS.defaultProvider;
306
- merged.providerSettings = mergeProviderSettings(
307
- getProviderDefaults(),
308
- normalized.providerSettings
309
- );
310
- if (
311
- merged.autoCheckUpdates === false ||
312
- typeof merged.lastUpdateCheckClaim !== 'string' ||
313
- merged.lastUpdateCheckClaim.trim().length === 0
314
- ) {
315
- merged.lastUpdateCheckClaim = null;
316
- }
317
- return applyLegacyModelBounds(merged);
318
- }
319
-
320
- /**
321
- * Load settings from disk, merging with defaults
322
- */
323
- function loadSettings(options = {}) {
324
- const settingsFile = getSettingsFile();
325
- if (!fs.existsSync(settingsFile)) {
326
- return resolvedDefaultSettings();
327
- }
328
- try {
329
- return mergeLoadedSettings(JSON.parse(fs.readFileSync(settingsFile, 'utf8')));
330
- } catch {
331
- if (!options.silent) console.error('Warning: Could not load settings, using defaults');
332
- return resolvedDefaultSettings();
333
- }
334
- }
335
-
336
- function readSettingsForMutation(settingsFile) {
337
- if (!fs.existsSync(settingsFile)) {
338
- return {
339
- settings: resolvedDefaultSettings(),
340
- requiresClaimInvalidation: false,
341
- requiresRecovery: false,
342
- };
343
- }
344
- let parsed;
345
- try {
346
- parsed = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
347
- } catch (error) {
348
- if (!(error instanceof SyntaxError)) throw error;
349
- return {
350
- settings: resolvedDefaultSettings(),
351
- requiresClaimInvalidation: false,
352
- requiresRecovery: true,
353
- };
354
- }
355
- if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
1
+ "use strict";
2
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
3
+ const storage = require('./settings-storage');
4
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
5
+ const validation = require('./settings-validation');
6
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
7
+ const models = require('./settings-models');
8
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
9
+ const defaults = require('./settings-defaults');
10
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11
+ const providerDefaults = require('./provider-defaults');
12
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
13
+ const { SettingsValidationError } = require('./settings-error');
14
+ const { loadSettings, mutateSettings, getSettingsFile, settingsFileExists } = storage;
15
+ const { validateSetting, coerceValue } = validation;
16
+ const { MODEL_HIERARCHY, VALID_MODELS, validateModelAgainstMax, mapLegacyModelToLevel } = models;
17
+ const { DEFAULT_SETTINGS } = defaults;
18
+ const { clearProviderDefaultsCache } = providerDefaults;
19
+ function getClaudeCommand() {
20
+ const settings = loadSettings();
21
+ const raw = process.env.ZEROSHOT_CLAUDE_COMMAND || settings.claudeCommand || 'claude';
22
+ const parts = raw.trim().split(/\s+/);
356
23
  return {
357
- settings: resolvedDefaultSettings(),
358
- requiresClaimInvalidation: false,
359
- requiresRecovery: true,
24
+ command: parts[0],
25
+ args: parts.slice(1),
360
26
  };
361
- }
362
-
363
- const updatesDisabled = parsed.autoCheckUpdates === false || parsed.updatePolicy === 'off';
364
- return {
365
- settings: mergeLoadedSettings(parsed),
366
- requiresClaimInvalidation: updatesDisabled && parsed.lastUpdateCheckClaim !== null,
367
- requiresRecovery: false,
368
- };
369
- }
370
-
371
- function getAtomicSettingsMode(settingsFile) {
372
- try {
373
- return fs.statSync(settingsFile).mode & 0o600;
374
- } catch (error) {
375
- if (error.code === 'ENOENT') return 0o600;
376
- throw error;
377
- }
378
- }
379
-
380
- function atomicWriteSettings(settingsFile, settings) {
381
- const dir = path.dirname(settingsFile);
382
- const temporaryFile = path.join(
383
- dir,
384
- `.${path.basename(settingsFile)}.${process.pid}.${crypto.randomUUID()}.tmp`
385
- );
386
-
387
- let operationError;
388
- try {
389
- fs.writeFileSync(temporaryFile, JSON.stringify(settings, null, 2), {
390
- encoding: 'utf8',
391
- flag: 'wx',
392
- mode: getAtomicSettingsMode(settingsFile),
393
- });
394
- fs.renameSync(temporaryFile, settingsFile);
395
- } catch (error) {
396
- operationError = error;
397
- }
398
-
399
- try {
400
- fs.unlinkSync(temporaryFile);
401
- } catch (error) {
402
- if (error.code !== 'ENOENT' && !operationError) operationError = error;
403
- }
404
- if (operationError) throw operationError;
405
- }
406
-
407
- function acquireSettingsLock(settingsFile, lockfilePath, timeoutMs) {
408
- const deadline = Date.now() + timeoutMs;
409
- while (true) {
410
- try {
411
- return lockfile.lockSync(settingsFile, {
412
- lockfilePath,
413
- realpath: false,
414
- stale: SETTINGS_LOCK_STALE_MS,
415
- });
416
- } catch (error) {
417
- if (error.code !== 'ELOCKED' || Date.now() >= deadline) throw error;
418
- Atomics.wait(SETTINGS_LOCK_SLEEP, 0, 0, SETTINGS_LOCK_RETRY_MS);
419
- }
420
- }
421
- }
422
-
423
- /**
424
- * Atomically mutate global settings under the process-shared settings lock.
425
- * The mutator receives only the freshly re-read, normalized state.
426
- *
427
- * @param {(settings: object) => any} mutator
428
- * @param {object} [options]
429
- * @returns {any} the mutator's return value
430
- */
431
- function mutateSettings(mutator, options = {}) {
432
- if (typeof mutator !== 'function') {
433
- throw new TypeError('Global settings mutation requires a callback');
434
- }
435
-
436
- const settingsFile = getSettingsFile();
437
- const dir = path.dirname(settingsFile);
438
- const lockfilePath = `${settingsFile}.lock`;
439
- let release;
440
- let result;
441
- let mutationError;
442
-
443
- try {
444
- fs.mkdirSync(dir, { recursive: true });
445
- release = acquireSettingsLock(
446
- settingsFile,
447
- lockfilePath,
448
- options.lockTimeoutMs ?? SETTINGS_LOCK_TIMEOUT_MS
449
- );
450
-
451
- const { settings, requiresClaimInvalidation, requiresRecovery } =
452
- readSettingsForMutation(settingsFile);
453
- const before = JSON.stringify(settings);
454
- result = mutator(settings);
455
- if (result && typeof result.then === 'function') {
456
- throw new TypeError('Global settings mutations must be synchronous');
457
- }
458
- if (settings.autoCheckUpdates === false || settings.updatePolicy === 'off') {
459
- settings.lastUpdateCheckClaim = null;
460
- }
461
- if (requiresRecovery || requiresClaimInvalidation || JSON.stringify(settings) !== before) {
462
- atomicWriteSettings(settingsFile, settings);
463
- }
464
- } catch (error) {
465
- mutationError =
466
- error instanceof SettingsValidationError
467
- ? error
468
- : new Error(`Unable to persist global settings: ${error.message}`, { cause: error });
469
- } finally {
470
- if (release) {
471
- try {
472
- release();
473
- } catch (error) {
474
- if (!mutationError) {
475
- mutationError = new Error(`Unable to release global settings lock: ${error.message}`, {
476
- cause: error,
477
- });
478
- }
479
- }
480
- }
481
- }
482
- if (mutationError) throw mutationError;
483
- return result;
484
- }
485
-
486
- /**
487
- * Validate claudeCommand setting
488
- * @returns {string|null} Error message if invalid, null if valid
489
- */
490
- function validateClaudeCommand(value) {
491
- if (typeof value !== 'string') {
492
- return 'claudeCommand must be a string';
493
- }
494
- if (value.trim().length === 0) {
495
- return 'claudeCommand cannot be empty';
496
- }
497
- return null;
498
- }
499
-
500
- /**
501
- * Validate providerSettings structure by delegating to provider implementations
502
- * @returns {string|null} Error message if invalid, null if valid
503
- */
504
- function validateProviderSettings(value) {
505
- const normalizedSettings = normalizeProviderSettings(value);
506
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
507
- return 'providerSettings must be an object';
508
- }
509
-
510
- // Lazy require to avoid circular dependency
511
- const { getProvider } = require('../src/providers');
512
-
513
- for (const [providerName, settings] of Object.entries(normalizedSettings || {})) {
514
- if (!VALID_PROVIDERS.includes(providerName)) {
515
- return `Unknown provider in providerSettings: ${providerName}. Valid providers: ${VALID_PROVIDERS.join(', ')}`;
516
- }
517
-
518
- // Delegate validation to the provider
519
- try {
520
- const provider = getProvider(providerName);
521
- const error = provider.validateSettings(settings);
522
- if (error) return error;
523
- } catch (err) {
524
- return `Failed to validate ${providerName} settings: ${err.message}`;
525
- }
526
- }
527
-
528
- return null;
529
- }
530
-
531
- /**
532
- * Validate a setting value
533
- * @returns {string|null} Error message if invalid, null if valid
534
- */
535
- function validateSetting(key, value) {
536
- if (!(key in DEFAULT_SETTINGS)) {
537
- return `Unknown setting: ${key}`;
538
- }
539
-
540
- if (
541
- [
542
- 'maxRetries',
543
- 'maxRestartAttempts',
544
- 'maxTotalRestarts',
545
- 'staleWarningsBeforeKill',
546
- 'backoffBaseMs',
547
- 'backoffMaxMs',
548
- ].includes(key)
549
- ) {
550
- if (!Number.isFinite(value)) {
551
- return `${key} must be a number`;
552
- }
553
- if (!Number.isInteger(value)) {
554
- return `${key} must be an integer`;
555
- }
556
- const min = ['backoffBaseMs', 'backoffMaxMs'].includes(key) ? 0 : 1;
557
- if (value < min) {
558
- return `${key} must be >= ${min}`;
559
- }
560
- }
561
-
562
- if (key === 'jitterFactor') {
563
- if (!Number.isFinite(value)) {
564
- return 'jitterFactor must be a number';
565
- }
566
- if (value < 0 || value > 1) {
567
- return 'jitterFactor must be between 0 and 1';
568
- }
569
- }
570
-
571
- if (key === 'maxModel' && !VALID_MODELS.includes(value)) {
572
- return `Invalid model: ${value}. Valid models: ${VALID_MODELS.join(', ')}`;
573
- }
574
-
575
- if (key === 'minModel' && value !== null && !VALID_MODELS.includes(value)) {
576
- return `Invalid model: ${value}. Valid models: ${VALID_MODELS.join(', ')}, null`;
577
- }
578
-
579
- if (key === 'logLevel' && !['quiet', 'normal', 'verbose'].includes(value)) {
580
- return `Invalid log level: ${value}. Valid levels: quiet, normal, verbose`;
581
- }
582
-
583
- if (key === 'defaultDelivery' && !['none', 'pr', 'ship'].includes(value)) {
584
- return `Invalid defaultDelivery: ${value}. Valid: none, pr, ship`;
585
- }
586
- if (key === 'defaultIsolation' && !['none', 'worktree', 'docker'].includes(value)) {
587
- return `Invalid defaultIsolation: ${value}. Valid: none, worktree, docker`;
588
- }
589
-
590
- if (key === 'claudeCommand') {
591
- return validateClaudeCommand(value);
592
- }
593
-
594
- if (key === 'defaultProvider') {
595
- const normalized = normalizeProviderName(value);
596
- if (!VALID_PROVIDERS.includes(normalized)) {
597
- return `Invalid provider: ${value}. Valid providers: ${VALID_PROVIDERS.join(', ')}`;
598
- }
599
- }
600
-
601
- if (key === 'providerSettings') {
602
- return validateProviderSettings(value);
603
- }
604
-
605
- if (key === 'dockerMounts') {
606
- return validateMountConfig(value);
607
- }
608
-
609
- if (key === 'dockerEnvPassthrough') {
610
- return validateEnvPassthrough(value);
611
- }
612
-
613
- // Issue source settings validation (grouped for maintainability)
614
- const issueSourceError = validateIssueSourceSetting(key, value);
615
- if (issueSourceError !== undefined) {
616
- return issueSourceError;
617
- }
618
-
619
- return null;
620
- }
621
-
622
- /**
623
- * Validate issue source related settings
624
- * Delegates to issue providers for provider-specific settings
625
- * @param {string} key - Setting key
626
- * @param {any} value - Setting value
627
- * @returns {string|null|undefined} Error message, null if valid, undefined if not an issue source setting
628
- */
629
- function validateIssueSourceSetting(key, value) {
630
- // defaultIssueSource is handled here (not provider-specific)
631
- if (key === 'defaultIssueSource') {
632
- const { listProviders } = getIssueProviderFns();
633
- const validSources = listProviders();
634
- if (!validSources.includes(value)) {
635
- return `Invalid issue source: ${value}. Valid: ${validSources.join(', ')}`;
636
- }
637
- return null;
638
- }
639
-
640
- // Delegate to issue providers for provider-specific settings
641
- const { validateIssueProviderSetting } = getIssueProviderFns();
642
- return validateIssueProviderSetting(key, value);
643
- }
644
-
645
- /**
646
- * Coerce value to correct type based on default value type
647
- */
648
- function coerceValue(key, value) {
649
- const defaultValue = DEFAULT_SETTINGS[key];
650
-
651
- // Handle null values for minModel
652
- if (key === 'minModel' && (value === 'null' || value === null)) {
653
- return null;
654
- }
655
-
656
- if (typeof defaultValue === 'boolean') {
657
- return value === 'true' || value === '1' || value === 'yes' || value === true;
658
- }
659
-
660
- if (typeof defaultValue === 'number') {
661
- const parsed = parseFloat(value);
662
- if (isNaN(parsed)) {
663
- throw new Error(`Invalid number: ${value}`);
664
- }
665
- return parsed;
666
- }
667
-
668
- // Handle array settings (dockerMounts, dockerEnvPassthrough)
669
- if (Array.isArray(defaultValue)) {
670
- if (typeof value === 'string') {
671
- try {
672
- const parsed = JSON.parse(value);
673
- if (!Array.isArray(parsed)) {
674
- throw new Error(`${key} must be an array`);
675
- }
676
- return parsed;
677
- } catch (e) {
678
- if (e instanceof SyntaxError) {
679
- throw new Error(`Invalid JSON for ${key}: ${value}`);
680
- }
681
- throw e;
682
- }
683
- }
684
- return value;
685
- }
686
-
687
- if (key === 'providerSettings') {
688
- if (typeof value === 'string') {
689
- try {
690
- return normalizeProviderSettings(JSON.parse(value));
691
- } catch {
692
- throw new Error(`Invalid JSON for providerSettings: ${value}`);
693
- }
694
- }
695
- return normalizeProviderSettings(value);
696
- }
697
-
698
- if (key === 'defaultProvider') {
699
- return normalizeProviderName(value);
700
- }
701
-
702
- return value;
703
- }
704
-
705
- /**
706
- * Get parsed Claude command from settings/env
707
- * Supports space-separated commands like 'ccr code'
708
- * @returns {{ command: string, args: string[] }}
709
- */
710
- function getClaudeCommand() {
711
- const settings = loadSettings();
712
- const raw = process.env.ZEROSHOT_CLAUDE_COMMAND || settings.claudeCommand || 'claude';
713
- const parts = raw.trim().split(/\s+/);
714
- return {
715
- command: parts[0],
716
- args: parts.slice(1),
717
- };
718
27
  }
719
-
720
28
  module.exports = {
721
- loadSettings,
722
- mutateSettings,
723
- validateSetting,
724
- coerceValue,
725
- SettingsValidationError,
726
- DEFAULT_SETTINGS,
727
- getSettingsFile,
728
- settingsFileExists,
729
- getClaudeCommand,
730
- // Model validation exports
731
- MODEL_HIERARCHY,
732
- VALID_MODELS,
733
- validateModelAgainstMax,
734
- // Provider defaults exports
735
- clearProviderDefaultsCache,
736
- mapLegacyModelToLevel,
737
- // Backward compatibility: SETTINGS_FILE as getter (reads env var dynamically)
738
- get SETTINGS_FILE() {
739
- return getSettingsFile();
740
- },
29
+ loadSettings,
30
+ mutateSettings,
31
+ validateSetting,
32
+ coerceValue,
33
+ SettingsValidationError,
34
+ DEFAULT_SETTINGS,
35
+ getSettingsFile,
36
+ settingsFileExists,
37
+ getClaudeCommand,
38
+ MODEL_HIERARCHY,
39
+ VALID_MODELS,
40
+ validateModelAgainstMax,
41
+ clearProviderDefaultsCache,
42
+ mapLegacyModelToLevel,
43
+ get SETTINGS_FILE() {
44
+ return getSettingsFile();
45
+ },
741
46
  };