@the-open-engine/zeroshot 6.9.1 → 6.10.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.
- package/cli/commands/inspect.js +1 -0
- package/cli/index.js +1 -1
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/claude.js +15 -2
- 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 +1 -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 +4 -0
- 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 +30 -2
- package/src/agent/agent-task-executor.js +31 -1
- package/src/agent/guidance-queue.js +29 -7
- package/src/agent/provider-session.js +277 -0
- package/src/agent-cli-provider/adapters/claude.ts +18 -3
- 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 +1 -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 +4 -0
- package/src/agent-wrapper.js +44 -8
- package/src/ledger-sequence.js +63 -0
- package/src/ledger.js +114 -29
- package/src/orchestrator.js +40 -2
- package/task-lib/attachable-watcher.js +27 -5
- package/task-lib/commands/resume.js +29 -6
- package/task-lib/commands/status.js +3 -0
- package/task-lib/provider-helper-runtime.js +1 -0
- package/task-lib/provider-session-capture.js +138 -0
- package/task-lib/runner.js +10 -2
- package/task-lib/store.js +58 -6
- package/task-lib/watcher.js +27 -5
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { extractProviderSessionId } from './provider-helper-runtime.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Capture a provider-owned session ID from one complete output line.
|
|
5
|
+
*
|
|
6
|
+
* The caller owns persistence so watcher variants can share parsing without
|
|
7
|
+
* importing each other's process lifecycle. Repeated IDs are idempotent; any
|
|
8
|
+
* differing ID makes the capture permanently ambiguous.
|
|
9
|
+
*/
|
|
10
|
+
export function captureProviderSessionLine({
|
|
11
|
+
providerName,
|
|
12
|
+
line,
|
|
13
|
+
currentSessionId = null,
|
|
14
|
+
observedSessionIds = new Set(currentSessionId ? [currentSessionId] : []),
|
|
15
|
+
sessionIdConflict = false,
|
|
16
|
+
onCapture = () => {},
|
|
17
|
+
onConflict = () => {},
|
|
18
|
+
}) {
|
|
19
|
+
const sessionId = extractProviderSessionId(providerName, line);
|
|
20
|
+
if (!sessionId) {
|
|
21
|
+
return { currentSessionId, sessionIdConflict };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
observedSessionIds.add(sessionId);
|
|
25
|
+
if (sessionIdConflict || observedSessionIds.size > 1) {
|
|
26
|
+
if (!sessionIdConflict) {
|
|
27
|
+
onConflict([...observedSessionIds]);
|
|
28
|
+
}
|
|
29
|
+
return { currentSessionId: null, sessionIdConflict: true };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (sessionId === currentSessionId) {
|
|
33
|
+
return { currentSessionId, sessionIdConflict: false };
|
|
34
|
+
}
|
|
35
|
+
onCapture(sessionId);
|
|
36
|
+
return { currentSessionId: sessionId, sessionIdConflict: false };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function providerSessionCompletionError({
|
|
40
|
+
requestedSessionId,
|
|
41
|
+
currentSessionId,
|
|
42
|
+
sessionIdConflict,
|
|
43
|
+
persistenceError,
|
|
44
|
+
}) {
|
|
45
|
+
if (persistenceError) {
|
|
46
|
+
return `Provider session identity could not be persisted: ${persistenceError.message}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const requested = requestedSessionId?.trim() || null;
|
|
50
|
+
if (!requested) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (sessionIdConflict) {
|
|
54
|
+
return 'Provider continuation emitted conflicting session identities';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const captured = currentSessionId?.trim() || null;
|
|
58
|
+
if (captured === requested) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return captured
|
|
62
|
+
? 'Provider continuation returned a different session identity'
|
|
63
|
+
: 'Provider continuation did not confirm the requested session identity';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createProviderSessionCapture({
|
|
67
|
+
providerName,
|
|
68
|
+
taskId,
|
|
69
|
+
updateTask,
|
|
70
|
+
log,
|
|
71
|
+
requestedSessionId = null,
|
|
72
|
+
initialSessionId = null,
|
|
73
|
+
initialSessionIdConflict = false,
|
|
74
|
+
}) {
|
|
75
|
+
let currentSessionId = initialSessionId;
|
|
76
|
+
let sessionIdConflict = initialSessionIdConflict;
|
|
77
|
+
let persistenceError = null;
|
|
78
|
+
const observedSessionIds = new Set(initialSessionId ? [initialSessionId] : []);
|
|
79
|
+
|
|
80
|
+
function persist(update) {
|
|
81
|
+
try {
|
|
82
|
+
updateTask(taskId, update);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
persistenceError ||= error;
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function captureLine(line) {
|
|
90
|
+
try {
|
|
91
|
+
({ currentSessionId, sessionIdConflict } = captureProviderSessionLine({
|
|
92
|
+
providerName,
|
|
93
|
+
line,
|
|
94
|
+
currentSessionId,
|
|
95
|
+
observedSessionIds,
|
|
96
|
+
sessionIdConflict,
|
|
97
|
+
onCapture: (sessionId) => {
|
|
98
|
+
// Advance memory before persistence so a failed write can never leave
|
|
99
|
+
// this watcher believing the earlier identity is still trustworthy.
|
|
100
|
+
currentSessionId = sessionId;
|
|
101
|
+
persist({ sessionId });
|
|
102
|
+
},
|
|
103
|
+
onConflict: () => {
|
|
104
|
+
currentSessionId = null;
|
|
105
|
+
sessionIdConflict = true;
|
|
106
|
+
persist({
|
|
107
|
+
sessionId: null,
|
|
108
|
+
sessionIdConflict: true,
|
|
109
|
+
resumeIdentityVerified: false,
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
}));
|
|
113
|
+
} catch (error) {
|
|
114
|
+
log(`[${Date.now()}][SESSION] Failed to persist provider session: ${error.message}\n`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function getCompletionError() {
|
|
119
|
+
return providerSessionCompletionError({
|
|
120
|
+
requestedSessionId,
|
|
121
|
+
currentSessionId,
|
|
122
|
+
sessionIdConflict,
|
|
123
|
+
persistenceError,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
captureLine,
|
|
129
|
+
getCompletionError,
|
|
130
|
+
getCompletionUpdate: (resolvedCode) => {
|
|
131
|
+
const error = getCompletionError();
|
|
132
|
+
return {
|
|
133
|
+
resumeIdentityVerified: resolvedCode === 0 && !error,
|
|
134
|
+
...(error ? { sessionId: null, sessionIdConflict: true } : {}),
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
package/task-lib/runner.js
CHANGED
|
@@ -155,7 +155,7 @@ function providerLevelSelection(options) {
|
|
|
155
155
|
return { modelSpec };
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
function buildTaskRecord({ id, prompt, cwd, options, logFile, providerName, modelSpec }) {
|
|
158
|
+
export function buildTaskRecord({ id, prompt, cwd, options, logFile, providerName, modelSpec }) {
|
|
159
159
|
return {
|
|
160
160
|
id,
|
|
161
161
|
prompt: prompt.slice(0, 200) + (prompt.length > 200 ? '...' : ''),
|
|
@@ -163,7 +163,15 @@ function buildTaskRecord({ id, prompt, cwd, options, logFile, providerName, mode
|
|
|
163
163
|
cwd,
|
|
164
164
|
status: 'running',
|
|
165
165
|
pid: null,
|
|
166
|
-
|
|
166
|
+
// Only watcher-observed provider output may populate sessionId. A requested
|
|
167
|
+
// resume ID is diagnostic input, not proof the resumed provider emitted or
|
|
168
|
+
// accepted that session identity.
|
|
169
|
+
sessionId: null,
|
|
170
|
+
sessionIdConflict: false,
|
|
171
|
+
requestedResumeSessionId: options.resume || null,
|
|
172
|
+
// Resumed tasks start fail-closed. Only the watcher terminal transaction
|
|
173
|
+
// may prove that the requested identity completed without conflict.
|
|
174
|
+
resumeIdentityVerified: !options.resume,
|
|
167
175
|
logFile,
|
|
168
176
|
createdAt: new Date().toISOString(),
|
|
169
177
|
updatedAt: new Date().toISOString(),
|
package/task-lib/store.js
CHANGED
|
@@ -12,6 +12,7 @@ import { TASKS_DIR, LOGS_DIR } from './config.js';
|
|
|
12
12
|
import { generateName } from './name-generator.js';
|
|
13
13
|
|
|
14
14
|
const DB_FILE = join(TASKS_DIR, 'store.db');
|
|
15
|
+
export const TASK_STORE_SCHEMA_VERSION = 4;
|
|
15
16
|
|
|
16
17
|
/** @type {Database.Database | null} */
|
|
17
18
|
let db = null;
|
|
@@ -41,6 +42,9 @@ function getDb() {
|
|
|
41
42
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
42
43
|
pid INTEGER,
|
|
43
44
|
session_id TEXT,
|
|
45
|
+
session_id_conflict INTEGER NOT NULL DEFAULT 0,
|
|
46
|
+
requested_resume_session_id TEXT,
|
|
47
|
+
resume_identity_verified INTEGER NOT NULL DEFAULT 0,
|
|
44
48
|
log_file TEXT,
|
|
45
49
|
created_at TEXT NOT NULL,
|
|
46
50
|
updated_at TEXT NOT NULL,
|
|
@@ -77,8 +81,7 @@ function getDb() {
|
|
|
77
81
|
CREATE INDEX IF NOT EXISTS idx_schedules_enabled ON schedules(enabled);
|
|
78
82
|
`);
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
ensureTaskColumn(db, 'termination_strategy', 'TEXT');
|
|
84
|
+
migrateTaskStore(db);
|
|
82
85
|
|
|
83
86
|
return db;
|
|
84
87
|
}
|
|
@@ -90,6 +93,40 @@ function ensureTaskColumn(database, name, definition) {
|
|
|
90
93
|
}
|
|
91
94
|
}
|
|
92
95
|
|
|
96
|
+
export function migrateTaskStore(database) {
|
|
97
|
+
ensureTaskColumn(database, 'process_group_id', 'INTEGER');
|
|
98
|
+
ensureTaskColumn(database, 'termination_strategy', 'TEXT');
|
|
99
|
+
ensureTaskColumn(database, 'requested_resume_session_id', 'TEXT');
|
|
100
|
+
ensureTaskColumn(database, 'session_id_conflict', 'INTEGER NOT NULL DEFAULT 0');
|
|
101
|
+
ensureTaskColumn(database, 'resume_identity_verified', 'INTEGER NOT NULL DEFAULT 0');
|
|
102
|
+
|
|
103
|
+
const version = database.pragma('user_version', { simple: true });
|
|
104
|
+
if (version >= TASK_STORE_SCHEMA_VERSION) return;
|
|
105
|
+
|
|
106
|
+
database.transaction(() => {
|
|
107
|
+
if (version < 2) {
|
|
108
|
+
database
|
|
109
|
+
.prepare(
|
|
110
|
+
`UPDATE tasks
|
|
111
|
+
SET requested_resume_session_id = COALESCE(requested_resume_session_id, session_id),
|
|
112
|
+
session_id = NULL`
|
|
113
|
+
)
|
|
114
|
+
.run();
|
|
115
|
+
}
|
|
116
|
+
if (version < 3) {
|
|
117
|
+
database.prepare('UPDATE tasks SET session_id_conflict = 0').run();
|
|
118
|
+
}
|
|
119
|
+
if (version < 4) {
|
|
120
|
+
database.prepare('UPDATE tasks SET resume_identity_verified = 0').run();
|
|
121
|
+
}
|
|
122
|
+
database.pragma(`user_version = ${TASK_STORE_SCHEMA_VERSION}`);
|
|
123
|
+
})();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function nullable(value) {
|
|
127
|
+
return value || null;
|
|
128
|
+
}
|
|
129
|
+
|
|
93
130
|
export function ensureDirs() {
|
|
94
131
|
if (!existsSync(TASKS_DIR)) mkdirSync(TASKS_DIR, { recursive: true });
|
|
95
132
|
if (!existsSync(LOGS_DIR)) mkdirSync(LOGS_DIR, { recursive: true });
|
|
@@ -112,6 +149,9 @@ function rowToTask(row) {
|
|
|
112
149
|
status: row.status,
|
|
113
150
|
pid: row.pid,
|
|
114
151
|
sessionId: row.session_id,
|
|
152
|
+
sessionIdConflict: Boolean(row.session_id_conflict),
|
|
153
|
+
requestedResumeSessionId: row.requested_resume_session_id,
|
|
154
|
+
resumeIdentityVerified: Boolean(row.resume_identity_verified),
|
|
115
155
|
logFile: row.log_file,
|
|
116
156
|
createdAt: row.created_at,
|
|
117
157
|
updatedAt: row.updated_at,
|
|
@@ -149,11 +189,11 @@ export function saveTasks(tasks) {
|
|
|
149
189
|
const database = getDb();
|
|
150
190
|
const insert = database.prepare(`
|
|
151
191
|
INSERT OR REPLACE INTO tasks (
|
|
152
|
-
id, prompt, full_prompt, cwd, status, pid, session_id, log_file,
|
|
192
|
+
id, prompt, full_prompt, cwd, status, pid, session_id, session_id_conflict, requested_resume_session_id, resume_identity_verified, log_file,
|
|
153
193
|
created_at, updated_at, exit_code, error, provider, model,
|
|
154
194
|
schedule_id, socket_path, attachable, process_group_id, termination_strategy
|
|
155
195
|
) VALUES (
|
|
156
|
-
@id, @prompt, @fullPrompt, @cwd, @status, @pid, @sessionId, @logFile,
|
|
196
|
+
@id, @prompt, @fullPrompt, @cwd, @status, @pid, @sessionId, @sessionIdConflict, @requestedResumeSessionId, @resumeIdentityVerified, @logFile,
|
|
157
197
|
@createdAt, @updatedAt, @exitCode, @error, @provider, @model,
|
|
158
198
|
@scheduleId, @socketPath, @attachable, @processGroupId, @terminationStrategy
|
|
159
199
|
)
|
|
@@ -172,6 +212,9 @@ export function saveTasks(tasks) {
|
|
|
172
212
|
status: task.status || 'pending',
|
|
173
213
|
pid: task.pid || null,
|
|
174
214
|
sessionId: task.sessionId || null,
|
|
215
|
+
sessionIdConflict: task.sessionIdConflict ? 1 : 0,
|
|
216
|
+
requestedResumeSessionId: nullable(task.requestedResumeSessionId),
|
|
217
|
+
resumeIdentityVerified: task.resumeIdentityVerified ? 1 : 0,
|
|
175
218
|
logFile: task.logFile || null,
|
|
176
219
|
createdAt: task.createdAt || new Date().toISOString(),
|
|
177
220
|
updatedAt: task.updatedAt || new Date().toISOString(),
|
|
@@ -240,6 +283,9 @@ export function updateTask(id, updates) {
|
|
|
240
283
|
status = @status,
|
|
241
284
|
pid = @pid,
|
|
242
285
|
session_id = @sessionId,
|
|
286
|
+
session_id_conflict = @sessionIdConflict,
|
|
287
|
+
requested_resume_session_id = @requestedResumeSessionId,
|
|
288
|
+
resume_identity_verified = @resumeIdentityVerified,
|
|
243
289
|
log_file = @logFile,
|
|
244
290
|
updated_at = @updatedAt,
|
|
245
291
|
exit_code = @exitCode,
|
|
@@ -262,6 +308,9 @@ export function updateTask(id, updates) {
|
|
|
262
308
|
status: updated.status || 'pending',
|
|
263
309
|
pid: updated.pid || null,
|
|
264
310
|
sessionId: updated.sessionId || null,
|
|
311
|
+
sessionIdConflict: updated.sessionIdConflict ? 1 : 0,
|
|
312
|
+
requestedResumeSessionId: nullable(updated.requestedResumeSessionId),
|
|
313
|
+
resumeIdentityVerified: updated.resumeIdentityVerified ? 1 : 0,
|
|
265
314
|
logFile: updated.logFile || null,
|
|
266
315
|
updatedAt: updated.updatedAt,
|
|
267
316
|
exitCode: updated.exitCode ?? null,
|
|
@@ -295,11 +344,11 @@ export function addTask(task) {
|
|
|
295
344
|
.prepare(
|
|
296
345
|
`
|
|
297
346
|
INSERT INTO tasks (
|
|
298
|
-
id, prompt, full_prompt, cwd, status, pid, session_id, log_file,
|
|
347
|
+
id, prompt, full_prompt, cwd, status, pid, session_id, session_id_conflict, requested_resume_session_id, resume_identity_verified, log_file,
|
|
299
348
|
created_at, updated_at, exit_code, error, provider, model,
|
|
300
349
|
schedule_id, socket_path, attachable, process_group_id, termination_strategy
|
|
301
350
|
) VALUES (
|
|
302
|
-
@id, @prompt, @fullPrompt, @cwd, @status, @pid, @sessionId, @logFile,
|
|
351
|
+
@id, @prompt, @fullPrompt, @cwd, @status, @pid, @sessionId, @sessionIdConflict, @requestedResumeSessionId, @resumeIdentityVerified, @logFile,
|
|
303
352
|
@createdAt, @updatedAt, @exitCode, @error, @provider, @model,
|
|
304
353
|
@scheduleId, @socketPath, @attachable, @processGroupId, @terminationStrategy
|
|
305
354
|
)
|
|
@@ -313,6 +362,9 @@ export function addTask(task) {
|
|
|
313
362
|
status: fullTask.status || 'pending',
|
|
314
363
|
pid: fullTask.pid || null,
|
|
315
364
|
sessionId: fullTask.sessionId || null,
|
|
365
|
+
sessionIdConflict: fullTask.sessionIdConflict ? 1 : 0,
|
|
366
|
+
requestedResumeSessionId: nullable(fullTask.requestedResumeSessionId),
|
|
367
|
+
resumeIdentityVerified: fullTask.resumeIdentityVerified ? 1 : 0,
|
|
316
368
|
logFile: fullTask.logFile || null,
|
|
317
369
|
createdAt: fullTask.createdAt,
|
|
318
370
|
updatedAt: fullTask.updatedAt,
|
package/task-lib/watcher.js
CHANGED
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
import { spawn } from 'child_process';
|
|
9
9
|
import { appendFileSync, unlinkSync } from 'fs';
|
|
10
10
|
import { unlink } from 'fs/promises';
|
|
11
|
-
import { updateTask } from './store.js';
|
|
11
|
+
import { getTask, updateTask } from './store.js';
|
|
12
12
|
import {
|
|
13
13
|
detectProviderFatalError,
|
|
14
14
|
detectProviderStreamingModeError,
|
|
15
15
|
recoverProviderStructuredOutput,
|
|
16
16
|
supportsProviderStructuredOutputRecovery,
|
|
17
17
|
} from './provider-helper-runtime.js';
|
|
18
|
+
import { createProviderSessionCapture } from './provider-session-capture.js';
|
|
18
19
|
import { createRequire } from 'module';
|
|
19
20
|
|
|
20
21
|
const require = createRequire(import.meta.url);
|
|
@@ -36,6 +37,17 @@ function log(msg) {
|
|
|
36
37
|
|
|
37
38
|
const providerName = normalizeProviderName(config.provider || 'claude');
|
|
38
39
|
const enableRecovery = supportsProviderStructuredOutputRecovery(providerName);
|
|
40
|
+
const storedTask = getTask(taskId);
|
|
41
|
+
const providerSessionCapture = createProviderSessionCapture({
|
|
42
|
+
providerName,
|
|
43
|
+
taskId,
|
|
44
|
+
updateTask,
|
|
45
|
+
log,
|
|
46
|
+
requestedSessionId: storedTask?.requestedResumeSessionId || null,
|
|
47
|
+
initialSessionId: storedTask?.sessionId || null,
|
|
48
|
+
initialSessionIdConflict: storedTask?.sessionIdConflict === true,
|
|
49
|
+
});
|
|
50
|
+
const maybeCaptureProviderSession = providerSessionCapture.captureLine;
|
|
39
51
|
|
|
40
52
|
const env = { ...process.env, ...(commandSpec.env || {}) };
|
|
41
53
|
const command = commandSpec.binary;
|
|
@@ -136,6 +148,7 @@ function maybeCaptureStructuredOutput(line) {
|
|
|
136
148
|
function handleSilentJsonLines(lines, timestamp) {
|
|
137
149
|
for (const line of lines) {
|
|
138
150
|
if (!line.trim()) continue;
|
|
151
|
+
maybeCaptureProviderSession(line);
|
|
139
152
|
maybeHandleFatalError(line, timestamp);
|
|
140
153
|
if (captureStreamingError(line, timestamp)) {
|
|
141
154
|
continue;
|
|
@@ -146,6 +159,7 @@ function handleSilentJsonLines(lines, timestamp) {
|
|
|
146
159
|
|
|
147
160
|
function handleStreamingLines(lines, timestamp) {
|
|
148
161
|
for (const line of lines) {
|
|
162
|
+
maybeCaptureProviderSession(line);
|
|
149
163
|
maybeHandleFatalError(line, timestamp);
|
|
150
164
|
if (captureStreamingError(line, timestamp)) {
|
|
151
165
|
continue;
|
|
@@ -159,6 +173,7 @@ function flushStdoutBuffer(timestamp) {
|
|
|
159
173
|
return;
|
|
160
174
|
}
|
|
161
175
|
|
|
176
|
+
maybeCaptureProviderSession(stdoutBuffer);
|
|
162
177
|
if (!enableRecovery) {
|
|
163
178
|
if (!silentJsonMode) {
|
|
164
179
|
log(`[${timestamp}]${stdoutBuffer}\n`);
|
|
@@ -282,18 +297,25 @@ child.on('close', async (code, signal) => {
|
|
|
282
297
|
log(finalResultJson + '\n');
|
|
283
298
|
}
|
|
284
299
|
|
|
285
|
-
|
|
300
|
+
const sessionIdentityError = providerSessionCapture.getCompletionError();
|
|
301
|
+
const resolvedCode =
|
|
302
|
+
fatalError || sessionIdentityError ? 1 : recovered?.payload ? 0 : code;
|
|
303
|
+
const status = resolvedCode === 0 ? 'completed' : 'failed';
|
|
304
|
+
|
|
305
|
+
writeCompletionFooter(resolvedCode, signal);
|
|
286
306
|
await cleanupCommandSpec();
|
|
287
307
|
|
|
288
|
-
const resolvedCode = fatalError ? 1 : recovered?.payload ? 0 : code;
|
|
289
|
-
const status = resolvedCode === 0 ? 'completed' : 'failed';
|
|
290
308
|
try {
|
|
291
309
|
await updateTask(taskId, {
|
|
292
310
|
status,
|
|
293
311
|
pid: null,
|
|
294
312
|
processGroupId: null,
|
|
295
313
|
exitCode: resolvedCode,
|
|
296
|
-
|
|
314
|
+
...providerSessionCapture.getCompletionUpdate(resolvedCode),
|
|
315
|
+
error:
|
|
316
|
+
fatalError ||
|
|
317
|
+
sessionIdentityError ||
|
|
318
|
+
(resolvedCode !== 0 && signal ? `Killed by ${signal}` : null),
|
|
297
319
|
});
|
|
298
320
|
} catch (updateError) {
|
|
299
321
|
log(`[${Date.now()}][ERROR] Failed to update task status: ${updateError.message}\n`);
|