@the-open-engine/zeroshot 6.9.1 → 6.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/commands/inspect.js +1 -0
- package/cli/index.js +17 -1
- package/cluster-hooks/block-ask-user-question.py +2 -3
- package/cluster-hooks/block-dangerous-git.py +2 -3
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/claude.js +57 -3
- package/lib/agent-cli-provider/adapters/claude.js.map +1 -1
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/codex.js +32 -2
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
- package/lib/agent-cli-provider/adapters/common.js +1 -1
- package/lib/agent-cli-provider/adapters/common.js.map +1 -1
- package/lib/agent-cli-provider/adapters/index.d.ts +1 -0
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/index.js +8 -0
- package/lib/agent-cli-provider/adapters/index.js.map +1 -1
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-options.js +3 -0
- package/lib/agent-cli-provider/contract-options.js.map +1 -1
- package/lib/agent-cli-provider/index.d.ts +1 -1
- package/lib/agent-cli-provider/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/index.js +2 -1
- package/lib/agent-cli-provider/index.js.map +1 -1
- package/lib/agent-cli-provider/provider-registry.d.ts +9 -0
- package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
- package/lib/agent-cli-provider/provider-registry.js +3 -0
- package/lib/agent-cli-provider/provider-registry.js.map +1 -1
- package/lib/agent-cli-provider/types.d.ts +10 -2
- package/lib/agent-cli-provider/types.d.ts.map +1 -1
- package/lib/agent-cli-provider/types.js.map +1 -1
- package/package.json +1 -1
- package/src/agent/agent-context-builder.js +90 -4
- package/src/agent/agent-context-sources.js +20 -2
- package/src/agent/agent-lifecycle.js +43 -8
- package/src/agent/agent-task-executor.js +557 -314
- package/src/agent/guidance-queue.js +29 -7
- package/src/agent/provider-session.js +277 -0
- package/src/agent-cli-provider/adapters/claude.ts +64 -4
- package/src/agent-cli-provider/adapters/codex.ts +47 -3
- package/src/agent-cli-provider/adapters/common.ts +1 -1
- package/src/agent-cli-provider/adapters/index.ts +10 -0
- package/src/agent-cli-provider/contract-options.ts +7 -0
- package/src/agent-cli-provider/index.ts +1 -0
- package/src/agent-cli-provider/provider-registry.ts +13 -1
- package/src/agent-cli-provider/types.ts +13 -6
- package/src/agent-wrapper.js +44 -8
- package/src/claude-task-runner.js +153 -44
- package/src/ledger-sequence.js +63 -0
- package/src/ledger.js +114 -29
- package/src/orchestrator.js +40 -2
- package/src/task-spawn-cleanup-ownership.js +82 -0
- package/src/worktree-claude-config.js +193 -85
- package/task-lib/attachable-watcher.js +101 -253
- package/task-lib/command-spec-cleanup.js +219 -0
- package/task-lib/commands/clean.js +35 -5
- package/task-lib/commands/get-task-id-by-spawn-token.js +10 -0
- package/task-lib/commands/kill.js +117 -4
- package/task-lib/commands/resume.js +29 -6
- package/task-lib/commands/status.js +4 -0
- package/task-lib/provider-helper-runtime.js +1 -0
- package/task-lib/provider-session-capture.js +138 -0
- package/task-lib/runner.js +70 -5
- package/task-lib/store.js +126 -12
- package/task-lib/watcher-output-runtime.js +284 -0
- package/task-lib/watcher.js +131 -242
package/task-lib/watcher.js
CHANGED
|
@@ -5,16 +5,19 @@
|
|
|
5
5
|
* Runs detached from parent, updates task status on completion
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { appendFileSync } from 'fs';
|
|
9
|
+
import { getTask, updateTask } from './store.js';
|
|
10
|
+
import { createCommandSpecCleanup } from './command-spec-cleanup.js';
|
|
11
|
+
import { createProviderSessionCapture } from './provider-session-capture.js';
|
|
12
12
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
completeWatcherFailure,
|
|
14
|
+
completePendingWatcherCancellation,
|
|
15
|
+
completeWatcherTask,
|
|
16
|
+
createWatcherOutputRuntime,
|
|
17
|
+
resolveWatcherCommand,
|
|
18
|
+
spawnWatcherProvider,
|
|
19
|
+
terminateWatcherProvider,
|
|
20
|
+
} from './watcher-output-runtime.js';
|
|
18
21
|
import { createRequire } from 'module';
|
|
19
22
|
|
|
20
23
|
const require = createRequire(import.meta.url);
|
|
@@ -34,65 +37,42 @@ function log(msg) {
|
|
|
34
37
|
appendFileSync(logFile, msg);
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
function emergencyLog(msg) {
|
|
41
|
+
try {
|
|
42
|
+
log(msg);
|
|
43
|
+
} catch {
|
|
44
|
+
process.stderr.write(msg);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
43
47
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
env,
|
|
47
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
48
|
-
detached: process.platform !== 'win32',
|
|
49
|
-
windowsHide: true,
|
|
48
|
+
const commandCleanup = createCommandSpecCleanup(commandSpec, (cleanupPath, error) => {
|
|
49
|
+
emergencyLog(`[${Date.now()}][CLEANUP] Failed to delete ${cleanupPath}: ${error.message}\n`);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
const { providerName, env, command, finalArgs } = resolveWatcherCommand(
|
|
53
|
+
config,
|
|
54
|
+
commandSpec,
|
|
55
|
+
args,
|
|
56
|
+
normalizeProviderName
|
|
57
|
+
);
|
|
58
|
+
const storedTask = getTask(taskId);
|
|
59
|
+
const providerSessionCapture = createProviderSessionCapture({
|
|
60
|
+
providerName,
|
|
61
|
+
taskId,
|
|
62
|
+
updateTask,
|
|
63
|
+
log,
|
|
64
|
+
requestedSessionId: storedTask?.requestedResumeSessionId || null,
|
|
65
|
+
initialSessionId: storedTask?.sessionId || null,
|
|
66
|
+
initialSessionIdConflict: storedTask?.sessionIdConflict === true,
|
|
56
67
|
});
|
|
57
68
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
config.jsonSchema &&
|
|
61
|
-
config.silentJsonOutput &&
|
|
62
|
-
supportsProviderStructuredOutputRecovery(providerName);
|
|
63
|
-
|
|
64
|
-
let finalResultJson = null;
|
|
65
|
-
let streamingModeError = null;
|
|
66
|
-
let fatalError = null;
|
|
67
|
-
let cleanupStarted = false;
|
|
68
|
-
|
|
69
|
+
let crashStarted = false;
|
|
70
|
+
let child = null;
|
|
69
71
|
let stdoutBuffer = '';
|
|
70
72
|
let stderrBuffer = '';
|
|
71
73
|
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
const lines = nextBuffer.split('\n');
|
|
75
|
-
const remaining = lines.pop() || '';
|
|
76
|
-
return { lines, remaining };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function maybeHandleFatalError(line, timestamp) {
|
|
80
|
-
if (fatalError) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const detected = detectProviderFatalError(providerName, line);
|
|
85
|
-
if (!detected) {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
fatalError = detected;
|
|
90
|
-
|
|
91
|
-
if (silentJsonMode) {
|
|
92
|
-
log(`[${timestamp}]${line}\n`);
|
|
93
|
-
}
|
|
94
|
-
log(`[${timestamp}][FATAL] ${detected}\n`);
|
|
95
|
-
|
|
74
|
+
function stopProviderAfterFatalOutput() {
|
|
75
|
+
if (!child) return;
|
|
96
76
|
try {
|
|
97
77
|
child.kill('SIGTERM');
|
|
98
78
|
} catch {
|
|
@@ -108,211 +88,120 @@ function maybeHandleFatalError(line, timestamp) {
|
|
|
108
88
|
}
|
|
109
89
|
}
|
|
110
90
|
}, 5000);
|
|
111
|
-
|
|
112
|
-
return true;
|
|
113
91
|
}
|
|
114
92
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function maybeCaptureStructuredOutput(line) {
|
|
126
|
-
try {
|
|
127
|
-
const json = JSON.parse(line);
|
|
128
|
-
if (json.structured_output) {
|
|
129
|
-
finalResultJson = line;
|
|
130
|
-
}
|
|
131
|
-
} catch {
|
|
132
|
-
// Not JSON, skip
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function handleSilentJsonLines(lines, timestamp) {
|
|
137
|
-
for (const line of lines) {
|
|
138
|
-
if (!line.trim()) continue;
|
|
139
|
-
maybeHandleFatalError(line, timestamp);
|
|
140
|
-
if (captureStreamingError(line, timestamp)) {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
maybeCaptureStructuredOutput(line);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function handleStreamingLines(lines, timestamp) {
|
|
148
|
-
for (const line of lines) {
|
|
149
|
-
maybeHandleFatalError(line, timestamp);
|
|
150
|
-
if (captureStreamingError(line, timestamp)) {
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
log(`[${timestamp}]${line}\n`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function flushStdoutBuffer(timestamp) {
|
|
158
|
-
if (!stdoutBuffer.trim()) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (!enableRecovery) {
|
|
163
|
-
if (!silentJsonMode) {
|
|
164
|
-
log(`[${timestamp}]${stdoutBuffer}\n`);
|
|
165
|
-
}
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
maybeHandleFatalError(stdoutBuffer, timestamp);
|
|
170
|
-
if (captureStreamingError(stdoutBuffer, timestamp)) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (silentJsonMode) {
|
|
175
|
-
maybeCaptureStructuredOutput(stdoutBuffer);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
93
|
+
const outputRuntime = createWatcherOutputRuntime({
|
|
94
|
+
config,
|
|
95
|
+
providerName,
|
|
96
|
+
log,
|
|
97
|
+
stopProvider: stopProviderAfterFatalOutput,
|
|
98
|
+
providerSessionCapture,
|
|
99
|
+
});
|
|
178
100
|
|
|
179
|
-
|
|
101
|
+
function terminateOwnedProviderBoundary(exitObserved = false) {
|
|
102
|
+
return terminateWatcherProvider(child, { exitObserved });
|
|
180
103
|
}
|
|
181
104
|
|
|
182
|
-
function
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
105
|
+
async function crashWithError(error, source) {
|
|
106
|
+
if (crashStarted) return;
|
|
107
|
+
crashStarted = true;
|
|
108
|
+
await completeWatcherFailure({
|
|
109
|
+
taskId,
|
|
110
|
+
error,
|
|
111
|
+
source,
|
|
112
|
+
commandCleanup,
|
|
113
|
+
terminateProvider: terminateOwnedProviderBoundary,
|
|
114
|
+
updateTask,
|
|
115
|
+
emergencyLog,
|
|
116
|
+
});
|
|
117
|
+
process.exit(1);
|
|
187
118
|
}
|
|
188
119
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const recovered = recoverProviderStructuredOutput(providerName, streamingModeError.sessionId);
|
|
195
|
-
if (recovered?.payload) {
|
|
196
|
-
const recoveredLine = JSON.stringify(recovered.payload);
|
|
197
|
-
if (silentJsonMode) {
|
|
198
|
-
finalResultJson = recoveredLine;
|
|
199
|
-
} else {
|
|
200
|
-
log(`[${timestamp}]${recoveredLine}\n`);
|
|
201
|
-
}
|
|
202
|
-
} else if (streamingModeError.line) {
|
|
203
|
-
if (silentJsonMode) {
|
|
204
|
-
log(streamingModeError.line + '\n');
|
|
205
|
-
} else {
|
|
206
|
-
log(`[${streamingModeError.timestamp}]${streamingModeError.line}\n`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return recovered;
|
|
211
|
-
}
|
|
120
|
+
process.on('uncaughtException', (error) => {
|
|
121
|
+
void crashWithError(error, 'uncaughtException');
|
|
122
|
+
});
|
|
212
123
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
for (const file of commandSpec.cleanup || []) {
|
|
217
|
-
try {
|
|
218
|
-
await unlink(file);
|
|
219
|
-
} catch (error) {
|
|
220
|
-
log(`[${Date.now()}][CLEANUP] Failed to delete ${file}: ${error.message}\n`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
124
|
+
process.on('unhandledRejection', (reason) => {
|
|
125
|
+
void crashWithError(reason, 'unhandledRejection');
|
|
126
|
+
});
|
|
224
127
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
128
|
+
if (
|
|
129
|
+
await completePendingWatcherCancellation({
|
|
130
|
+
taskId,
|
|
131
|
+
getTask,
|
|
132
|
+
commandCleanup,
|
|
133
|
+
terminateProvider: () => true,
|
|
134
|
+
updateTask,
|
|
135
|
+
emergencyLog,
|
|
136
|
+
})
|
|
137
|
+
) {
|
|
138
|
+
process.exit(0);
|
|
235
139
|
}
|
|
236
140
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
log(`Finished: ${new Date().toISOString()}\n`);
|
|
244
|
-
log(`Exit code: ${code}, Signal: ${signal}\n`);
|
|
245
|
-
}
|
|
141
|
+
child = spawnWatcherProvider(command, finalArgs, {
|
|
142
|
+
cwd: commandSpec.cwd || cwd,
|
|
143
|
+
env,
|
|
144
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
145
|
+
detached: process.platform !== 'win32',
|
|
146
|
+
});
|
|
246
147
|
|
|
247
148
|
child.stdout.on('data', (data) => {
|
|
248
|
-
|
|
249
|
-
const timestamp = Date.now();
|
|
250
|
-
|
|
251
|
-
const { lines, remaining } = splitBufferLines(stdoutBuffer, chunk);
|
|
252
|
-
stdoutBuffer = remaining;
|
|
253
|
-
|
|
254
|
-
if (silentJsonMode) {
|
|
255
|
-
handleSilentJsonLines(lines, timestamp);
|
|
256
|
-
} else {
|
|
257
|
-
handleStreamingLines(lines, timestamp);
|
|
258
|
-
}
|
|
149
|
+
stdoutBuffer = outputRuntime.consumeOutput(stdoutBuffer, data);
|
|
259
150
|
});
|
|
260
151
|
|
|
261
152
|
child.stderr.on('data', (data) => {
|
|
262
|
-
|
|
263
|
-
const timestamp = Date.now();
|
|
264
|
-
|
|
265
|
-
const { lines, remaining } = splitBufferLines(stderrBuffer, chunk);
|
|
266
|
-
stderrBuffer = remaining;
|
|
267
|
-
|
|
268
|
-
for (const line of lines) {
|
|
269
|
-
log(`[${timestamp}]${line}\n`);
|
|
270
|
-
}
|
|
153
|
+
stderrBuffer = outputRuntime.consumeStderr(stderrBuffer, data);
|
|
271
154
|
});
|
|
272
155
|
|
|
273
156
|
child.on('close', async (code, signal) => {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const status = resolvedCode === 0 ? 'completed' : 'failed';
|
|
290
|
-
try {
|
|
291
|
-
await updateTask(taskId, {
|
|
292
|
-
status,
|
|
293
|
-
pid: null,
|
|
294
|
-
processGroupId: null,
|
|
295
|
-
exitCode: resolvedCode,
|
|
296
|
-
error: fatalError || (resolvedCode !== 0 && signal ? `Killed by ${signal}` : null),
|
|
297
|
-
});
|
|
298
|
-
} catch (updateError) {
|
|
299
|
-
log(`[${Date.now()}][ERROR] Failed to update task status: ${updateError.message}\n`);
|
|
300
|
-
}
|
|
157
|
+
if (crashStarted) return;
|
|
158
|
+
const completion = outputRuntime.complete({
|
|
159
|
+
code,
|
|
160
|
+
signal,
|
|
161
|
+
outputBuffer: stdoutBuffer,
|
|
162
|
+
stderrBuffer,
|
|
163
|
+
});
|
|
164
|
+
await completeWatcherTask({
|
|
165
|
+
taskId,
|
|
166
|
+
completion,
|
|
167
|
+
commandCleanup,
|
|
168
|
+
terminateProvider: () => terminateOwnedProviderBoundary(true),
|
|
169
|
+
updateTask,
|
|
170
|
+
emergencyLog,
|
|
171
|
+
});
|
|
301
172
|
process.exit(0);
|
|
302
173
|
});
|
|
303
174
|
|
|
304
175
|
child.on('error', async (err) => {
|
|
176
|
+
if (crashStarted) return;
|
|
177
|
+
crashStarted = true;
|
|
305
178
|
log(`\nError: ${err.message}\n`);
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
} catch (updateError) {
|
|
315
|
-
log(`[${Date.now()}][ERROR] Failed to update task status: ${updateError.message}\n`);
|
|
316
|
-
}
|
|
179
|
+
await completeWatcherTask({
|
|
180
|
+
taskId,
|
|
181
|
+
completion: { status: 'failed', resolvedCode: 1, error: err.message },
|
|
182
|
+
commandCleanup,
|
|
183
|
+
terminateProvider: terminateOwnedProviderBoundary,
|
|
184
|
+
updateTask,
|
|
185
|
+
emergencyLog,
|
|
186
|
+
});
|
|
317
187
|
process.exit(1);
|
|
318
188
|
});
|
|
189
|
+
|
|
190
|
+
updateTask(taskId, {
|
|
191
|
+
pid: child.pid,
|
|
192
|
+
processGroupId: process.platform === 'win32' ? null : child.pid,
|
|
193
|
+
terminationStrategy: process.platform === 'win32' ? 'process-tree' : 'process-group',
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (getTask(taskId)?.cancelRequested) {
|
|
197
|
+
crashStarted = true;
|
|
198
|
+
await completePendingWatcherCancellation({
|
|
199
|
+
taskId,
|
|
200
|
+
getTask,
|
|
201
|
+
commandCleanup,
|
|
202
|
+
terminateProvider: terminateOwnedProviderBoundary,
|
|
203
|
+
updateTask,
|
|
204
|
+
emergencyLog,
|
|
205
|
+
});
|
|
206
|
+
process.exit(0);
|
|
207
|
+
}
|