@the-open-engine/zeroshot 6.38.0 → 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 (381) 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/agent-cli-provider/adapters/codex.d.ts.map +1 -1
  13. package/lib/agent-cli-provider/adapters/codex.js +3 -1
  14. package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
  15. package/lib/cluster/generated/protocol-schema.cjs +1 -1
  16. package/lib/cluster/generated/protocol-schema.mjs +1 -1
  17. package/lib/cluster/generated/protocol.d.cts +8 -0
  18. package/lib/cluster/generated/protocol.d.mts +8 -0
  19. package/lib/cluster/generated/protocol.d.ts +8 -0
  20. package/lib/clusters-registry.d.ts +23 -0
  21. package/lib/clusters-registry.js +17 -29
  22. package/lib/completion.d.ts +12 -0
  23. package/lib/completion.js +84 -94
  24. package/lib/compose-utils.d.ts +32 -0
  25. package/lib/compose-utils.js +57 -72
  26. package/lib/detached-startup.d.ts +83 -0
  27. package/lib/detached-startup.js +231 -302
  28. package/lib/docker-config.d.ts +43 -0
  29. package/lib/docker-config.js +150 -325
  30. package/lib/docker-env-auth.d.ts +17 -0
  31. package/lib/docker-env-auth.js +75 -0
  32. package/lib/git-remote-utils.d.ts +48 -0
  33. package/lib/git-remote-utils.js +147 -213
  34. package/lib/id-detector.d.ts +15 -0
  35. package/lib/id-detector.js +37 -41
  36. package/lib/path-check.d.ts +30 -0
  37. package/lib/path-check.js +42 -45
  38. package/lib/process-liveness.d.ts +16 -0
  39. package/lib/process-liveness.js +19 -12
  40. package/lib/provider-credential-path.d.ts +16 -0
  41. package/lib/provider-credential-path.js +31 -29
  42. package/lib/provider-defaults.d.ts +30 -0
  43. package/lib/provider-defaults.js +47 -42
  44. package/lib/provider-detection.d.ts +11 -0
  45. package/lib/provider-detection.js +91 -54
  46. package/lib/provider-names.d.ts +38 -0
  47. package/lib/provider-names.js +56 -64
  48. package/lib/repo-settings.d.ts +28 -0
  49. package/lib/repo-settings.js +83 -59
  50. package/lib/run-mode.d.ts +21 -0
  51. package/lib/run-mode.js +21 -20
  52. package/lib/run-plan.d.ts +28 -0
  53. package/lib/run-plan.js +17 -16
  54. package/lib/settings/claude-auth.d.ts +35 -0
  55. package/lib/settings/claude-auth.js +30 -47
  56. package/lib/settings-defaults.d.ts +20 -0
  57. package/lib/settings-defaults.js +149 -0
  58. package/lib/settings-error.d.ts +7 -0
  59. package/lib/settings-error.js +8 -0
  60. package/lib/settings-issue-providers.d.ts +10 -0
  61. package/lib/settings-issue-providers.js +12 -0
  62. package/lib/settings-models.d.ts +22 -0
  63. package/lib/settings-models.js +93 -0
  64. package/lib/settings-storage.d.ts +23 -0
  65. package/lib/settings-storage.js +205 -0
  66. package/lib/settings-validation.d.ts +7 -0
  67. package/lib/settings-validation.js +182 -0
  68. package/lib/settings.d.ts +29 -0
  69. package/lib/settings.js +41 -736
  70. package/lib/setup-apply-types.d.ts +66 -0
  71. package/lib/setup-apply-types.js +2 -0
  72. package/lib/setup-apply.d.ts +39 -0
  73. package/lib/setup-apply.js +267 -264
  74. package/lib/setup-journal.d.ts +41 -0
  75. package/lib/setup-journal.js +119 -76
  76. package/lib/setup-plan-types.d.ts +102 -0
  77. package/lib/setup-plan-types.js +2 -0
  78. package/lib/setup-plan.d.ts +30 -0
  79. package/lib/setup-plan.js +300 -324
  80. package/lib/setup-undo.d.ts +44 -0
  81. package/lib/setup-undo.js +61 -74
  82. package/lib/start-cluster-config.d.ts +26 -0
  83. package/lib/start-cluster-config.js +56 -0
  84. package/lib/start-cluster-environment.d.ts +40 -0
  85. package/lib/start-cluster-environment.js +134 -0
  86. package/lib/start-cluster-run-options.d.ts +53 -0
  87. package/lib/start-cluster-run-options.js +147 -0
  88. package/lib/start-cluster.d.ts +64 -0
  89. package/lib/start-cluster.js +98 -454
  90. package/lib/stream-json-parser.d.ts +17 -0
  91. package/lib/stream-json-parser.js +51 -52
  92. package/npm-shrinkwrap.json +2 -2
  93. package/package.json +12 -6
  94. package/scripts/postinstall.js +27 -0
  95. package/scripts/rust-distribution.js +23 -14
  96. package/src/agent/agent-command-proofs-context.js +35 -45
  97. package/src/agent/agent-command-proofs-context.ts +87 -0
  98. package/src/agent/agent-config-settings.js +19 -0
  99. package/src/agent/agent-config-settings.ts +41 -0
  100. package/src/agent/agent-config.js +122 -231
  101. package/src/agent/agent-config.ts +285 -0
  102. package/src/agent/agent-context-builder.js +38 -273
  103. package/src/agent/agent-context-builder.ts +51 -0
  104. package/src/agent/agent-context-environment-sections.js +105 -0
  105. package/src/agent/agent-context-environment-sections.ts +134 -0
  106. package/src/agent/agent-context-packs.js +165 -0
  107. package/src/agent/agent-context-packs.ts +296 -0
  108. package/src/agent/agent-context-prompt-sections.js +100 -0
  109. package/src/agent/agent-context-prompt-sections.ts +148 -0
  110. package/src/agent/agent-context-sections.js +25 -334
  111. package/src/agent/agent-context-sections.ts +36 -0
  112. package/src/agent/agent-context-sources.js +96 -149
  113. package/src/agent/agent-context-sources.ts +253 -0
  114. package/src/agent/agent-context-types.js +2 -0
  115. package/src/agent/agent-context-types.ts +132 -0
  116. package/src/agent/agent-context-validation-sections.js +73 -0
  117. package/src/agent/agent-context-validation-sections.ts +129 -0
  118. package/src/agent/agent-input-injector.js +99 -118
  119. package/src/agent/agent-input-injector.ts +195 -0
  120. package/src/agent/agent-liveness-poll.js +74 -78
  121. package/src/agent/agent-liveness-poll.ts +163 -0
  122. package/src/agent/agent-quality-gate-schema.js +77 -86
  123. package/src/agent/agent-quality-gate-schema.ts +135 -0
  124. package/src/agent/agent-quality-gates-context.js +44 -50
  125. package/src/agent/agent-quality-gates-context.ts +91 -0
  126. package/src/agent/agent-stuck-detector.js +171 -239
  127. package/src/agent/agent-stuck-detector.ts +288 -0
  128. package/src/agent/agent-trigger-evaluator.js +29 -46
  129. package/src/agent/agent-trigger-evaluator.ts +98 -0
  130. package/src/agent/context-metrics.js +110 -141
  131. package/src/agent/context-metrics.ts +261 -0
  132. package/src/agent/context-pack-builder.js +72 -358
  133. package/src/agent/context-pack-builder.ts +105 -0
  134. package/src/agent/context-pack-core.js +70 -0
  135. package/src/agent/context-pack-core.ts +96 -0
  136. package/src/agent/context-pack-selection.js +119 -0
  137. package/src/agent/context-pack-selection.ts +187 -0
  138. package/src/agent/context-pack-truncation.js +139 -0
  139. package/src/agent/context-pack-truncation.ts +208 -0
  140. package/src/agent/context-pack-types.js +2 -0
  141. package/src/agent/context-pack-types.ts +98 -0
  142. package/src/agent/context-replay-policy.js +30 -39
  143. package/src/agent/context-replay-policy.ts +71 -0
  144. package/src/agent/critical-agent-policy.js +6 -10
  145. package/src/agent/critical-agent-policy.ts +20 -0
  146. package/src/agent/guidance-queue.js +74 -87
  147. package/src/agent/guidance-queue.ts +166 -0
  148. package/src/agent/output-extraction-error-detail.js +45 -0
  149. package/src/agent/output-extraction-error-detail.ts +49 -0
  150. package/src/agent/output-extraction-failures.js +125 -0
  151. package/src/agent/output-extraction-failures.ts +149 -0
  152. package/src/agent/output-extraction-json.js +234 -0
  153. package/src/agent/output-extraction-json.ts +235 -0
  154. package/src/agent/output-extraction-pi.js +124 -0
  155. package/src/agent/output-extraction-pi.ts +137 -0
  156. package/src/agent/output-extraction-types.js +2 -0
  157. package/src/agent/output-extraction-types.ts +49 -0
  158. package/src/agent/output-extraction.js +17 -617
  159. package/src/agent/output-extraction.ts +30 -0
  160. package/src/agent/output-reformatter-errors.js +83 -0
  161. package/src/agent/output-reformatter-errors.ts +106 -0
  162. package/src/agent/output-reformatter-types.js +2 -0
  163. package/src/agent/output-reformatter-types.ts +86 -0
  164. package/src/agent/output-reformatter.js +140 -185
  165. package/src/agent/output-reformatter.ts +238 -0
  166. package/src/agent/pi-terminal-lifecycle-types.js +2 -0
  167. package/src/agent/pi-terminal-lifecycle-types.ts +41 -0
  168. package/src/agent/pi-terminal-lifecycle.js +186 -206
  169. package/src/agent/pi-terminal-lifecycle.ts +297 -0
  170. package/src/agent/provider-control-plane.js +22 -21
  171. package/src/agent/provider-control-plane.ts +52 -0
  172. package/src/agent/provider-terminal-failure-types.js +2 -0
  173. package/src/agent/provider-terminal-failure-types.ts +60 -0
  174. package/src/agent/provider-terminal-failure.js +194 -191
  175. package/src/agent/provider-terminal-failure.ts +262 -0
  176. package/src/agent/rate-limit-backoff.js +31 -74
  177. package/src/agent/rate-limit-backoff.ts +71 -0
  178. package/src/agent/schema-utils.js +85 -125
  179. package/src/agent/schema-utils.ts +148 -0
  180. package/src/agent/structured-output-error.js +34 -33
  181. package/src/agent/structured-output-error.ts +105 -0
  182. package/src/agent/validation-platform.js +27 -27
  183. package/src/agent/validation-platform.ts +49 -0
  184. package/src/agent-cli-provider/adapters/codex.ts +3 -1
  185. package/src/attach/attach-client.js +232 -405
  186. package/src/attach/attach-client.ts +300 -0
  187. package/src/attach/attach-server-cleanup.js +43 -0
  188. package/src/attach/attach-server-cleanup.ts +39 -0
  189. package/src/attach/attach-server-clients.js +117 -0
  190. package/src/attach/attach-server-clients.ts +122 -0
  191. package/src/attach/attach-server-events.js +38 -0
  192. package/src/attach/attach-server-events.ts +36 -0
  193. package/src/attach/attach-server-pty.js +45 -0
  194. package/src/attach/attach-server-pty.ts +64 -0
  195. package/src/attach/attach-server-runtime.js +73 -0
  196. package/src/attach/attach-server-runtime.ts +62 -0
  197. package/src/attach/attach-server-socket.js +28 -0
  198. package/src/attach/attach-server-socket.ts +23 -0
  199. package/src/attach/attach-server-types.js +2 -0
  200. package/src/attach/attach-server-types.ts +82 -0
  201. package/src/attach/attach-server.js +106 -523
  202. package/src/attach/attach-server.ts +158 -0
  203. package/src/attach/index.js +18 -35
  204. package/src/attach/index.ts +18 -0
  205. package/src/attach/protocol.js +95 -184
  206. package/src/attach/protocol.ts +181 -0
  207. package/src/attach/ring-buffer.js +76 -102
  208. package/src/attach/ring-buffer.ts +109 -0
  209. package/src/attach/send-input.js +64 -75
  210. package/src/attach/send-input.ts +94 -0
  211. package/src/attach/socket-discovery.js +164 -218
  212. package/src/attach/socket-discovery.ts +230 -0
  213. package/src/attach/socket-paths.js +42 -61
  214. package/src/attach/socket-paths.ts +81 -0
  215. package/src/claude-credentials.js +51 -54
  216. package/src/claude-credentials.ts +89 -0
  217. package/src/cluster/generated/protocol-schema.ts +1 -1
  218. package/src/cluster/generated/protocol.ts +2 -1
  219. package/src/command-proofs.js +110 -141
  220. package/src/command-proofs.ts +216 -0
  221. package/src/config-router.js +81 -73
  222. package/src/config-router.ts +131 -0
  223. package/src/copy-worker.js +53 -29
  224. package/src/copy-worker.ts +75 -0
  225. package/src/darwin-keychain-boundary.js +80 -111
  226. package/src/darwin-keychain-boundary.ts +148 -0
  227. package/src/guidance-topics.js +4 -5
  228. package/src/guidance-topics.ts +10 -0
  229. package/src/input-helpers.js +33 -54
  230. package/src/input-helpers.ts +58 -0
  231. package/src/ledger-sequence.js +45 -48
  232. package/src/ledger-sequence.ts +63 -0
  233. package/src/legacy-lib/clusters-registry.ts +40 -0
  234. package/src/legacy-lib/completion.ts +148 -0
  235. package/src/legacy-lib/compose-utils.ts +102 -0
  236. package/src/legacy-lib/detached-startup.ts +449 -0
  237. package/src/legacy-lib/docker-config.ts +282 -0
  238. package/src/legacy-lib/docker-env-auth.ts +116 -0
  239. package/src/legacy-lib/git-remote-utils.ts +244 -0
  240. package/src/legacy-lib/id-detector.ts +57 -0
  241. package/src/legacy-lib/js-yaml.d.ts +3 -0
  242. package/src/legacy-lib/omelette.d.ts +15 -0
  243. package/src/legacy-lib/path-check.ts +89 -0
  244. package/src/legacy-lib/process-liveness.ts +36 -0
  245. package/src/legacy-lib/provider-credential-path.ts +57 -0
  246. package/src/legacy-lib/provider-defaults.ts +114 -0
  247. package/src/legacy-lib/provider-detection.ts +67 -0
  248. package/src/legacy-lib/provider-names.ts +126 -0
  249. package/src/legacy-lib/repo-settings.ts +88 -0
  250. package/src/legacy-lib/run-mode.ts +58 -0
  251. package/src/legacy-lib/run-plan.ts +49 -0
  252. package/src/legacy-lib/settings/claude-auth.ts +80 -0
  253. package/src/legacy-lib/settings-defaults.ts +212 -0
  254. package/src/legacy-lib/settings-error.ts +8 -0
  255. package/src/legacy-lib/settings-issue-providers.ts +18 -0
  256. package/src/legacy-lib/settings-models.ts +124 -0
  257. package/src/legacy-lib/settings-storage.ts +277 -0
  258. package/src/legacy-lib/settings-validation.ts +220 -0
  259. package/src/legacy-lib/settings.ts +87 -0
  260. package/src/legacy-lib/setup-apply-types.ts +70 -0
  261. package/src/legacy-lib/setup-apply.ts +456 -0
  262. package/src/legacy-lib/setup-journal.ts +185 -0
  263. package/src/legacy-lib/setup-plan-types.ts +96 -0
  264. package/src/legacy-lib/setup-plan.ts +500 -0
  265. package/src/legacy-lib/setup-undo.ts +142 -0
  266. package/src/legacy-lib/start-cluster-config.ts +124 -0
  267. package/src/legacy-lib/start-cluster-environment.ts +165 -0
  268. package/src/legacy-lib/start-cluster-run-options.ts +279 -0
  269. package/src/legacy-lib/start-cluster.ts +237 -0
  270. package/src/legacy-lib/stream-json-parser.ts +88 -0
  271. package/src/message-buffer.js +55 -71
  272. package/src/message-buffer.ts +107 -0
  273. package/src/message-bus-bridge.js +92 -133
  274. package/src/message-bus-bridge.ts +149 -0
  275. package/src/name-generator-shared.js +220 -0
  276. package/src/name-generator-shared.ts +231 -0
  277. package/src/name-generator.js +8 -217
  278. package/src/name-generator.ts +27 -0
  279. package/src/omp-blob-root.js +83 -64
  280. package/src/omp-blob-root.ts +116 -0
  281. package/src/omp-config-overlay.js +65 -38
  282. package/src/omp-config-overlay.ts +110 -0
  283. package/src/omp-execution-fingerprint.js +38 -43
  284. package/src/omp-execution-fingerprint.ts +92 -0
  285. package/src/omp-session-limits.js +10 -15
  286. package/src/omp-session-limits.ts +53 -0
  287. package/src/omp-session-partition.js +2 -2
  288. package/src/omp-session-verifier.js +1 -1
  289. package/src/orchestrator.js +19 -4
  290. package/src/providers/anthropic/index.js +3 -1
  291. package/src/providers/anthropic/index.ts +13 -0
  292. package/src/providers/capabilities.js +19 -27
  293. package/src/providers/capabilities.ts +44 -0
  294. package/src/providers/google/index.js +3 -1
  295. package/src/providers/google/index.ts +13 -0
  296. package/src/providers/openai/index.js +3 -1
  297. package/src/providers/openai/index.ts +13 -0
  298. package/src/providers/opencode/index.js +3 -1
  299. package/src/providers/opencode/index.ts +13 -0
  300. package/src/quality-gates.js +67 -127
  301. package/src/quality-gates.ts +149 -0
  302. package/src/repo-settings-access.js +40 -0
  303. package/src/repo-settings-access.ts +64 -0
  304. package/src/schemas/sub-cluster.js +127 -183
  305. package/src/schemas/sub-cluster.ts +238 -0
  306. package/src/state-snapshot-normalization.js +224 -0
  307. package/src/state-snapshot-normalization.ts +222 -0
  308. package/src/state-snapshot.js +192 -359
  309. package/src/state-snapshot.ts +291 -0
  310. package/src/state-snapshotter.js +118 -130
  311. package/src/state-snapshotter.ts +167 -0
  312. package/src/sub-cluster-wrapper.js +4 -13
  313. package/src/task-run-model-args.js +87 -124
  314. package/src/task-run-model-args.ts +204 -0
  315. package/src/task-runner.js +5 -25
  316. package/src/task-runner.ts +28 -0
  317. package/src/task-startup-error.js +73 -58
  318. package/src/task-startup-error.ts +126 -0
  319. package/src/template-validation/consensus-gate-contracts.js +2 -0
  320. package/src/template-validation/consensus-gate-contracts.ts +70 -0
  321. package/src/template-validation/consensus-gate-scenarios.js +76 -0
  322. package/src/template-validation/consensus-gate-scenarios.ts +130 -0
  323. package/src/template-validation/consensus-gate-stage.js +34 -0
  324. package/src/template-validation/consensus-gate-stage.ts +56 -0
  325. package/src/template-validation/random-topology-contracts.js +2 -0
  326. package/src/template-validation/random-topology-contracts.ts +54 -0
  327. package/src/template-validation/random-topology-dispatch.js +25 -0
  328. package/src/template-validation/random-topology-dispatch.ts +33 -0
  329. package/src/template-validation/random-topology-message-loop.js +53 -0
  330. package/src/template-validation/random-topology-message-loop.ts +73 -0
  331. package/src/template-validation/random-topology-operations.js +79 -0
  332. package/src/template-validation/random-topology-operations.ts +101 -0
  333. package/src/template-validation/random-topology-runtime.js +25 -0
  334. package/src/template-validation/random-topology-runtime.ts +43 -0
  335. package/src/template-validation/random-topology-scenario.js +52 -0
  336. package/src/template-validation/random-topology-scenario.ts +57 -0
  337. package/src/template-validation/random-topology-schema.js +159 -0
  338. package/src/template-validation/random-topology-schema.ts +190 -0
  339. package/src/template-validation/random-topology-state.js +28 -0
  340. package/src/template-validation/random-topology-state.ts +30 -0
  341. package/src/template-validation/random-topology-trigger-evaluation.js +45 -0
  342. package/src/template-validation/random-topology-trigger-evaluation.ts +65 -0
  343. package/src/template-validation/random-topology-trigger.js +75 -0
  344. package/src/template-validation/random-topology-trigger.ts +88 -0
  345. package/src/template-validation/report-formatter.js +40 -46
  346. package/src/template-validation/report-formatter.ts +101 -0
  347. package/src/template-validation/simulate-consensus-gates.js +111 -302
  348. package/src/template-validation/simulate-consensus-gates.ts +196 -0
  349. package/src/template-validation/simulate-random-topology.js +26 -519
  350. package/src/template-validation/simulate-random-topology.ts +57 -0
  351. package/src/template-validation/simulate-two-stage-validation.js +41 -265
  352. package/src/template-validation/simulate-two-stage-validation.ts +55 -0
  353. package/src/template-validation/simulation-agent-runtime.js +25 -0
  354. package/src/template-validation/simulation-agent-runtime.ts +40 -0
  355. package/src/template-validation/simulation-agent.js +29 -0
  356. package/src/template-validation/simulation-agent.ts +73 -0
  357. package/src/template-validation/simulation-runtime.js +34 -0
  358. package/src/template-validation/simulation-runtime.ts +72 -0
  359. package/src/template-validation/two-stage-contracts.js +2 -0
  360. package/src/template-validation/two-stage-contracts.ts +41 -0
  361. package/src/template-validation/two-stage-inputs.js +21 -0
  362. package/src/template-validation/two-stage-inputs.ts +29 -0
  363. package/src/template-validation/two-stage-results.js +30 -0
  364. package/src/template-validation/two-stage-results.ts +40 -0
  365. package/src/template-validation/two-stage-scenario.js +86 -0
  366. package/src/template-validation/two-stage-scenario.ts +120 -0
  367. package/src/worktree-claude-config.js +182 -220
  368. package/src/worktree-claude-config.ts +272 -0
  369. package/src/worktree-tooling-env.js +103 -125
  370. package/src/worktree-tooling-env.ts +142 -0
  371. package/task-lib/completion.js +2 -3
  372. package/task-lib/completion.ts +9 -0
  373. package/task-lib/config.js +1 -4
  374. package/task-lib/config.ts +11 -0
  375. package/task-lib/name-generator.js +4 -226
  376. package/task-lib/name-generator.ts +11 -0
  377. package/task-lib/omp-session-cleanup.js +1 -1
  378. package/task-lib/omp-storage-root.js +8 -12
  379. package/task-lib/omp-storage-root.ts +62 -0
  380. package/task-lib/process-termination.js +143 -166
  381. 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
  };