cli-jaw 2.2.12 → 2.2.14
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/dist/src/agent/error-classifier.js +11 -2
- package/dist/src/agent/error-classifier.js.map +1 -1
- package/dist/src/agent/events/claude.js +7 -1
- package/dist/src/agent/events/claude.js.map +1 -1
- package/dist/src/agent/events/fulltext-bound.js +32 -0
- package/dist/src/agent/events/fulltext-bound.js.map +1 -0
- package/dist/src/agent/events/grok.js +7 -1
- package/dist/src/agent/events/grok.js.map +1 -1
- package/dist/src/agent/events/helpers.js +17 -4
- package/dist/src/agent/events/helpers.js.map +1 -1
- package/dist/src/agent/lifecycle-handler.js +56 -5
- package/dist/src/agent/lifecycle-handler.js.map +1 -1
- package/dist/src/agent/pi-runtime.js +30 -1
- package/dist/src/agent/pi-runtime.js.map +1 -1
- package/dist/src/agent/spawn/exit-drain.js +96 -0
- package/dist/src/agent/spawn/exit-drain.js.map +1 -0
- package/dist/src/agent/spawn/line-buffer.js +26 -0
- package/dist/src/agent/spawn/line-buffer.js.map +1 -0
- package/dist/src/agent/spawn/process-kill.js +31 -0
- package/dist/src/agent/spawn/process-kill.js.map +1 -1
- package/dist/src/agent/spawn.js +80 -23
- package/dist/src/agent/spawn.js.map +1 -1
- package/dist/src/agent/watchdog.js +21 -5
- package/dist/src/agent/watchdog.js.map +1 -1
- package/dist/src/routes/link-preview.js +14 -0
- package/dist/src/routes/link-preview.js.map +1 -1
- package/package.json +3 -1
- package/public/dist/assets/CodeCanvas-CbfPP7Ad.js +3 -0
- package/public/dist/assets/{DocPanel-DtYwv3n3.js → DocPanel-Dz-J_l5M.js} +1 -1
- package/public/dist/assets/MarkdownRenderer-CXrilOsG.js +15 -0
- package/public/dist/assets/MarkdownRenderer-DZVh_xX2.js +1 -0
- package/public/dist/assets/{MilkdownWysiwygEditor-BNoLGbFC.js → MilkdownWysiwygEditor-Em7iC6y9.js} +8 -8
- package/public/dist/assets/{react-dom-AWKMTIe7.js → bounded-set-CFlZiEF4.js} +1 -1
- package/public/dist/assets/manager-DaN201mN.js +13 -0
- package/public/dist/assets/{wiki-link-resolver-BfNXN9px.js → wiki-link-resolver-DyFzBTIQ.js} +1 -1
- package/public/dist/manager/index.html +1 -1
- package/public/manager/src/SidebarRailRouter.tsx +9 -3
- package/public/manager/src/browser-panel/use-embedded-target-sync.ts +19 -1
- package/public/manager/src/code/CodeCanvas.tsx +14 -2
- package/public/manager/src/code/CodeTranscript.tsx +33 -9
- package/public/manager/src/code/CodeWorkbench.tsx +1 -1
- package/public/manager/src/code/code-event-dedupe.ts +27 -3
- package/public/manager/src/code/use-code-transcript-scroll.ts +28 -3
- package/public/manager/src/code/use-throttled-markdown.ts +75 -0
- package/public/manager/src/code/useCodeTranscriptVirtualRows.ts +19 -0
- package/public/manager/src/electron-metrics.tsx +7 -0
- package/public/manager/src/jaw-ceo/useJawCeo.ts +6 -1
- package/public/manager/src/jaw-ceo/useJawCeoVirtualTimeline.ts +5 -0
- package/public/manager/src/lib/bounded-set.ts +15 -0
- package/public/manager/src/lib/fnv1a.ts +14 -0
- package/public/manager/src/notes/rendering/CodeBlock.tsx +13 -4
- package/public/manager/src/notes/rendering/MarkdownRenderer.tsx +14 -2
- package/public/manager/src/notes/rendering/highlight-cache.ts +111 -0
- package/public/manager/src/notes/useNotesExternalSync.ts +6 -1
- package/scripts/bundle-sidecar.sh +8 -0
- package/scripts/check-electron-no-native.cjs +90 -4
- package/scripts/check-native-load.cjs +157 -0
- package/scripts/check-sidecar-smoke.mjs +151 -0
- package/scripts/ensure-native-modules.cjs +222 -18
- package/scripts/release-gates.mjs +29 -0
- package/scripts/run-electron-builder.mjs +60 -0
- package/public/dist/assets/CodeCanvas-BZo3dohZ.js +0 -3
- package/public/dist/assets/MarkdownRenderer-CHacL2M_.js +0 -15
- package/public/dist/assets/MarkdownRenderer-DyrB9FbZ.js +0 -1
- package/public/dist/assets/manager-DL1GygQL.js +0 -13
package/dist/src/agent/spawn.js
CHANGED
|
@@ -52,14 +52,48 @@ import { getEmployeeMcpServers } from './mcp-passthrough.js';
|
|
|
52
52
|
// ─── State ───────────────────────────────────────────
|
|
53
53
|
export let activeProcess = null;
|
|
54
54
|
export const activeProcesses = new Map(); // agentId → child process
|
|
55
|
+
/** Kill reason recorded when a duplicate registration reaps the previous child. */
|
|
56
|
+
const DUP_REGISTRATION_KILL_REASON = 'dup-registration';
|
|
57
|
+
/** Grace before escalating that kill to SIGKILL, matching the sibling kill paths. */
|
|
58
|
+
const DUP_REGISTRATION_KILL_GRACE_MS = 2_000;
|
|
55
59
|
function registerActiveProcess(agentLabel, child) {
|
|
56
60
|
const prev = activeProcesses.get(agentLabel);
|
|
57
61
|
if (prev && prev !== child) {
|
|
58
|
-
|
|
62
|
+
// `killed` only records that a signal was delivered, so it is not a
|
|
63
|
+
// liveness test: a CLI that traps SIGTERM stays alive with killed set.
|
|
64
|
+
// Treating it as exited would drop that survivor from the map without
|
|
65
|
+
// even scheduling the escalation below — the exact invisible process
|
|
66
|
+
// this branch exists to prevent.
|
|
67
|
+
if (hasChildExited(prev)) {
|
|
59
68
|
activeProcesses.delete(agentLabel);
|
|
60
69
|
}
|
|
61
70
|
else {
|
|
62
|
-
|
|
71
|
+
// Dropping a live child from the map makes it invisible to
|
|
72
|
+
// killAllAgents, so it survives stop, shutdown, and restart while
|
|
73
|
+
// still holding its own memory. Reap it instead of leaking it.
|
|
74
|
+
console.warn(`[spawn:dup] activeProcesses already has a live child for ${agentLabel} — killing it before overwrite (pid=${prev.pid ?? 'unknown'})`);
|
|
75
|
+
if (prev.pid) {
|
|
76
|
+
const prevPid = prev.pid;
|
|
77
|
+
// Record a kill reason so the stale exit handler classifies this
|
|
78
|
+
// as an intentional kill rather than a genuine agent error.
|
|
79
|
+
killReasons.set(prevPid, DUP_REGISTRATION_KILL_REASON);
|
|
80
|
+
try {
|
|
81
|
+
killProcessTree(prevPid, 'SIGTERM');
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.warn(`[spawn:dup] failed to kill previous child for ${agentLabel}:`, error?.message ?? error);
|
|
85
|
+
}
|
|
86
|
+
// Escalate like every sibling kill path does: a CLI that traps
|
|
87
|
+
// SIGTERM would otherwise survive with no map entry to find it.
|
|
88
|
+
// Route through killProcessTreeIfAlive so a child that exited
|
|
89
|
+
// during the grace period cannot have its recycled PID killed:
|
|
90
|
+
// killProcessTree walks `pgrep -P`, so a blind escalation would
|
|
91
|
+
// take an unrelated process tree down with it.
|
|
92
|
+
const escalate = setTimeout(() => {
|
|
93
|
+
killProcessTreeIfAlive(prev, prevPid);
|
|
94
|
+
}, DUP_REGISTRATION_KILL_GRACE_MS);
|
|
95
|
+
escalate.unref?.();
|
|
96
|
+
}
|
|
63
97
|
}
|
|
64
98
|
}
|
|
65
99
|
activeProcesses.set(agentLabel, child);
|
|
@@ -90,7 +124,10 @@ export function buildAiERuntimeStatusMeta(cli, provider, model) {
|
|
|
90
124
|
},
|
|
91
125
|
};
|
|
92
126
|
}
|
|
93
|
-
import { killProcessTree } from './spawn/process-kill.js';
|
|
127
|
+
import { hasChildExited, killProcessTree, killProcessTreeIfAlive } from './spawn/process-kill.js';
|
|
128
|
+
import { releaseChildOutputAfterExit } from './spawn/exit-drain.js';
|
|
129
|
+
import { clampPendingLine } from './spawn/line-buffer.js';
|
|
130
|
+
import { appendBoundedFullText } from './events/fulltext-bound.js';
|
|
94
131
|
/** Single choke point for streamed assistant text: appends to the live-run
|
|
95
132
|
* accumulator and broadcasts agent_output tagged with the owning trace run
|
|
96
133
|
* id plus the cumulative text length (`textLen`). The web UI uses the pair
|
|
@@ -174,9 +211,9 @@ export function killAgentById(agentId) {
|
|
|
174
211
|
setTimeout(() => {
|
|
175
212
|
try {
|
|
176
213
|
if (proc.pid) {
|
|
177
|
-
|
|
214
|
+
killProcessTreeIfAlive(proc);
|
|
178
215
|
}
|
|
179
|
-
else {
|
|
216
|
+
else if (proc.exitCode === null && proc.signalCode === null) {
|
|
180
217
|
proc.kill('SIGKILL');
|
|
181
218
|
}
|
|
182
219
|
}
|
|
@@ -1425,10 +1462,21 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1425
1462
|
const delta = String(event.text || '');
|
|
1426
1463
|
if (!delta)
|
|
1427
1464
|
return;
|
|
1428
|
-
|
|
1465
|
+
{
|
|
1466
|
+
// D3: bound fullText — see events/fulltext-bound.ts.
|
|
1467
|
+
const bounded = appendBoundedFullText(ctx.fullText, delta);
|
|
1468
|
+
ctx.fullText = bounded.text;
|
|
1469
|
+
if (bounded.truncated)
|
|
1470
|
+
ctx.fullTextTruncated = true;
|
|
1471
|
+
}
|
|
1429
1472
|
const displayDelta = normalizeAssistantDisplayText(delta);
|
|
1430
|
-
if (ctx.liveOutputText !== undefined)
|
|
1431
|
-
|
|
1473
|
+
if (ctx.liveOutputText !== undefined) {
|
|
1474
|
+
// Bound this too: it is promoted into fullText at close.
|
|
1475
|
+
const live = appendBoundedFullText(ctx.liveOutputText, displayDelta);
|
|
1476
|
+
ctx.liveOutputText = live.text;
|
|
1477
|
+
if (live.truncated)
|
|
1478
|
+
ctx.fullTextTruncated = true;
|
|
1479
|
+
}
|
|
1432
1480
|
if (!ctx.outputTextStarted)
|
|
1433
1481
|
ctx.outputTextStarted = true;
|
|
1434
1482
|
broadcastAgentOutput(ctx, agentLabel, cli, displayDelta, empTag, traceAudience);
|
|
@@ -1456,10 +1504,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1456
1504
|
const pid = child.pid;
|
|
1457
1505
|
killProcessTree(pid, 'SIGTERM');
|
|
1458
1506
|
setTimeout(() => {
|
|
1459
|
-
|
|
1460
|
-
killProcessTree(pid, 'SIGKILL');
|
|
1461
|
-
}
|
|
1462
|
-
catch { /* already dead */ }
|
|
1507
|
+
killProcessTreeIfAlive(child, pid);
|
|
1463
1508
|
}, 5_000);
|
|
1464
1509
|
}
|
|
1465
1510
|
else
|
|
@@ -1515,7 +1560,10 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1515
1560
|
opts.lifecycle?.onExit?.(result.code);
|
|
1516
1561
|
const killReason = consumeKillReason(child.pid);
|
|
1517
1562
|
const wasKilled = !!killReason;
|
|
1518
|
-
|
|
1563
|
+
// 'dup-registration' behaves like a steer for cleanup purposes: a
|
|
1564
|
+
// replacement child already owns this label, so the stale exit handler
|
|
1565
|
+
// must not delete the new child's map entry.
|
|
1566
|
+
const wasSteer = killReason === 'steer' || killReason === DUP_REGISTRATION_KILL_REASON;
|
|
1519
1567
|
const smokeResult = detectSmokeResponse(ctx.fullText, ctx.toolLog, result.code, cli);
|
|
1520
1568
|
return handleAgentExit({
|
|
1521
1569
|
ctx, code: result.code, cli, model: runtimeModel, effectiveProvider: profile.id, agentLabel, mainManaged, origin,
|
|
@@ -1879,7 +1927,8 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1879
1927
|
console.warn(`[codex-app:unexpected-exit] code=${processExit.value.code} signal=${processExit.value.signal} threadId=${ctx.sessionId || 'none'}`);
|
|
1880
1928
|
}
|
|
1881
1929
|
const wasKilled = !!killReason;
|
|
1882
|
-
|
|
1930
|
+
// See above: a dup-registration kill must not clobber the replacement.
|
|
1931
|
+
const wasSteer = killReason === 'steer' || killReason === DUP_REGISTRATION_KILL_REASON;
|
|
1883
1932
|
flushCodexAppThinking();
|
|
1884
1933
|
const smokeResult = detectSmokeResponse(ctx.fullText, ctx.toolLog, exitCode, cli);
|
|
1885
1934
|
await handleAgentExit({
|
|
@@ -1961,6 +2010,14 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1961
2010
|
broadcast('agent_status', { running: true, agentId: agentLabel, cli, ...runtimeStatusMeta, ...empTag });
|
|
1962
2011
|
if (mainManaged && !opts.internal)
|
|
1963
2012
|
beginLiveRun(liveScope, cli);
|
|
2013
|
+
// The turn settles on 'close', which waits for every stdio stream to close.
|
|
2014
|
+
// A descendant that inherited these pipes can outlive the child and hold
|
|
2015
|
+
// them open forever, so bound that wait while still draining short tails.
|
|
2016
|
+
const releaseExitDrain = releaseChildOutputAfterExit(child, {
|
|
2017
|
+
onRelease: (reason) => {
|
|
2018
|
+
console.warn(`[jaw:drain] ${agentLabel} exited but output stayed open — released after ${reason}`);
|
|
2019
|
+
},
|
|
2020
|
+
});
|
|
1964
2021
|
// ─── DIFF-A: error guard — prevent uncaught ENOENT crash ───
|
|
1965
2022
|
let stdSettled = false; // guard: error→close can fire sequentially
|
|
1966
2023
|
let lastOpencodeIoAt = Date.now();
|
|
@@ -1981,6 +2038,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
1981
2038
|
child.on('error', (err) => {
|
|
1982
2039
|
clearOpencodeIdleTimer();
|
|
1983
2040
|
clearAgyQuietCompletionTimer();
|
|
2041
|
+
releaseExitDrain();
|
|
1984
2042
|
if (stdSettled)
|
|
1985
2043
|
return;
|
|
1986
2044
|
stdSettled = true;
|
|
@@ -2089,11 +2147,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
2089
2147
|
try {
|
|
2090
2148
|
killProcessTree(child.pid, 'SIGTERM');
|
|
2091
2149
|
setTimeout(() => {
|
|
2092
|
-
|
|
2093
|
-
if (child.pid)
|
|
2094
|
-
killProcessTree(child.pid, 'SIGKILL');
|
|
2095
|
-
}
|
|
2096
|
-
catch { /* already dead */ }
|
|
2150
|
+
killProcessTreeIfAlive(child);
|
|
2097
2151
|
}, DEFAULT_KILL_ESCALATION_MS);
|
|
2098
2152
|
}
|
|
2099
2153
|
catch (e) {
|
|
@@ -2129,10 +2183,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
2129
2183
|
if (child.pid) {
|
|
2130
2184
|
killProcessTree(child.pid, 'SIGTERM');
|
|
2131
2185
|
setTimeout(() => {
|
|
2132
|
-
|
|
2133
|
-
killProcessTree(child.pid, 'SIGKILL');
|
|
2134
|
-
}
|
|
2135
|
-
catch { /* already dead */ }
|
|
2186
|
+
killProcessTreeIfAlive(child);
|
|
2136
2187
|
}, 5_000);
|
|
2137
2188
|
}
|
|
2138
2189
|
}, watchdogConfig);
|
|
@@ -2326,6 +2377,11 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
2326
2377
|
buffer += chunk.toString();
|
|
2327
2378
|
const lines = buffer.split('\n');
|
|
2328
2379
|
buffer = lines.pop() ?? '';
|
|
2380
|
+
const clampedPending = clampPendingLine(buffer);
|
|
2381
|
+
if (clampedPending.overflowed) {
|
|
2382
|
+
console.warn(`[jaw:${agentLabel}] stdout line exceeded the pending-line cap without a newline — truncating`);
|
|
2383
|
+
buffer = clampedPending.buffer;
|
|
2384
|
+
}
|
|
2329
2385
|
for (const line of lines) {
|
|
2330
2386
|
if (!line.trim())
|
|
2331
2387
|
continue;
|
|
@@ -2351,6 +2407,7 @@ export function spawnAgent(prompt, opts = {}) {
|
|
|
2351
2407
|
clearOpencodeIdleTimer();
|
|
2352
2408
|
clearAgyQuietCompletionTimer();
|
|
2353
2409
|
stallWatchdog.stop();
|
|
2410
|
+
releaseExitDrain();
|
|
2354
2411
|
if (stdSettled)
|
|
2355
2412
|
return; // error handler already resolved
|
|
2356
2413
|
// [I1] Flush residual NDJSON buffer — last event may lack trailing newline
|