@loicngr/kobo 1.11.43 → 1.11.45
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/AGENTS.md +74 -1
- package/CHANGELOG.md +11 -0
- package/README.md +13 -1
- package/dist/mcp-server/kobo-tasks-handlers.js +33 -75
- package/dist/mcp-server/kobo-tasks-server.js +82 -9
- package/dist/server/db/auto-loop-schema.js +44 -0
- package/dist/server/db/migrations.js +18 -0
- package/dist/server/db/review-return-schema.js +10 -0
- package/dist/server/db/schema.js +8 -1
- package/dist/server/db/session-handoff-schema.js +24 -0
- package/dist/server/index.js +12 -9
- package/dist/server/routes/auto-loop-messages.js +51 -0
- package/dist/server/routes/documents.js +1 -1
- package/dist/server/routes/workspaces.js +119 -145
- package/dist/server/services/agent/engines/claude-code/engine.js +16 -0
- package/dist/server/services/agent/engines/codex/engine.js +92 -31
- package/dist/server/services/agent/orchestrator.js +298 -96
- package/dist/server/services/agent/session-controller.js +42 -2
- package/dist/server/services/auto-loop-message-service.js +79 -0
- package/dist/server/services/auto-loop-service.js +225 -86
- package/dist/server/services/auto-loop-state-service.js +30 -0
- package/dist/server/services/cron-service.js +30 -16
- package/dist/server/services/engine-handoff-service.js +5 -5
- package/dist/server/services/quota-backoff-service.js +42 -10
- package/dist/server/services/review-return-service.js +64 -0
- package/dist/server/services/review-service.js +187 -0
- package/dist/server/services/session-activity-service.js +17 -0
- package/dist/server/services/session-handoff-runtime.js +26 -0
- package/dist/server/services/session-handoff-service.js +479 -0
- package/dist/server/services/settings-service.js +3 -3
- package/dist/server/services/task-mutations.js +186 -0
- package/dist/server/services/wakeup-service.js +24 -1
- package/dist/server/services/workspace-message-service.js +16 -2
- package/dist/server/services/workspace-service.js +28 -36
- package/dist/server/utils/workspace-lifecycle-guard.js +35 -20
- package/dist/shared/auto-loop-prompts.js +22 -11
- package/dist/shared/auto-loop-types.js +1 -0
- package/dist/shared/document-roots.js +2 -0
- package/dist/shared/review.js +6 -0
- package/dist/shared/session-handoff.js +4 -0
- package/dist/shared/task-verification.js +11 -0
- package/package.json +12 -12
- package/src/client/dist/pwa/assets/ActivityFeed-CLXjdqS8.js +8 -0
- package/src/client/dist/pwa/assets/{ChangelogPage-DX1WGFea.js → ChangelogPage-CnXTnFhe.js} +1 -1
- package/src/client/dist/pwa/assets/{ClosePopup-BqW5dy-O.js → ClosePopup-CWjIRHxu.js} +1 -1
- package/src/client/dist/pwa/assets/{ComparisonPanel-B4ne55Xj.js → ComparisonPanel-CKylN45q.js} +2 -2
- package/src/client/dist/pwa/assets/CreatePage-SoCcvxuD.js +2 -0
- package/src/client/dist/pwa/assets/{DashboardPage-Cv6HJEcO.js → DashboardPage-CZQLfoV1.js} +1 -1
- package/src/client/dist/pwa/assets/{DiffViewer-COwGxPt6.js → DiffViewer-Csf3WmLc.js} +4 -4
- package/src/client/dist/pwa/assets/{DocumentsPanel-DCXpLLb7.js → DocumentsPanel-BeCttB-x.js} +1 -1
- package/src/client/dist/pwa/assets/{DrawerToggleButton-DRL3RCkM.js → DrawerToggleButton-BJBBYeOc.js} +1 -1
- package/src/client/dist/pwa/assets/GitPanel-B-ZHgghQ.js +3 -0
- package/src/client/dist/pwa/assets/{HealthPage-XrOuMoDJ.js → HealthPage-l88TEk-k.js} +1 -1
- package/src/client/dist/pwa/assets/MainLayout-BAAQLq95.css +1 -0
- package/src/client/dist/pwa/assets/MainLayout-Cf4Q9x1u.js +2 -0
- package/src/client/dist/pwa/assets/{QBadge-DbkxGtht.js → QBadge-FWC1zDCq.js} +1 -1
- package/src/client/dist/pwa/assets/{QBanner-DhqH95GW.js → QBanner-CcRI_QIy.js} +1 -1
- package/src/client/dist/pwa/assets/QBtn-DEdx_Y1V.js +1 -0
- package/src/client/dist/pwa/assets/{QBtnDropdown-163oM6T_.js → QBtnDropdown-44Mj-y_1.js} +1 -1
- package/src/client/dist/pwa/assets/{QBtnGroup-DELKYo3u.js → QBtnGroup-CAoD6G0Q.js} +1 -1
- package/src/client/dist/pwa/assets/{QBtnToggle-BI3k3Ch2.js → QBtnToggle-C_hQ2UCQ.js} +1 -1
- package/src/client/dist/pwa/assets/{QChip-owciRuZu.js → QChip-DCPUhqXX.js} +1 -1
- package/src/client/dist/pwa/assets/QDrawer-B4qHw8wt.js +1 -0
- package/src/client/dist/pwa/assets/QExpansionItem-Bok1m8Wi.js +1 -0
- package/src/client/dist/pwa/assets/{QItemSection-D_5_0UrX.js → QItemSection-BhqphaH6.js} +1 -1
- package/src/client/dist/pwa/assets/{QList-qupzuEqO.js → QList-CHm4JNP0.js} +1 -1
- package/src/client/dist/pwa/assets/{QMarkupTable-B9QuKtK_.js → QMarkupTable-CNpiA9oq.js} +1 -1
- package/src/client/dist/pwa/assets/{QPage-DaX9sMQC.js → QPage-BXBw2Alx.js} +1 -1
- package/src/client/dist/pwa/assets/QSelect-BbtD5Iks.js +1 -0
- package/src/client/dist/pwa/assets/{QSpinnerDots-DnCV3VzI.js → QSpinnerDots-BkFiCsH3.js} +1 -1
- package/src/client/dist/pwa/assets/{QTooltip-BQWY7aeM.js → QTooltip-BDihn2aY.js} +1 -1
- package/src/client/dist/pwa/assets/SchedulePanel-DiWCDNZ8.js +1 -0
- package/src/client/dist/pwa/assets/{SearchPage-DOHkP5lj.js → SearchPage-BwZ2TX1G.js} +1 -1
- package/src/client/dist/pwa/assets/SettingsPage-BUh0WfNf.js +9 -0
- package/src/client/dist/pwa/assets/{SplitWorkspacePage-DL4kp9Ah.js → SplitWorkspacePage-Bg6nKBcA.js} +1 -1
- package/src/client/dist/pwa/assets/{TerminalPanel-DHDnyiYh.js → TerminalPanel-Cq-FR7A7.js} +9 -9
- package/src/client/dist/pwa/assets/{TouchPan-BnHXDR6F.js → TouchPan-BQ1cvzRC.js} +1 -1
- package/src/client/dist/pwa/assets/{TourReplayButton-ClVcB6mO.js → TourReplayButton-Dz1yEz_z.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspaceAttentionLabels-Bxe6oIJI.js → WorkspaceAttentionLabels-BCVOP8vp.js} +1 -1
- package/src/client/dist/pwa/assets/WorkspacePage-BbUvR1Vf.css +1 -0
- package/src/client/dist/pwa/assets/WorkspacePage-Cl8gap4b.js +4 -0
- package/src/client/dist/pwa/assets/{browser-Bkx4fbxl.js → browser-BmWgBx9M.js} +1 -1
- package/src/client/dist/pwa/assets/build-path-tree-6EjBmYYY.js +1 -0
- package/src/client/dist/pwa/assets/de-COObokpu.js +17 -0
- package/src/client/dist/pwa/assets/documents-BZ8bwcjr.js +1 -0
- package/src/client/dist/pwa/assets/{editor.api-DreEG0If.js → editor.api-CK7--ckY.js} +1 -1
- package/src/client/dist/pwa/assets/editor.worker-iO5iN0DB.js +33 -0
- package/src/client/dist/pwa/assets/efforts-Bw0o2Bud.js +9 -0
- package/src/client/dist/pwa/assets/engineFeatures-B-9jHocY.css +1 -0
- package/src/client/dist/pwa/assets/engineFeatures-DQO9HHXI.js +1 -0
- package/src/client/dist/pwa/assets/es-By2Q9Tca.js +17 -0
- package/src/client/dist/pwa/assets/expand-template-C8VyjASo.js +1 -0
- package/src/client/dist/pwa/assets/{format-B7z3F-Mw.js → format-LtTLLitJ.js} +3 -3
- package/src/client/dist/pwa/assets/{formatters-BTBiU0v3.js → formatters-Bl9zA7Ai.js} +1 -1
- package/src/client/dist/pwa/assets/fr-D-sd0ddn.js +17 -0
- package/src/client/dist/pwa/assets/{freemarker2-BZNTVQ3K.js → freemarker2-DxezMaje.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-k8tN0dI3.js → handlebars-DK_Bj6Rx.js} +1 -1
- package/src/client/dist/pwa/assets/{html-iZL3bbXq.js → html-DFfsfStD.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-aDWy4KHt.js +1 -0
- package/src/client/dist/pwa/assets/index-0wtBh1Xt.css +1 -0
- package/src/client/dist/pwa/assets/index-BLd8DAzg.js +20 -0
- package/src/client/dist/pwa/assets/it-CeK5lyIA.js +17 -0
- package/src/client/dist/pwa/assets/{javascript-TV6gg0MO.js → javascript-DuNbcaL7.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-Cifa5ujF.js → liquid-CM3JKyhv.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-Bnj_8yGE.js → mdx-BG3zYwNF.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco-contributions-pEh0n9Wk.js → monaco-contributions-DLscZGH-.js} +1 -1
- package/src/client/dist/pwa/assets/monaco.contribution-DnhZHwXT.js +2 -0
- package/src/client/dist/pwa/assets/{network-auth-3_5p4fdc.js → network-auth-BJiPbO5l.js} +1 -1
- package/src/client/dist/pwa/assets/{notify-theme-D_nf9AeF.js → notify-theme-D4p3qPkp.js} +1 -1
- package/src/client/dist/pwa/assets/{pending-diff-open-CUnxC1xw.js → pending-diff-open-dYIFdS4r.js} +1 -1
- package/src/client/dist/pwa/assets/permissionModes-B7fuUegU.js +36 -0
- package/src/client/dist/pwa/assets/{pinia-BKqmoVru.js → pinia-CTJ3OfP3.js} +1 -1
- package/src/client/dist/pwa/assets/{private.use-form-C7umbGO-.js → private.use-form-Bk8ti264.js} +1 -1
- package/src/client/dist/pwa/assets/{project-color-CihwpDAA.js → project-color-R_1je4vW.js} +1 -1
- package/src/client/dist/pwa/assets/{python-DOoNBSNL.js → python-COs6pHk2.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-Bvq5vVpJ.js → razor-Jl1VqMsk.js} +1 -1
- package/src/client/dist/pwa/assets/reactivity.esm-bundler-Bo9anJjg.js +1 -0
- package/src/client/dist/pwa/assets/render-chat-markdown-AaOo0Tgq.js +67 -0
- package/src/client/dist/pwa/assets/{runtime-core.esm-bundler-CDnVUfeZ.js → runtime-core.esm-bundler-CNFKm4ij.js} +1 -1
- package/src/client/dist/pwa/assets/{typescript-VVzxoeyM.js → typescript-BU_TUuAM.js} +1 -1
- package/src/client/dist/pwa/assets/{use-checkbox-FnauAgTp.js → use-checkbox-CnvHBhSK.js} +1 -1
- package/src/client/dist/pwa/assets/{use-id-BIuaWKkB.js → use-id-wXd4c8FA.js} +1 -1
- package/src/client/dist/pwa/assets/use-is-mobile-DpKI3XMy.js +1 -0
- package/src/client/dist/pwa/assets/{use-search-index-status-DtLzygcy.js → use-search-index-status-Bluf9L99.js} +1 -1
- package/src/client/dist/pwa/assets/use-slide-transition-CiaDm-tR.js +1 -0
- package/src/client/dist/pwa/assets/use-tours-CVn874kD.js +2 -0
- package/src/client/dist/pwa/assets/{workspace-templates-1ui80Kqo.js → workspace-templates-BCKR8TXr.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-CzGFIxQa.js → xml-gFFsobxE.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-Bl7nDJeC.js → yaml-DaXaqaGD.js} +1 -1
- package/src/client/dist/pwa/index.html +10 -10
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/mcp-server/README.md +8 -1
- package/src/mcp-server/kobo-tasks-handlers.ts +72 -105
- package/src/mcp-server/kobo-tasks-server.ts +94 -9
- package/src/client/dist/pwa/assets/ActivityFeed-D1e-zV3O.js +0 -8
- package/src/client/dist/pwa/assets/CreatePage-Bx_GpUni.js +0 -2
- package/src/client/dist/pwa/assets/GitPanel-C9mz4GXF.js +0 -3
- package/src/client/dist/pwa/assets/MainLayout-BwV89R-l.css +0 -1
- package/src/client/dist/pwa/assets/MainLayout-CwWavQjV.js +0 -2
- package/src/client/dist/pwa/assets/QBtn-CZaBclNw.js +0 -1
- package/src/client/dist/pwa/assets/QDrawer-DNEj8wLZ.js +0 -1
- package/src/client/dist/pwa/assets/QSelect-BUlOQe5k.js +0 -1
- package/src/client/dist/pwa/assets/SchedulePanel-Bos94OyR.js +0 -1
- package/src/client/dist/pwa/assets/SettingsPage-DtGsd0XH.js +0 -9
- package/src/client/dist/pwa/assets/WorkspacePage-Clc4Fl3B.js +0 -4
- package/src/client/dist/pwa/assets/WorkspacePage-DCg9zSxz.css +0 -1
- package/src/client/dist/pwa/assets/build-path-tree-BpSY7N5O.js +0 -1
- package/src/client/dist/pwa/assets/de-BU0dILBs.js +0 -17
- package/src/client/dist/pwa/assets/documents-BIlWh3o-.js +0 -1
- package/src/client/dist/pwa/assets/editor.worker-CwQAd_xr.js +0 -30
- package/src/client/dist/pwa/assets/efforts-D_yTzymv.js +0 -9
- package/src/client/dist/pwa/assets/engineFeatures-C33LTVkK.js +0 -1
- package/src/client/dist/pwa/assets/es-CNp0s9na.js +0 -17
- package/src/client/dist/pwa/assets/expand-template-wRhAXT-1.js +0 -1
- package/src/client/dist/pwa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
- package/src/client/dist/pwa/assets/fr-RqJyWSdm.js +0 -17
- package/src/client/dist/pwa/assets/i18n--PCet00a.js +0 -1
- package/src/client/dist/pwa/assets/index-CPfrY38K.js +0 -20
- package/src/client/dist/pwa/assets/index-CUoUy6tH.css +0 -1
- package/src/client/dist/pwa/assets/it-DSxNuCwL.js +0 -17
- package/src/client/dist/pwa/assets/monaco.contribution-D7NmJUJH.js +0 -2
- package/src/client/dist/pwa/assets/permissionModes-DC51Pz84.js +0 -36
- package/src/client/dist/pwa/assets/reactivity.esm-bundler-DEJj9cFv.js +0 -1
- package/src/client/dist/pwa/assets/render-chat-markdown-BQ_saiy5.js +0 -67
- package/src/client/dist/pwa/assets/use-is-mobile-CWjqe8gC.js +0 -1
- package/src/client/dist/pwa/assets/use-tours-BKeYrJaQ.js +0 -2
|
@@ -124,14 +124,41 @@ export function createCodexEngine() {
|
|
|
124
124
|
});
|
|
125
125
|
void childFailurePromise.catch(() => { });
|
|
126
126
|
const child = spawnAppServer({ cwd: options.workingDir, env: options.env, signal: abortController.signal });
|
|
127
|
+
let childExited = false;
|
|
128
|
+
let resolveChildExited;
|
|
129
|
+
const childExitedPromise = new Promise((resolve) => {
|
|
130
|
+
resolveChildExited = resolve;
|
|
131
|
+
});
|
|
132
|
+
const confirmChildExited = () => {
|
|
133
|
+
childExited = true;
|
|
134
|
+
resolveChildExited();
|
|
135
|
+
};
|
|
136
|
+
let shutdownPromise;
|
|
137
|
+
const terminateChild = () => {
|
|
138
|
+
if (childExited)
|
|
139
|
+
return Promise.resolve();
|
|
140
|
+
shutdownPromise ??= (async () => {
|
|
141
|
+
if (!(await signalAndWaitForExit(child, 'SIGTERM', CODEX_FORCE_KILL_TIMEOUT_MS))) {
|
|
142
|
+
console.warn('[codex] app-server ignored SIGTERM — sending SIGKILL');
|
|
143
|
+
if (!(await signalAndWaitForExit(child, 'SIGKILL', CODEX_FORCE_KILL_TIMEOUT_MS))) {
|
|
144
|
+
throw new Error('Codex app-server did not exit after SIGKILL');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
confirmChildExited();
|
|
148
|
+
})();
|
|
149
|
+
return shutdownPromise;
|
|
150
|
+
};
|
|
127
151
|
child.on('error', (error) => {
|
|
128
152
|
const isExpectedAbort = abortController.signal.aborted && (error.code === 'ABORT_ERR' || error.name === 'AbortError');
|
|
129
153
|
if (isExpectedAbort)
|
|
130
154
|
return;
|
|
155
|
+
if (child.pid === undefined)
|
|
156
|
+
confirmChildExited();
|
|
131
157
|
console.error('[codex] child process error:', error);
|
|
132
158
|
rejectChildFailure(error);
|
|
133
159
|
});
|
|
134
160
|
child.once('exit', (code, signal) => {
|
|
161
|
+
confirmChildExited();
|
|
135
162
|
if (!iteratorRunning || abortController.signal.aborted)
|
|
136
163
|
return;
|
|
137
164
|
const detail = code === null ? `signal ${signal ?? 'unknown'}` : `code ${code}`;
|
|
@@ -154,6 +181,7 @@ export function createCodexEngine() {
|
|
|
154
181
|
let rejectTurnDone;
|
|
155
182
|
const activeSubagentThreads = new Map();
|
|
156
183
|
let waitingForBackgroundSubagents = false;
|
|
184
|
+
let watchdogEnded = false;
|
|
157
185
|
const turnDonePromise = new Promise((resolve, reject) => {
|
|
158
186
|
resolveTurnDone = resolve;
|
|
159
187
|
rejectTurnDone = reject;
|
|
@@ -169,6 +197,8 @@ export function createCodexEngine() {
|
|
|
169
197
|
// deadline tracks the last activity, not the start of the wait.
|
|
170
198
|
const armSubagentStallWatchdog = () => {
|
|
171
199
|
clearSubagentStallWatchdog();
|
|
200
|
+
if (!waitingForBackgroundSubagents || pendingByCallId.size > 0 || activeSubagentThreads.size === 0)
|
|
201
|
+
return;
|
|
172
202
|
subagentStallTimer = setTimeout(() => {
|
|
173
203
|
subagentStallTimer = undefined;
|
|
174
204
|
console.warn(`[codex-engine] Background subagent thread(s) still tracked active ${CODEX_SUBAGENT_STALL_TIMEOUT_MS}ms after the turn completed — forcing session drain.`);
|
|
@@ -176,7 +206,13 @@ export function createCodexEngine() {
|
|
|
176
206
|
// Forced, unclean termination (a thread may still be alive
|
|
177
207
|
// server-side) — never report it as a normal completion, so
|
|
178
208
|
// auto-loop doesn't treat an orphaned run as forward progress.
|
|
179
|
-
|
|
209
|
+
watchdogEnded = true;
|
|
210
|
+
safeEmit({
|
|
211
|
+
kind: 'error',
|
|
212
|
+
category: 'other',
|
|
213
|
+
code: 'subagent_idle_timeout',
|
|
214
|
+
message: 'Session force-ended: background subagents stopped reporting activity (watchdog).',
|
|
215
|
+
});
|
|
180
216
|
if (waitingForBackgroundSubagents)
|
|
181
217
|
resolveTurnDone();
|
|
182
218
|
}, CODEX_SUBAGENT_STALL_TIMEOUT_MS);
|
|
@@ -209,8 +245,13 @@ export function createCodexEngine() {
|
|
|
209
245
|
const turnLiveness = createTurnLiveness({
|
|
210
246
|
timeoutMs: CODEX_TURN_IDLE_TIMEOUT_MS,
|
|
211
247
|
onTimeout() {
|
|
212
|
-
|
|
213
|
-
safeEmit({
|
|
248
|
+
watchdogEnded = true;
|
|
249
|
+
safeEmit({
|
|
250
|
+
kind: 'error',
|
|
251
|
+
category: 'other',
|
|
252
|
+
code: 'stream_idle_timeout',
|
|
253
|
+
message: 'Codex stopped reporting activity for this turn',
|
|
254
|
+
});
|
|
214
255
|
rejectTurnDone(new CodexTurnTimeoutError());
|
|
215
256
|
},
|
|
216
257
|
});
|
|
@@ -221,6 +262,21 @@ export function createCodexEngine() {
|
|
|
221
262
|
rejectTurnDone(err);
|
|
222
263
|
});
|
|
223
264
|
const fileChanges = new Map();
|
|
265
|
+
const isParentTurn = (threadId, turnId) => (!threadId || threadId === discoveredSessionId) && (!activeTurnId || !turnId || turnId === activeTurnId);
|
|
266
|
+
const otherTurnMappers = new Map();
|
|
267
|
+
const mapTurnEvents = (notification, map) => {
|
|
268
|
+
if (isParentTurn(notification.threadId, notification.turnId))
|
|
269
|
+
return map(mapperState);
|
|
270
|
+
const key = JSON.stringify([notification.threadId, notification.turnId]);
|
|
271
|
+
let state = otherTurnMappers.get(key);
|
|
272
|
+
if (!state) {
|
|
273
|
+
state = createMapperState();
|
|
274
|
+
otherTurnMappers.set(key, state);
|
|
275
|
+
}
|
|
276
|
+
// Child output remains visible, but its failures/compaction/markers
|
|
277
|
+
// cannot control the parent session or its auto-loop lifecycle.
|
|
278
|
+
return map(state).filter((event) => event.kind !== 'error' && !event.kind.startsWith('session:'));
|
|
279
|
+
};
|
|
224
280
|
const itemKey = (threadId, turnId, itemId) => JSON.stringify([threadId, turnId, itemId]);
|
|
225
281
|
const client = createAppServerClient({
|
|
226
282
|
stdin: child.stdin,
|
|
@@ -228,6 +284,10 @@ export function createCodexEngine() {
|
|
|
228
284
|
clientInfo: { name: 'kobo', version: getPackageVersion() },
|
|
229
285
|
onNotification(method, params) {
|
|
230
286
|
turnLiveness.activity();
|
|
287
|
+
const notification = params;
|
|
288
|
+
if (notification?.threadId && activeSubagentThreads.has(notification.threadId)) {
|
|
289
|
+
armSubagentStallWatchdog();
|
|
290
|
+
}
|
|
231
291
|
// Ignored notifications — harmless bookkeeping by the server
|
|
232
292
|
if (method === 'mcpServer/startupStatus/updated') {
|
|
233
293
|
const status = (params ?? {});
|
|
@@ -257,7 +317,7 @@ export function createCodexEngine() {
|
|
|
257
317
|
const n = params;
|
|
258
318
|
if (n.item.type === 'fileChange')
|
|
259
319
|
fileChanges.set(itemKey(n.threadId, n.turnId, n.item.id), n.item);
|
|
260
|
-
const events = handleItemStarted(n.item,
|
|
320
|
+
const events = mapTurnEvents(n, (state) => handleItemStarted(n.item, state));
|
|
261
321
|
for (const ev of events)
|
|
262
322
|
safeEmit(ev);
|
|
263
323
|
if (n.item.type === 'collabAgentToolCall' && n.item.tool === 'spawnAgent') {
|
|
@@ -278,7 +338,7 @@ export function createCodexEngine() {
|
|
|
278
338
|
const n = params;
|
|
279
339
|
if (n.item.type === 'fileChange')
|
|
280
340
|
fileChanges.set(itemKey(n.threadId, n.turnId, n.item.id), n.item);
|
|
281
|
-
const events = handleItemCompleted(n.item,
|
|
341
|
+
const events = mapTurnEvents(n, (state) => handleItemCompleted(n.item, state));
|
|
282
342
|
for (const ev of events)
|
|
283
343
|
safeEmit(ev);
|
|
284
344
|
if (n.item.type === 'collabAgentToolCall') {
|
|
@@ -302,7 +362,7 @@ export function createCodexEngine() {
|
|
|
302
362
|
}
|
|
303
363
|
if (method === 'item/agentMessage/delta') {
|
|
304
364
|
const n = params;
|
|
305
|
-
for (const ev of handleAgentMessageDelta(n,
|
|
365
|
+
for (const ev of mapTurnEvents(n, (state) => handleAgentMessageDelta(n, state)))
|
|
306
366
|
safeEmit(ev);
|
|
307
367
|
return;
|
|
308
368
|
}
|
|
@@ -313,9 +373,9 @@ export function createCodexEngine() {
|
|
|
313
373
|
if (threadId === n.threadId && turnId === n.turn.id)
|
|
314
374
|
fileChanges.delete(key);
|
|
315
375
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
376
|
+
if (isParentTurn(n.threadId, n.turn.id)) {
|
|
377
|
+
for (const ev of handleTurnCompleted(n, mapperState))
|
|
378
|
+
safeEmit(ev);
|
|
319
379
|
if (n.turn?.status === 'completed' && activeSubagentThreads.size > 0) {
|
|
320
380
|
waitingForBackgroundSubagents = true;
|
|
321
381
|
turnLiveness.pause();
|
|
@@ -349,6 +409,8 @@ export function createCodexEngine() {
|
|
|
349
409
|
}
|
|
350
410
|
if (method === 'error') {
|
|
351
411
|
const n = params;
|
|
412
|
+
if (!isParentTurn(n.threadId, n.turnId))
|
|
413
|
+
return;
|
|
352
414
|
if (n.willRetry)
|
|
353
415
|
return;
|
|
354
416
|
const msg = n?.error?.message ?? 'unknown error';
|
|
@@ -371,6 +433,7 @@ export function createCodexEngine() {
|
|
|
371
433
|
register(callId, pending) {
|
|
372
434
|
pendingByCallId.set(callId, pending);
|
|
373
435
|
turnLiveness.pause();
|
|
436
|
+
clearSubagentStallWatchdog();
|
|
374
437
|
},
|
|
375
438
|
respondError: (reqId, code, message) => client.peer.respondError(reqId, code, message),
|
|
376
439
|
respond: (reqId, result) => client.peer.respond(reqId, result),
|
|
@@ -444,7 +507,9 @@ export function createCodexEngine() {
|
|
|
444
507
|
? 'error'
|
|
445
508
|
: mapperState.sawTurnInterrupted
|
|
446
509
|
? 'killed'
|
|
447
|
-
:
|
|
510
|
+
: watchdogEnded
|
|
511
|
+
? 'watchdog'
|
|
512
|
+
: 'completed';
|
|
448
513
|
safeEmit({
|
|
449
514
|
kind: 'session:ended',
|
|
450
515
|
reason,
|
|
@@ -465,7 +530,11 @@ export function createCodexEngine() {
|
|
|
465
530
|
safeEmit({ kind: 'session:ended', reason: 'killed', exitCode: null });
|
|
466
531
|
}
|
|
467
532
|
else if (error.name === 'CodexTurnTimeoutError') {
|
|
468
|
-
safeEmit({
|
|
533
|
+
safeEmit({
|
|
534
|
+
kind: 'session:ended',
|
|
535
|
+
reason: mapperState.sawErrorResult ? 'error' : 'watchdog',
|
|
536
|
+
exitCode: null,
|
|
537
|
+
});
|
|
469
538
|
}
|
|
470
539
|
else if (QUOTA_PATTERN.test(message)) {
|
|
471
540
|
tryEmitQuota(mapperState, safeEmit, message);
|
|
@@ -497,18 +566,16 @@ export function createCodexEngine() {
|
|
|
497
566
|
}
|
|
498
567
|
}
|
|
499
568
|
fileChanges.clear();
|
|
569
|
+
otherTurnMappers.clear();
|
|
500
570
|
pendingByCallId.clear();
|
|
501
571
|
pendingToolCalls.clear();
|
|
502
572
|
client.close();
|
|
503
|
-
|
|
504
|
-
child.kill('SIGTERM');
|
|
505
|
-
}
|
|
506
|
-
catch {
|
|
507
|
-
// best-effort
|
|
508
|
-
}
|
|
573
|
+
await terminateChild().catch((error) => console.error('[codex] runtime shutdown unconfirmed:', error));
|
|
509
574
|
}
|
|
510
575
|
})();
|
|
511
576
|
const engineProcess = {
|
|
577
|
+
ready: readyPromise,
|
|
578
|
+
closed: Promise.all([iteratorPromise, childExitedPromise]).then(() => { }),
|
|
512
579
|
get pid() {
|
|
513
580
|
return child.pid;
|
|
514
581
|
},
|
|
@@ -516,7 +583,7 @@ export function createCodexEngine() {
|
|
|
516
583
|
return discoveredSessionId;
|
|
517
584
|
},
|
|
518
585
|
isAlive() {
|
|
519
|
-
return iteratorRunning;
|
|
586
|
+
return iteratorRunning || !childExited;
|
|
520
587
|
},
|
|
521
588
|
sendMessage(text) {
|
|
522
589
|
const steer = async () => {
|
|
@@ -581,17 +648,7 @@ export function createCodexEngine() {
|
|
|
581
648
|
catch {
|
|
582
649
|
// swallow
|
|
583
650
|
}
|
|
584
|
-
|
|
585
|
-
// guarantee: a wedged `codex app-server` that ignores it outlives the
|
|
586
|
-
// stop, the archive, the delete and Kōbō's own shutdown, holding the
|
|
587
|
-
// worktree and its share of the model quota, with nothing left in
|
|
588
|
-
// this process tracking it.
|
|
589
|
-
if (!(await signalAndWaitForExit(child, 'SIGTERM', CODEX_FORCE_KILL_TIMEOUT_MS))) {
|
|
590
|
-
console.warn('[codex] app-server ignored SIGTERM — sending SIGKILL');
|
|
591
|
-
if (!(await signalAndWaitForExit(child, 'SIGKILL', CODEX_FORCE_KILL_TIMEOUT_MS))) {
|
|
592
|
-
throw new Error('Codex app-server did not exit after SIGKILL');
|
|
593
|
-
}
|
|
594
|
-
}
|
|
651
|
+
await terminateChild();
|
|
595
652
|
},
|
|
596
653
|
resolvePendingUserInput(callId, response) {
|
|
597
654
|
const pending = pendingByCallId.get(callId);
|
|
@@ -601,8 +658,12 @@ export function createCodexEngine() {
|
|
|
601
658
|
// Only resume the idle-timeout clock once every outstanding
|
|
602
659
|
// approval has been answered — a sibling request may still be
|
|
603
660
|
// waiting on a human decision.
|
|
604
|
-
if (pendingByCallId.size === 0)
|
|
605
|
-
|
|
661
|
+
if (pendingByCallId.size === 0) {
|
|
662
|
+
if (waitingForBackgroundSubagents)
|
|
663
|
+
armSubagentStallWatchdog();
|
|
664
|
+
else
|
|
665
|
+
turnLiveness.resume();
|
|
666
|
+
}
|
|
606
667
|
const result = buildResponseForResolve(pending, response);
|
|
607
668
|
client.peer.respond(pending.requestId, result);
|
|
608
669
|
return true;
|