@the-open-engine/zeroshot 5.4.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 (295) hide show
  1. package/CHANGELOG.md +539 -0
  2. package/LICENSE +21 -0
  3. package/README.md +508 -0
  4. package/cli/commands/inspect-render.js +135 -0
  5. package/cli/commands/inspect.js +294 -0
  6. package/cli/commands/providers.js +149 -0
  7. package/cli/index.js +5431 -0
  8. package/cli/lib/first-run.js +211 -0
  9. package/cli/lib/update-checker.js +281 -0
  10. package/cli/message-formatter-utils.js +75 -0
  11. package/cli/message-formatters-normal.js +275 -0
  12. package/cli/message-formatters-watch.js +185 -0
  13. package/cluster-templates/base-templates/debug-workflow.json +422 -0
  14. package/cluster-templates/base-templates/full-workflow.json +727 -0
  15. package/cluster-templates/base-templates/heavy-validation.json +272 -0
  16. package/cluster-templates/base-templates/quick-validation.json +285 -0
  17. package/cluster-templates/base-templates/single-worker.json +71 -0
  18. package/cluster-templates/base-templates/worker-validator.json +230 -0
  19. package/cluster-templates/conductor-bootstrap.json +130 -0
  20. package/docker/zeroshot-cluster/Dockerfile +153 -0
  21. package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
  22. package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
  23. package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
  24. package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
  25. package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
  26. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
  27. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
  28. package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
  29. package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
  30. package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
  31. package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
  32. package/lib/agent-cli-provider/adapters/claude.js +181 -0
  33. package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
  34. package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
  35. package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
  36. package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
  37. package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
  38. package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
  39. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
  40. package/lib/agent-cli-provider/adapters/codex.js +151 -0
  41. package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
  42. package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
  43. package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
  44. package/lib/agent-cli-provider/adapters/common.js +71 -0
  45. package/lib/agent-cli-provider/adapters/common.js.map +1 -0
  46. package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
  47. package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
  48. package/lib/agent-cli-provider/adapters/gemini.js +176 -0
  49. package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
  50. package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
  51. package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
  52. package/lib/agent-cli-provider/adapters/index.js +95 -0
  53. package/lib/agent-cli-provider/adapters/index.js.map +1 -0
  54. package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
  55. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
  56. package/lib/agent-cli-provider/adapters/opencode.js +228 -0
  57. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
  58. package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
  59. package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
  60. package/lib/agent-cli-provider/contract-actions.js +140 -0
  61. package/lib/agent-cli-provider/contract-actions.js.map +1 -0
  62. package/lib/agent-cli-provider/contract-env.d.ts +11 -0
  63. package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
  64. package/lib/agent-cli-provider/contract-env.js +113 -0
  65. package/lib/agent-cli-provider/contract-env.js.map +1 -0
  66. package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
  67. package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
  68. package/lib/agent-cli-provider/contract-envelope.js +52 -0
  69. package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
  70. package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
  71. package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
  72. package/lib/agent-cli-provider/contract-errors.js +71 -0
  73. package/lib/agent-cli-provider/contract-errors.js.map +1 -0
  74. package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
  75. package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
  76. package/lib/agent-cli-provider/contract-fallback.js +102 -0
  77. package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
  78. package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
  79. package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
  80. package/lib/agent-cli-provider/contract-invoke.js +89 -0
  81. package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
  82. package/lib/agent-cli-provider/contract-options.d.ts +3 -0
  83. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
  84. package/lib/agent-cli-provider/contract-options.js +140 -0
  85. package/lib/agent-cli-provider/contract-options.js.map +1 -0
  86. package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
  87. package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
  88. package/lib/agent-cli-provider/contract-parse.js +81 -0
  89. package/lib/agent-cli-provider/contract-parse.js.map +1 -0
  90. package/lib/agent-cli-provider/contract-support.d.ts +18 -0
  91. package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
  92. package/lib/agent-cli-provider/contract-support.js +154 -0
  93. package/lib/agent-cli-provider/contract-support.js.map +1 -0
  94. package/lib/agent-cli-provider/contract.d.ts +13 -0
  95. package/lib/agent-cli-provider/contract.d.ts.map +1 -0
  96. package/lib/agent-cli-provider/contract.js +30 -0
  97. package/lib/agent-cli-provider/contract.js.map +1 -0
  98. package/lib/agent-cli-provider/env-safety.d.ts +11 -0
  99. package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
  100. package/lib/agent-cli-provider/env-safety.js +83 -0
  101. package/lib/agent-cli-provider/env-safety.js.map +1 -0
  102. package/lib/agent-cli-provider/errors.d.ts +5 -0
  103. package/lib/agent-cli-provider/errors.d.ts.map +1 -0
  104. package/lib/agent-cli-provider/errors.js +115 -0
  105. package/lib/agent-cli-provider/errors.js.map +1 -0
  106. package/lib/agent-cli-provider/executable.d.ts +3 -0
  107. package/lib/agent-cli-provider/executable.d.ts.map +1 -0
  108. package/lib/agent-cli-provider/executable.js +24 -0
  109. package/lib/agent-cli-provider/executable.js.map +1 -0
  110. package/lib/agent-cli-provider/index.d.ts +8 -0
  111. package/lib/agent-cli-provider/index.d.ts.map +1 -0
  112. package/lib/agent-cli-provider/index.js +31 -0
  113. package/lib/agent-cli-provider/index.js.map +1 -0
  114. package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
  115. package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
  116. package/lib/agent-cli-provider/invoke-evidence.js +17 -0
  117. package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
  118. package/lib/agent-cli-provider/json.d.ts +16 -0
  119. package/lib/agent-cli-provider/json.d.ts.map +1 -0
  120. package/lib/agent-cli-provider/json.js +110 -0
  121. package/lib/agent-cli-provider/json.js.map +1 -0
  122. package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
  123. package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
  124. package/lib/agent-cli-provider/log-prefix.js +22 -0
  125. package/lib/agent-cli-provider/log-prefix.js.map +1 -0
  126. package/lib/agent-cli-provider/process-runner.d.ts +17 -0
  127. package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
  128. package/lib/agent-cli-provider/process-runner.js +89 -0
  129. package/lib/agent-cli-provider/process-runner.js.map +1 -0
  130. package/lib/agent-cli-provider/redaction.d.ts +9 -0
  131. package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
  132. package/lib/agent-cli-provider/redaction.js +227 -0
  133. package/lib/agent-cli-provider/redaction.js.map +1 -0
  134. package/lib/agent-cli-provider/schema.d.ts +5 -0
  135. package/lib/agent-cli-provider/schema.d.ts.map +1 -0
  136. package/lib/agent-cli-provider/schema.js +136 -0
  137. package/lib/agent-cli-provider/schema.js.map +1 -0
  138. package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
  139. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
  140. package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
  141. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
  142. package/lib/agent-cli-provider/types.d.ts +194 -0
  143. package/lib/agent-cli-provider/types.d.ts.map +1 -0
  144. package/lib/agent-cli-provider/types.js +12 -0
  145. package/lib/agent-cli-provider/types.js.map +1 -0
  146. package/lib/completion.js +174 -0
  147. package/lib/detached-startup.js +220 -0
  148. package/lib/docker-config.js +220 -0
  149. package/lib/git-remote-utils.js +165 -0
  150. package/lib/id-detector.js +55 -0
  151. package/lib/provider-defaults.js +62 -0
  152. package/lib/provider-detection.js +59 -0
  153. package/lib/provider-names.js +57 -0
  154. package/lib/repo-settings.js +69 -0
  155. package/lib/settings/claude-auth.js +78 -0
  156. package/lib/settings.js +542 -0
  157. package/lib/start-cluster.js +321 -0
  158. package/lib/stream-json-parser.js +67 -0
  159. package/package.json +162 -0
  160. package/scripts/fix-node-pty-permissions.js +75 -0
  161. package/scripts/record-demo.sh +279 -0
  162. package/scripts/setup-merge-queue.sh +170 -0
  163. package/scripts/test-install.sh +40 -0
  164. package/scripts/validate-templates.js +107 -0
  165. package/src/agent/agent-config.js +266 -0
  166. package/src/agent/agent-context-builder.js +189 -0
  167. package/src/agent/agent-context-sections.js +338 -0
  168. package/src/agent/agent-context-sources.js +147 -0
  169. package/src/agent/agent-hook-executor.js +721 -0
  170. package/src/agent/agent-input-injector.js +141 -0
  171. package/src/agent/agent-lifecycle.js +982 -0
  172. package/src/agent/agent-quality-gate-schema.js +93 -0
  173. package/src/agent/agent-quality-gates-context.js +51 -0
  174. package/src/agent/agent-stuck-detector.js +256 -0
  175. package/src/agent/agent-task-executor.js +2028 -0
  176. package/src/agent/agent-trigger-evaluator.js +67 -0
  177. package/src/agent/context-metrics.js +160 -0
  178. package/src/agent/context-pack-builder.js +367 -0
  179. package/src/agent/context-replay-policy.js +51 -0
  180. package/src/agent/guidance-queue.js +77 -0
  181. package/src/agent/output-extraction.js +367 -0
  182. package/src/agent/output-reformatter.js +175 -0
  183. package/src/agent/pr-verification.js +653 -0
  184. package/src/agent/rate-limit-backoff.js +82 -0
  185. package/src/agent/schema-utils.js +146 -0
  186. package/src/agent/validation-platform.js +35 -0
  187. package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
  188. package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
  189. package/src/agent-cli-provider/adapters/claude.ts +247 -0
  190. package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
  191. package/src/agent-cli-provider/adapters/codex.ts +217 -0
  192. package/src/agent-cli-provider/adapters/common.ts +124 -0
  193. package/src/agent-cli-provider/adapters/gemini.ts +243 -0
  194. package/src/agent-cli-provider/adapters/index.ts +126 -0
  195. package/src/agent-cli-provider/adapters/opencode.ts +286 -0
  196. package/src/agent-cli-provider/contract-actions.ts +150 -0
  197. package/src/agent-cli-provider/contract-env.ts +111 -0
  198. package/src/agent-cli-provider/contract-envelope.ts +110 -0
  199. package/src/agent-cli-provider/contract-errors.ts +66 -0
  200. package/src/agent-cli-provider/contract-fallback.ts +121 -0
  201. package/src/agent-cli-provider/contract-invoke.ts +104 -0
  202. package/src/agent-cli-provider/contract-options.ts +173 -0
  203. package/src/agent-cli-provider/contract-parse.ts +94 -0
  204. package/src/agent-cli-provider/contract-support.ts +167 -0
  205. package/src/agent-cli-provider/contract.ts +56 -0
  206. package/src/agent-cli-provider/env-safety.ts +82 -0
  207. package/src/agent-cli-provider/errors.ts +122 -0
  208. package/src/agent-cli-provider/executable.ts +24 -0
  209. package/src/agent-cli-provider/index.ts +83 -0
  210. package/src/agent-cli-provider/invoke-evidence.ts +18 -0
  211. package/src/agent-cli-provider/json.ts +114 -0
  212. package/src/agent-cli-provider/log-prefix.ts +20 -0
  213. package/src/agent-cli-provider/process-runner.ts +145 -0
  214. package/src/agent-cli-provider/redaction.ts +282 -0
  215. package/src/agent-cli-provider/schema.ts +115 -0
  216. package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
  217. package/src/agent-cli-provider/types.ts +237 -0
  218. package/src/agent-wrapper.js +615 -0
  219. package/src/agents/git-pusher-template.js +705 -0
  220. package/src/attach/attach-client.js +438 -0
  221. package/src/attach/attach-server.js +543 -0
  222. package/src/attach/index.js +37 -0
  223. package/src/attach/protocol.js +220 -0
  224. package/src/attach/ring-buffer.js +121 -0
  225. package/src/attach/send-input.js +88 -0
  226. package/src/attach/socket-discovery.js +267 -0
  227. package/src/claude-task-runner.js +661 -0
  228. package/src/config-router.js +89 -0
  229. package/src/config-validator.js +2202 -0
  230. package/src/copy-worker.js +43 -0
  231. package/src/guidance-topics.js +10 -0
  232. package/src/input-helpers.js +65 -0
  233. package/src/isolation-manager.js +1734 -0
  234. package/src/issue-providers/README.md +305 -0
  235. package/src/issue-providers/azure-devops-provider.js +307 -0
  236. package/src/issue-providers/base-provider.js +232 -0
  237. package/src/issue-providers/github-provider.js +210 -0
  238. package/src/issue-providers/gitlab-provider.js +262 -0
  239. package/src/issue-providers/index.js +196 -0
  240. package/src/issue-providers/jira-provider.js +260 -0
  241. package/src/ledger.js +692 -0
  242. package/src/lib/gc.js +232 -0
  243. package/src/lib/safe-exec.js +88 -0
  244. package/src/logic-engine.js +201 -0
  245. package/src/message-buffer.js +81 -0
  246. package/src/message-bus-bridge.js +144 -0
  247. package/src/message-bus.js +256 -0
  248. package/src/name-generator.js +232 -0
  249. package/src/orchestrator.js +3924 -0
  250. package/src/preflight.js +712 -0
  251. package/src/process-metrics.js +608 -0
  252. package/src/providers/anthropic/index.js +3 -0
  253. package/src/providers/base-provider.js +355 -0
  254. package/src/providers/capabilities.js +60 -0
  255. package/src/providers/google/index.js +3 -0
  256. package/src/providers/index.js +293 -0
  257. package/src/providers/openai/index.js +3 -0
  258. package/src/providers/opencode/index.js +3 -0
  259. package/src/quality-gates.js +143 -0
  260. package/src/schemas/sub-cluster.js +208 -0
  261. package/src/state-snapshot.js +398 -0
  262. package/src/state-snapshotter.js +142 -0
  263. package/src/status-footer.js +1026 -0
  264. package/src/sub-cluster-wrapper.js +693 -0
  265. package/src/task-runner.js +30 -0
  266. package/src/template-resolver.js +425 -0
  267. package/src/template-validation/index.js +338 -0
  268. package/src/template-validation/simulate-consensus-gates.js +324 -0
  269. package/src/template-validation/simulate-random-topology.js +541 -0
  270. package/src/template-validation/simulate-two-stage-validation.js +270 -0
  271. package/src/worktree-claude-config.js +135 -0
  272. package/src/worktree-tooling-env.js +150 -0
  273. package/task-lib/attachable-watcher.js +381 -0
  274. package/task-lib/commands/clean.js +50 -0
  275. package/task-lib/commands/episodes.js +105 -0
  276. package/task-lib/commands/get-log-path.js +23 -0
  277. package/task-lib/commands/kill.js +32 -0
  278. package/task-lib/commands/list.js +105 -0
  279. package/task-lib/commands/logs.js +439 -0
  280. package/task-lib/commands/resume.js +42 -0
  281. package/task-lib/commands/run.js +57 -0
  282. package/task-lib/commands/schedule.js +105 -0
  283. package/task-lib/commands/scheduler-cmd.js +96 -0
  284. package/task-lib/commands/schedules.js +148 -0
  285. package/task-lib/commands/status.js +44 -0
  286. package/task-lib/commands/unschedule.js +16 -0
  287. package/task-lib/completion.js +9 -0
  288. package/task-lib/config.js +11 -0
  289. package/task-lib/name-generator.js +230 -0
  290. package/task-lib/package.json +3 -0
  291. package/task-lib/provider-helper-runtime.js +29 -0
  292. package/task-lib/runner.js +190 -0
  293. package/task-lib/scheduler.js +252 -0
  294. package/task-lib/store.js +529 -0
  295. package/task-lib/watcher.js +305 -0
@@ -0,0 +1,661 @@
1
+ /**
2
+ * ClaudeTaskRunner - Production implementation of TaskRunner
3
+ *
4
+ * Executes provider tasks by spawning the `zeroshot task run` CLI command,
5
+ * following logs, and assembling results.
6
+ */
7
+
8
+ const { spawn, spawnSync } = require('child_process');
9
+ const fs = require('fs');
10
+ const TaskRunner = require('./task-runner');
11
+ const { loadSettings } = require('../lib/settings');
12
+ const { normalizeProviderName } = require('../lib/provider-names');
13
+ const { getProvider } = require('./providers');
14
+ const { prependWorktreeToolBinToEnv } = require('./worktree-tooling-env');
15
+ const { prepareClaudeConfigDir } = require('./worktree-claude-config');
16
+
17
+ function runCommand(command, args, options = {}, callback = null) {
18
+ const timeout = options.timeout ?? 30000;
19
+ if (timeout <= 0) {
20
+ const error = new Error('runCommand timeout must be > 0. Infinite waits are forbidden.');
21
+ if (callback) {
22
+ callback(error);
23
+ return;
24
+ }
25
+ return Promise.reject(error);
26
+ }
27
+
28
+ if (callback) {
29
+ const child = spawn(command, args, { ...options, stdio: ['ignore', 'pipe', 'pipe'] });
30
+ let stdout = '';
31
+ let stderr = '';
32
+ const timer = setTimeout(() => child.kill('SIGTERM'), timeout);
33
+ child.stdout.on('data', (chunk) => {
34
+ stdout += chunk.toString();
35
+ });
36
+ child.stderr.on('data', (chunk) => {
37
+ stderr += chunk.toString();
38
+ });
39
+ child.on('error', (error) => {
40
+ clearTimeout(timer);
41
+ callback(error, stdout, stderr);
42
+ });
43
+ child.on('close', (code, signal) => {
44
+ clearTimeout(timer);
45
+ if (code === 0) {
46
+ callback(null, stdout, stderr);
47
+ return;
48
+ }
49
+ const error = new Error(
50
+ `Command ${command} exited with code ${code ?? 'null'} signal ${signal || 'none'}`
51
+ );
52
+ error.code = code;
53
+ error.signal = signal;
54
+ error.stderr = stderr;
55
+ callback(error, stdout, stderr);
56
+ });
57
+ return;
58
+ }
59
+
60
+ return new Promise((resolve, reject) => {
61
+ runCommand(command, args, options, (error, stdout, stderr) => {
62
+ if (error) {
63
+ reject(error);
64
+ return;
65
+ }
66
+ resolve({ stdout, stderr });
67
+ });
68
+ });
69
+ }
70
+
71
+ function runCommandSync(command, args, options = {}) {
72
+ const timeout = options.timeout ?? 30000;
73
+ const result = spawnSync(command, args, { ...options, timeout });
74
+ if (result.status !== 0 || result.error) {
75
+ const detail = result.error?.message || result.stderr?.toString() || 'no stderr';
76
+ const error = new Error(
77
+ `Command ${command} failed with status ${result.status ?? 'null'}: ${detail}`
78
+ );
79
+ error.status = result.status;
80
+ error.stderr = result.stderr?.toString();
81
+ throw error;
82
+ }
83
+ return result.stdout?.toString() || '';
84
+ }
85
+
86
+ class ClaudeTaskRunner extends TaskRunner {
87
+ /**
88
+ * @param {Object} options
89
+ * @param {Object} [options.messageBus] - MessageBus for streaming output
90
+ * @param {boolean} [options.quiet] - Suppress console logging
91
+ * @param {number} [options.timeout] - Task timeout in ms (default: 1 hour)
92
+ * @param {Function} [options.onOutput] - Callback for output lines
93
+ */
94
+ constructor(options = {}) {
95
+ super();
96
+ this.messageBus = options.messageBus || null;
97
+ this.quiet = options.quiet || false;
98
+ this.timeout = options.timeout || 60 * 60 * 1000;
99
+ this.onOutput = options.onOutput || null;
100
+ }
101
+
102
+ /**
103
+ * @param {...any} args
104
+ */
105
+ _log(...args) {
106
+ if (!this.quiet) {
107
+ console.log(...args);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Execute a task via zeroshot CLI
113
+ *
114
+ * @param {string} context - Full prompt/context
115
+ * @param {{agentId?: string, model?: string, outputFormat?: string, jsonSchema?: any, strictSchema?: boolean, cwd?: string, worktreePath?: string|null, isolation?: any}} options - Execution options
116
+ * @returns {Promise<{success: boolean, output: string, error: string|null, taskId?: string}>}
117
+ */
118
+ async run(context, options = {}) {
119
+ const {
120
+ agentId = 'unknown',
121
+ provider,
122
+ model = null,
123
+ modelLevel = null,
124
+ modelSpec: explicitModelSpec = null,
125
+ reasoningEffort = null,
126
+ outputFormat = 'stream-json',
127
+ jsonSchema = null,
128
+ strictSchema = false, // false = live streaming (default), true = CLI schema enforcement (no streaming)
129
+ cwd = process.cwd(),
130
+ worktreePath = null,
131
+ isolation = null,
132
+ } = options;
133
+
134
+ const settings = loadSettings();
135
+ const providerName = normalizeProviderName(provider || settings.defaultProvider || 'claude');
136
+ const { providerModule, providerSettings, levelOverrides } = this._getProviderContext(
137
+ providerName,
138
+ settings
139
+ );
140
+ const resolvedModelSpec = this._resolveModelSpec({
141
+ explicitModelSpec,
142
+ model,
143
+ reasoningEffort,
144
+ modelLevel,
145
+ providerModule,
146
+ providerSettings,
147
+ levelOverrides,
148
+ });
149
+
150
+ // Isolation mode delegates to separate method
151
+ if (isolation?.enabled) {
152
+ return this._runIsolated(context, {
153
+ ...options,
154
+ provider: providerName,
155
+ modelSpec: resolvedModelSpec,
156
+ });
157
+ }
158
+
159
+ const ctPath = 'zeroshot';
160
+
161
+ const runOutputFormat = this._resolveOutputFormat({
162
+ outputFormat,
163
+ jsonSchema,
164
+ strictSchema,
165
+ });
166
+ const args = this._buildRunArgs({
167
+ context,
168
+ providerName,
169
+ runOutputFormat,
170
+ resolvedModelSpec,
171
+ jsonSchema,
172
+ });
173
+
174
+ // Spawn and get task ID
175
+ const spawnEnv = this._buildSpawnEnv(providerName, resolvedModelSpec, {
176
+ cwd,
177
+ worktreePath,
178
+ });
179
+
180
+ const taskId = await this._spawnAndGetTaskId(ctPath, args, cwd, spawnEnv, agentId);
181
+
182
+ this._log(`📋 [${agentId}]: Following zeroshot logs for ${taskId}`);
183
+
184
+ // Wait for task registration
185
+ await this._waitForTaskReady(ctPath, taskId);
186
+
187
+ // Follow logs until completion
188
+ return this._followLogs(ctPath, taskId, agentId);
189
+ }
190
+
191
+ _getProviderContext(providerName, settings) {
192
+ const providerModule = getProvider(providerName);
193
+ const providerSettings = settings.providerSettings?.[providerName] || {};
194
+ const levelOverrides = providerSettings.levelOverrides || {};
195
+ return { providerModule, providerSettings, levelOverrides };
196
+ }
197
+
198
+ _resolveModelSpec({
199
+ explicitModelSpec,
200
+ model,
201
+ reasoningEffort,
202
+ modelLevel,
203
+ providerModule,
204
+ providerSettings,
205
+ levelOverrides,
206
+ }) {
207
+ if (explicitModelSpec) {
208
+ if (explicitModelSpec.model) {
209
+ providerModule.validateModelId(explicitModelSpec.model);
210
+ }
211
+ return explicitModelSpec;
212
+ }
213
+
214
+ if (model) {
215
+ providerModule.validateModelId(model);
216
+ return { model, reasoningEffort };
217
+ }
218
+
219
+ const level = modelLevel || providerSettings.defaultLevel || providerModule.getDefaultLevel();
220
+ let resolvedModelSpec = providerModule.resolveModelSpec(level, levelOverrides);
221
+ if (reasoningEffort) {
222
+ resolvedModelSpec = { ...resolvedModelSpec, reasoningEffort };
223
+ }
224
+
225
+ return resolvedModelSpec;
226
+ }
227
+
228
+ _resolveOutputFormat({ outputFormat, jsonSchema, strictSchema }) {
229
+ // json output does not stream; if a jsonSchema is configured we run stream-json
230
+ // for live logs and validate/parse JSON after completion.
231
+ // Set strictSchema=true to disable live streaming and use CLI's native schema enforcement.
232
+ return jsonSchema && outputFormat === 'json' && !strictSchema ? 'stream-json' : outputFormat;
233
+ }
234
+
235
+ _buildRunArgs({ context, providerName, runOutputFormat, resolvedModelSpec, jsonSchema }) {
236
+ const args = ['task', 'run', '--output-format', runOutputFormat, '--provider', providerName];
237
+
238
+ if (resolvedModelSpec?.model) {
239
+ args.push('--model', resolvedModelSpec.model);
240
+ }
241
+
242
+ if (resolvedModelSpec?.reasoningEffort) {
243
+ args.push('--reasoning-effort', resolvedModelSpec.reasoningEffort);
244
+ }
245
+
246
+ // Pass schema to CLI only when using json output (strictSchema=true or no conflict)
247
+ if (jsonSchema && runOutputFormat === 'json') {
248
+ args.push('--json-schema', JSON.stringify(jsonSchema));
249
+ }
250
+
251
+ args.push(context);
252
+
253
+ return args;
254
+ }
255
+
256
+ _buildSpawnEnv(providerName, resolvedModelSpec, options = {}) {
257
+ const { cwd = process.cwd(), worktreePath = null } = options;
258
+ const spawnEnv = {
259
+ ...process.env,
260
+ };
261
+ if (providerName === 'claude' && resolvedModelSpec?.model) {
262
+ spawnEnv.ANTHROPIC_MODEL = resolvedModelSpec.model;
263
+ }
264
+ if (providerName === 'claude') {
265
+ const claudeConfigDir = prepareClaudeConfigDir({ cwd, worktreePath });
266
+ if (claudeConfigDir) {
267
+ spawnEnv.CLAUDE_CONFIG_DIR = claudeConfigDir;
268
+ }
269
+ }
270
+
271
+ prependWorktreeToolBinToEnv(spawnEnv, { cwd, worktreePath });
272
+
273
+ return spawnEnv;
274
+ }
275
+
276
+ /**
277
+ * @param {string} ctPath
278
+ * @param {string[]} args
279
+ * @param {string} cwd
280
+ * @param {Object} spawnEnv
281
+ * @param {string} _agentId
282
+ * @returns {Promise<string>}
283
+ */
284
+ _spawnAndGetTaskId(ctPath, args, cwd, spawnEnv, _agentId) {
285
+ return new Promise((resolve, reject) => {
286
+ const proc = spawn(ctPath, args, {
287
+ cwd,
288
+ stdio: ['ignore', 'pipe', 'pipe'],
289
+ env: spawnEnv,
290
+ });
291
+
292
+ let stdout = '';
293
+ let stderr = '';
294
+
295
+ proc.stdout.on('data', (data) => {
296
+ stdout += data.toString();
297
+ });
298
+
299
+ proc.stderr.on('data', (data) => {
300
+ stderr += data.toString();
301
+ });
302
+
303
+ proc.on('close', (code) => {
304
+ if (code === 0) {
305
+ const match = stdout.match(/Task spawned: ((?:task-)?[a-z]+-[a-z]+-[a-z0-9]+)/);
306
+ if (match) {
307
+ resolve(match[1]);
308
+ } else {
309
+ reject(new Error(`Could not parse task ID from output: ${stdout}`));
310
+ }
311
+ } else {
312
+ reject(new Error(`zeroshot task run failed with code ${code}: ${stderr}`));
313
+ }
314
+ });
315
+
316
+ proc.on('error', (error) => {
317
+ reject(error);
318
+ });
319
+ });
320
+ }
321
+
322
+ /**
323
+ * @param {string} ctPath
324
+ * @param {string} taskId
325
+ * @param {number} maxRetries
326
+ * @param {number} delayMs
327
+ * @returns {Promise<void>}
328
+ */
329
+ async _waitForTaskReady(ctPath, taskId, maxRetries = 10, delayMs = 200) {
330
+ for (let i = 0; i < maxRetries; i++) {
331
+ const exists = await new Promise((resolve) => {
332
+ runCommand(ctPath, ['status', taskId], {}, (error, stdout) => {
333
+ resolve(!error && !stdout.includes('Task not found'));
334
+ });
335
+ });
336
+
337
+ if (exists) return;
338
+ await new Promise((r) => setTimeout(r, delayMs));
339
+ }
340
+ console.warn(
341
+ `⚠️ Task ${taskId} not yet visible after ${maxRetries} retries, continuing anyway`
342
+ );
343
+ }
344
+
345
+ /**
346
+ * @param {string} ctPath
347
+ * @param {string} taskId
348
+ * @param {string} agentId
349
+ * @returns {Promise<{success: boolean, output: string, error: string|null, taskId: string}>}
350
+ */
351
+ _followLogs(ctPath, taskId, agentId) {
352
+ return new Promise((resolve, reject) => {
353
+ let output = '';
354
+ /** @type {string|null} */
355
+ let logFilePath = null;
356
+ let lastSize = 0;
357
+ /** @type {NodeJS.Timeout|null} */
358
+ let pollInterval = null;
359
+ /** @type {NodeJS.Timeout|null} */
360
+ let statusCheckInterval = null;
361
+ let resolved = false;
362
+ let lineBuffer = '';
363
+
364
+ // Get log file path
365
+ try {
366
+ logFilePath = runCommandSync(ctPath, ['get-log-path', taskId], {
367
+ encoding: 'utf-8',
368
+ }).trim();
369
+ } catch {
370
+ this._log(`⏳ [${agentId}]: Waiting for log file...`);
371
+ }
372
+
373
+ /**
374
+ * @param {string} line
375
+ */
376
+ const broadcastLine = (line) => {
377
+ if (!line.trim()) return;
378
+
379
+ let content = line;
380
+ const timestampMatch = line.match(/^\[(\d{13})\](.*)$/);
381
+ if (timestampMatch) {
382
+ content = timestampMatch[2];
383
+ }
384
+
385
+ // Skip non-JSON patterns
386
+ if (
387
+ content.startsWith('===') ||
388
+ content.startsWith('Finished:') ||
389
+ content.startsWith('Exit code:') ||
390
+ (content.includes('"type":"system"') && content.includes('"subtype":"init"'))
391
+ ) {
392
+ return;
393
+ }
394
+
395
+ if (!content.trim().startsWith('{')) return;
396
+
397
+ try {
398
+ JSON.parse(content);
399
+ } catch {
400
+ return;
401
+ }
402
+
403
+ output += content + '\n';
404
+
405
+ // Callback for output streaming
406
+ if (this.onOutput) {
407
+ this.onOutput(content, agentId);
408
+ }
409
+ };
410
+
411
+ /**
412
+ * @param {string} content
413
+ */
414
+ const processNewContent = (content) => {
415
+ lineBuffer += content;
416
+ const lines = lineBuffer.split('\n');
417
+
418
+ for (let i = 0; i < lines.length - 1; i++) {
419
+ broadcastLine(lines[i]);
420
+ }
421
+ lineBuffer = lines[lines.length - 1];
422
+ };
423
+
424
+ const pollLogFile = () => {
425
+ if (!logFilePath) {
426
+ try {
427
+ logFilePath = runCommandSync(ctPath, ['get-log-path', taskId], {
428
+ encoding: 'utf-8',
429
+ }).trim();
430
+ } catch {
431
+ return;
432
+ }
433
+ }
434
+
435
+ if (!fs.existsSync(logFilePath)) return;
436
+
437
+ try {
438
+ const stats = fs.statSync(logFilePath);
439
+ const currentSize = stats.size;
440
+
441
+ if (currentSize > lastSize) {
442
+ const fd = fs.openSync(logFilePath, 'r');
443
+ const buffer = Buffer.alloc(currentSize - lastSize);
444
+ fs.readSync(fd, buffer, 0, buffer.length, lastSize);
445
+ fs.closeSync(fd);
446
+
447
+ processNewContent(buffer.toString('utf-8'));
448
+ lastSize = currentSize;
449
+ }
450
+ } catch (err) {
451
+ const error = /** @type {Error} */ (err);
452
+ console.warn(`⚠️ [${agentId}]: Error reading log: ${error.message}`);
453
+ }
454
+ };
455
+
456
+ pollInterval = setInterval(pollLogFile, 300);
457
+
458
+ /**
459
+ * @param {boolean} success
460
+ * @param {string} stdout
461
+ * @returns {string|null}
462
+ */
463
+ const extractErrorContext = (success, stdout) => {
464
+ if (success) return null;
465
+
466
+ // Try to extract error from status output first
467
+ const statusErrorMatch = stdout.match(/Error:\s*(.+)/);
468
+ if (statusErrorMatch) {
469
+ return statusErrorMatch[1].trim();
470
+ }
471
+
472
+ // Fall back to last 500 chars of output
473
+ const lastOutput = output.slice(-500).trim();
474
+ if (!lastOutput) {
475
+ return 'Task failed with no output';
476
+ }
477
+
478
+ const errorPatterns = [
479
+ /Error:\s*(.+)/i,
480
+ /error:\s*(.+)/i,
481
+ /failed:\s*(.+)/i,
482
+ /Exception:\s*(.+)/i,
483
+ ];
484
+
485
+ for (const pattern of errorPatterns) {
486
+ const match = lastOutput.match(pattern);
487
+ if (match) {
488
+ return match[1].slice(0, 200);
489
+ }
490
+ }
491
+
492
+ return `Task failed. Last output: ${lastOutput.slice(-200)}`;
493
+ };
494
+
495
+ statusCheckInterval = setInterval(() => {
496
+ runCommand(ctPath, ['status', taskId], {}, (error, stdout) => {
497
+ if (resolved) return;
498
+
499
+ if (
500
+ !error &&
501
+ (stdout.includes('Status: completed') || stdout.includes('Status: failed'))
502
+ ) {
503
+ const success = stdout.includes('Status: completed');
504
+
505
+ pollLogFile();
506
+
507
+ setTimeout(() => {
508
+ if (resolved) return;
509
+ resolved = true;
510
+
511
+ if (pollInterval) clearInterval(pollInterval);
512
+ if (statusCheckInterval) clearInterval(statusCheckInterval);
513
+
514
+ const errorContext = extractErrorContext(success, stdout);
515
+
516
+ resolve({
517
+ success,
518
+ output,
519
+ error: errorContext,
520
+ taskId,
521
+ });
522
+ }, 500);
523
+ }
524
+ });
525
+ }, 1000);
526
+
527
+ // Timeout
528
+ setTimeout(() => {
529
+ if (resolved) return;
530
+ resolved = true;
531
+
532
+ clearInterval(pollInterval);
533
+ clearInterval(statusCheckInterval);
534
+
535
+ const timeoutMinutes = Math.round(this.timeout / 60000);
536
+ reject(new Error(`Task timed out after ${timeoutMinutes} minutes`));
537
+ }, this.timeout);
538
+ });
539
+ }
540
+
541
+ /**
542
+ * Run task in isolated Docker container
543
+ * @param {string} context
544
+ * @param {{agentId?: string, model?: string, outputFormat?: string, jsonSchema?: any, strictSchema?: boolean, isolation?: any}} options
545
+ * @returns {Promise<{success: boolean, output: string, error: string|null}>}
546
+ */
547
+ _runIsolated(context, options) {
548
+ const {
549
+ agentId = 'unknown',
550
+ provider = 'claude',
551
+ modelSpec = null,
552
+ outputFormat = 'stream-json',
553
+ jsonSchema = null,
554
+ strictSchema = false,
555
+ isolation,
556
+ } = options;
557
+ const { manager, clusterId } = isolation;
558
+
559
+ this._log(`📦 [${agentId}]: Running task in isolated container...`);
560
+
561
+ const desiredOutputFormat = outputFormat;
562
+ const runOutputFormat =
563
+ jsonSchema && desiredOutputFormat === 'json' && !strictSchema
564
+ ? 'stream-json'
565
+ : desiredOutputFormat;
566
+
567
+ const command = [
568
+ 'zeroshot',
569
+ 'task',
570
+ 'run',
571
+ '--output-format',
572
+ runOutputFormat,
573
+ '--provider',
574
+ provider,
575
+ ];
576
+
577
+ if (modelSpec?.model) {
578
+ command.push('--model', modelSpec.model);
579
+ }
580
+
581
+ if (modelSpec?.reasoningEffort) {
582
+ command.push('--reasoning-effort', modelSpec.reasoningEffort);
583
+ }
584
+
585
+ if (jsonSchema && runOutputFormat === 'json') {
586
+ command.push('--json-schema', JSON.stringify(jsonSchema));
587
+ }
588
+
589
+ let finalContext = context;
590
+ if (jsonSchema && desiredOutputFormat === 'json' && runOutputFormat === 'stream-json') {
591
+ finalContext += `\n\n## Output Format (REQUIRED)\n\nReturn a JSON object that matches this schema exactly.\n\nSchema:\n\`\`\`json\n${JSON.stringify(
592
+ jsonSchema,
593
+ null,
594
+ 2
595
+ )}\n\`\`\`\n`;
596
+ }
597
+
598
+ command.push(finalContext);
599
+
600
+ return new Promise((resolve, reject) => {
601
+ let output = '';
602
+ let resolved = false;
603
+
604
+ const proc = manager.spawnInContainer(clusterId, command, {
605
+ env:
606
+ provider === 'claude' && modelSpec?.model
607
+ ? { ANTHROPIC_MODEL: modelSpec.model, ZEROSHOT_BLOCK_ASK_USER: '1' }
608
+ : {},
609
+ });
610
+
611
+ proc.stdout.on('data', (/** @type {Buffer} */ data) => {
612
+ const chunk = data.toString();
613
+ output += chunk;
614
+
615
+ if (this.onOutput) {
616
+ this.onOutput(chunk, agentId);
617
+ }
618
+ });
619
+
620
+ proc.stderr.on('data', (/** @type {Buffer} */ data) => {
621
+ const chunk = data.toString();
622
+ if (!this.quiet) {
623
+ console.error(`[${agentId}] stderr:`, chunk);
624
+ }
625
+ });
626
+
627
+ proc.on('close', (/** @type {number|null} */ code) => {
628
+ if (resolved) return;
629
+ resolved = true;
630
+
631
+ resolve({
632
+ success: code === 0,
633
+ output,
634
+ error: code === 0 ? null : `Container exited with code ${code}`,
635
+ });
636
+ });
637
+
638
+ proc.on('error', (/** @type {Error} */ error) => {
639
+ if (resolved) return;
640
+ resolved = true;
641
+ reject(error);
642
+ });
643
+
644
+ setTimeout(() => {
645
+ if (resolved) return;
646
+ resolved = true;
647
+
648
+ try {
649
+ proc.kill('SIGKILL');
650
+ } catch {
651
+ // Ignore - process may already be dead
652
+ }
653
+
654
+ const timeoutMinutes = Math.round(this.timeout / 60000);
655
+ reject(new Error(`Isolated task timed out after ${timeoutMinutes} minutes`));
656
+ }, this.timeout);
657
+ });
658
+ }
659
+ }
660
+
661
+ module.exports = ClaudeTaskRunner;