@linzumi/cli 1.0.111 → 1.0.112

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/README.md CHANGED
@@ -58,7 +58,7 @@ Install the CLI or run it with `npx`:
58
58
  ```bash
59
59
  npm install -g @linzumi/cli@latest
60
60
  npx -y @linzumi/cli@latest signup
61
- npx -y @linzumi/cli@1.0.111 --version
61
+ npx -y @linzumi/cli@1.0.112 --version
62
62
  linzumi --version
63
63
  ```
64
64
 
package/dist/index.js CHANGED
@@ -1473,7 +1473,7 @@ To kick the agent off:
1473
1473
  `,L=Buffer.byteLength(P,"utf8");if(L>s)return l("runner_diagnostics.spool_entry_dropped",{reason:"entry_too_large",entry_bytes:L,max_entry_bytes:s}),!1;jSe(n,{recursive:!0}),m(u)+L>i&&f(),_(L);let D=w()?`
1474
1474
  `:"";return $Se(u,`${D}${P}`,"utf8"),!0}catch(R){return l("runner_diagnostics.spool_failed",{error:R instanceof Error?R.message:String(R)}),!1}},v=k=>{let R=[],P=zSe(k,"utf8");for(let L of P.split(`
1475
1475
  `))if(L.trim()!=="")try{let D=JSON.parse(L);D!==null&&typeof D=="object"&&R.push({parsed:D,raw:L})}catch{}return R};return{append:S,drain:async k=>{if(c)return{uploadedBatches:0,uploadedEntries:0,remainingBatches:p().length,skipped:!0};c=!0;let R=0,P=0;try{f(),_(0);let L=a()-o;for(let D of p()){let j;try{j=v(D)}catch(Z){l("runner_diagnostics.spool_batch_unreadable",{file:D,error:Z instanceof Error?Z.message:String(Z)}),y(D,"unreadable");continue}let z=j.filter(Z=>{let me=tke(Z.parsed);return me===void 0||me>=L});if(z.length===0){y(D,"empty_or_expired");continue}let E=hP(D,".jsonl"),B=z.map(Z=>Z.raw).join(`
1476
- `),q=z.map(Z=>Z.parsed.spooled_at).filter(Z=>typeof Z=="string"),W={bundle_name:"runner-diagnostics-spool",exported_at:new Date(a()).toISOString(),idempotency_key:E,diagnostics:{kind:"runner_diagnostic_log",spool_batch:!0,idempotency_key:E,entry_count:z.length,...q.length===0?{}:{first_spooled_at:q[0],last_spooled_at:q[q.length-1]}},telemetry_ndjson:B};try{await k(W)}catch(Z){l("runner_diagnostics.spool_upload_failed",{file:D,remaining_batches:p().length,error:Z instanceof Error?Z.message:String(Z)});break}R+=1,P+=z.length;try{F9(D)}catch(Z){l("runner_diagnostics.spool_delete_failed",{file:D,error:Z instanceof Error?Z.message:String(Z)})}}}catch(L){l("runner_diagnostics.spool_drain_failed",{error:L instanceof Error?L.message:String(L)})}finally{c=!1}return{uploadedBatches:R,uploadedEntries:P,remainingBatches:p().length,skipped:!1}},pendingBatchFiles:p}}var KSe,VSe,WSe,HSe,GSe,z9,QSe,XSe,N9=F(()=>{"use strict";ax();KSe=32*1024*1024,VSe=10080*60*1e3,WSe=4*1024*1024,HSe=1024*1024,GSe="spool.jsonl",z9=/^batch-(\d{1,16})-[0-9a-f-]+\.jsonl$/;QSe=/\b([A-Z][A-Z0-9_]{1,})=("[^"\n]*"|'[^'\n]*'|[^\s"'`;,)\]}]+)/g,XSe=/(?:\/(?:[A-Za-z0-9._@~+-]|%[0-9A-Fa-f]{2})+){2,}/g});var qt,yP,ta=F(()=>{"use strict";qt="1.0.111",yP=`linzumi ${qt}`});import{closeSync as B9,existsSync as _P,mkdirSync as rke,openSync as J9,readFileSync as K9,renameSync as oke,unlinkSync as _d,writeFileSync as ike,writeSync as V9}from"node:fs";import{dirname as W9,join as ske}from"node:path";function bP(e){return e instanceof ys||e instanceof Error&&e.name===G9&&"lockPath"in e&&"heldBy"in e}function wP(e,n=Wn(),t){let r=t===void 0?encodeURIComponent(e):ld(t);return ske(W9(n),"runners",`${r}.lock`)}function x_(e){let n=wP(e.machineId,e.configPath,e.linzumiUrl),t=e.isPidAlive??t3,r=e.now??(()=>new Date),o=e.killPid??_ke,i=r().toISOString(),s={version:1,machineId:e.machineId,runnerId:e.runnerId,pid:e.pid??process.pid,cwd:e.cwd,workspace:e.workspace,...e.linzumiUrl===void 0?{}:{linzumiUrl:zn(e.linzumiUrl)},...e.launchSource===void 0?{}:{launchSource:e.launchSource},startedAt:i,heartbeatAt:i,cliVersion:e.cliVersion??qt,...e.gracefulUpdateDrain===!0?{gracefulUpdateDrain:!0}:{}};cke(e.machineId,e.configPath,e.linzumiUrl,t,r,o,e.onTakeover),lke(n,s,t,r,o,e.onTakeover,e.beforeReadExistingLock,e.beforeReplaceStaleLock);let a=()=>Z9(n,s,r),d=setInterval(a,H9);return d.unref(),{path:n,record:s,heartbeat:a,release:()=>{clearInterval(d),e3(n,s)}}}function Z9(e,n,t){try{let r=b_(e);if(r===void 0||r.machineId!==n.machineId||r.runnerId!==n.runnerId||r.pid!==n.pid||r.startedAt!==n.startedAt)return;let o={...r,heartbeatAt:t().toISOString()},i=`${e}.tmp`;ike(i,`${JSON.stringify(o,null,2)}
1476
+ `),q=z.map(Z=>Z.parsed.spooled_at).filter(Z=>typeof Z=="string"),W={bundle_name:"runner-diagnostics-spool",exported_at:new Date(a()).toISOString(),idempotency_key:E,diagnostics:{kind:"runner_diagnostic_log",spool_batch:!0,idempotency_key:E,entry_count:z.length,...q.length===0?{}:{first_spooled_at:q[0],last_spooled_at:q[q.length-1]}},telemetry_ndjson:B};try{await k(W)}catch(Z){l("runner_diagnostics.spool_upload_failed",{file:D,remaining_batches:p().length,error:Z instanceof Error?Z.message:String(Z)});break}R+=1,P+=z.length;try{F9(D)}catch(Z){l("runner_diagnostics.spool_delete_failed",{file:D,error:Z instanceof Error?Z.message:String(Z)})}}}catch(L){l("runner_diagnostics.spool_drain_failed",{error:L instanceof Error?L.message:String(L)})}finally{c=!1}return{uploadedBatches:R,uploadedEntries:P,remainingBatches:p().length,skipped:!1}},pendingBatchFiles:p}}var KSe,VSe,WSe,HSe,GSe,z9,QSe,XSe,N9=F(()=>{"use strict";ax();KSe=32*1024*1024,VSe=10080*60*1e3,WSe=4*1024*1024,HSe=1024*1024,GSe="spool.jsonl",z9=/^batch-(\d{1,16})-[0-9a-f-]+\.jsonl$/;QSe=/\b([A-Z][A-Z0-9_]{1,})=("[^"\n]*"|'[^'\n]*'|[^\s"'`;,)\]}]+)/g,XSe=/(?:\/(?:[A-Za-z0-9._@~+-]|%[0-9A-Fa-f]{2})+){2,}/g});var qt,yP,ta=F(()=>{"use strict";qt="1.0.112",yP=`linzumi ${qt}`});import{closeSync as B9,existsSync as _P,mkdirSync as rke,openSync as J9,readFileSync as K9,renameSync as oke,unlinkSync as _d,writeFileSync as ike,writeSync as V9}from"node:fs";import{dirname as W9,join as ske}from"node:path";function bP(e){return e instanceof ys||e instanceof Error&&e.name===G9&&"lockPath"in e&&"heldBy"in e}function wP(e,n=Wn(),t){let r=t===void 0?encodeURIComponent(e):ld(t);return ske(W9(n),"runners",`${r}.lock`)}function x_(e){let n=wP(e.machineId,e.configPath,e.linzumiUrl),t=e.isPidAlive??t3,r=e.now??(()=>new Date),o=e.killPid??_ke,i=r().toISOString(),s={version:1,machineId:e.machineId,runnerId:e.runnerId,pid:e.pid??process.pid,cwd:e.cwd,workspace:e.workspace,...e.linzumiUrl===void 0?{}:{linzumiUrl:zn(e.linzumiUrl)},...e.launchSource===void 0?{}:{launchSource:e.launchSource},startedAt:i,heartbeatAt:i,cliVersion:e.cliVersion??qt,...e.gracefulUpdateDrain===!0?{gracefulUpdateDrain:!0}:{}};cke(e.machineId,e.configPath,e.linzumiUrl,t,r,o,e.onTakeover),lke(n,s,t,r,o,e.onTakeover,e.beforeReadExistingLock,e.beforeReplaceStaleLock);let a=()=>Z9(n,s,r),d=setInterval(a,H9);return d.unref(),{path:n,record:s,heartbeat:a,release:()=>{clearInterval(d),e3(n,s)}}}function Z9(e,n,t){try{let r=b_(e);if(r===void 0||r.machineId!==n.machineId||r.runnerId!==n.runnerId||r.pid!==n.pid||r.startedAt!==n.startedAt)return;let o={...r,heartbeatAt:t().toISOString()},i=`${e}.tmp`;ike(i,`${JSON.stringify(o,null,2)}
1477
1477
  `,"utf8"),oke(i,e)}catch{}}function bd(e,n,t){if(!n(e.pid))return{state:"dead"};let r=e.heartbeatAt??e.startedAt,o=Date.parse(r);return Number.isNaN(o)||t().getTime()-o>ake?{state:"wedged",reason:e.heartbeatAt===void 0?"missing_heartbeat":"stale_heartbeat"}:{state:"live"}}function Q9(e,n,t,r){return t!==__||e.launchSource!==__?!1:r.state!=="live"?!0:dke(n,e.cliVersion)>0}function dke(e,n){let t=U9(e),r=U9(n);for(let o=0;o<3;o++){let i=t.core[o],s=r.core[o];if(i===void 0||s===void 0)throw new Error(`compareCliVersions: unexpected undefined at core index ${o}`);if(i!==s)return i>s?1:-1}return t.prerelease.length===0&&r.prerelease.length===0?0:t.prerelease.length===0?1:r.prerelease.length===0?-1:uke(t.prerelease,r.prerelease)}function U9(e){let n=e.trim().replace(/^v/i,"").split("+",1)[0]??"",t=n.indexOf("-"),r=t===-1?n:n.slice(0,t),o=t===-1?"":n.slice(t+1),i=r.split("."),s=[xP(i[0]),xP(i[1]),xP(i[2])],a=o===""?[]:o.split(".").filter(d=>d!=="");return{core:s,prerelease:a}}function xP(e){let n=Number.parseInt(e??"",10);return Number.isInteger(n)&&n>=0?n:0}function uke(e,n){let t=Math.max(e.length,n.length);for(let r=0;r<t;r++){let o=e[r],i=n[r];if(o===void 0)return-1;if(i===void 0)return 1;let s=Number.parseInt(o,10),a=Number.parseInt(i,10),d=s.toString()===o,u=a.toString()===i;if(d&&u){if(s!==a)return s>a?1:-1}else{if(d!==u)return d?-1:1;if(o!==i)return o>i?1:-1}}return 0}function X9(e,n,t,r,o){try{r(n.pid)}catch(i){let s=i!==null&&typeof i=="object"&&"code"in i?i.code:void 0;if(s!=="ESRCH")throw s==="EPERM"||s==="EACCES"?new Error(`Cannot take over the runner lock at ${e}: its holder process (pid ${n.pid}) is owned by another user and cannot be stopped. Stop that process, or remove the lock file, then retry.`):i}o?.({lockPath:e,holderPid:n.pid,holderRunnerId:n.runnerId,reason:t})}function cke(e,n,t,r,o,i,s){if(t===void 0||yr(t)!==yr(Jt))return;let a=wP(e,n),d=b_(a);if(d!==void 0){if(bd(d,r,o).state==="live")throw new ys(a,d);Y9(a,r,()=>{let u=_P(a)?vP(a):void 0;if(u===void 0)return;let c=bd(u,r,o);if(c.state==="live")throw new ys(a,u);c.state==="wedged"&&X9(a,u,c.reason,i,s),_d(a)})}}function lke(e,n,t,r,o,i,s,a){if(y_(e,n))return;s?.();let d=b_(e);if(d===void 0){if(y_(e,n))return;throw new Error(`another Linzumi runner lock appeared while starting: ${e}`)}if(bd(d,t,r).state==="live")throw new ys(e,d);a?.(),Y9(e,t,()=>{let u=_P(e)?vP(e):void 0;if(u!==void 0){let c=bd(u,t,r);if(c.state==="live")throw new ys(e,u);c.state==="wedged"&&X9(e,u,c.reason,o,i),_d(e)}if(!y_(e,n))throw new Error(`another Linzumi runner lock appeared while starting: ${e}`)})}function y_(e,n){rke(W9(e),{recursive:!0});try{let t=J9(e,"wx");try{V9(t,`${JSON.stringify(n,null,2)}
1478
1478
  `)}finally{B9(t)}return!0}catch(t){if(Vc(t,"EEXIST"))return!1;throw t}}function Y9(e,n,t){let r=`${e}.replace`;for(;;)try{let o=J9(r,"wx");try{V9(o,`${process.pid}
1479
1479
  `),t()}finally{B9(o),_d(r)}return}catch(o){if(Vc(o,"EEXIST")){let i=pke(r);if(i===void 0)continue;if(n(i))throw new Error(["another Linzumi runner is already replacing a stale runner lock",`lock: ${e}`,"Wait for that startup to finish, then retry."].join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "1.0.111",
3
+ "version": "1.0.112",
4
4
  "description": "Linzumi CLI \u2014 point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,586 @@
1
+ // Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
2
+ // (M3 cluster 10, runner core).
3
+ // Relationship: Builds the TS-side PARITY ORACLE for three bounded
4
+ // commander-core seams of the ReScript commander port, the sibling of
5
+ // build-queue-parity-oracle.mjs (cluster 5) /
6
+ // build-transport-parity-oracle.mjs (cluster 4). ONE bundle imports the
7
+ // REAL commander-core exports from './src/runner' (the whole module graph
8
+ // rides along - large by design, exactly like the transport/config oracles
9
+ // that already import runner exports) behind the one-shot batch driver: a
10
+ // JSON array of cases on stdin, a JSON array of results on stdout.
11
+ //
12
+ // The three seams:
13
+ // 1. Control-dispatch dedup + commit-then-advance confirmation
14
+ // (staleStartInstanceWindowMs / staleStartInstanceAgeMs /
15
+ // shouldSkipReplayedStartInstanceForKnownSession /
16
+ // shouldSkipReplayedStartTurn / commitAppliedStartTurn /
17
+ // dispatchControlConfirmingStartTurn / settleControlDispatch).
18
+ // 2. The connection-staleness tracker (constants +
19
+ // createConnectionStalenessTracker) driven by scripted clocks and
20
+ // scripted escalation callbacks.
21
+ // 3. The app-server KILL/RESPAWN ladder (createCodexTurnFailureGuard,
22
+ // runnerOwnsRespawnableAppServer, awaitLossReportsThenExit, the
23
+ // crash-loop constants/store/classifier, appServerLossCwdDiagnosis).
24
+ //
25
+ // Determinism: clocks are scripted per case (every seam takes an injected
26
+ // now/nowMs), timers are scripted callbacks, scripted promises resolve in
27
+ // program order with explicit microtask flushes, and scratch paths are
28
+ // normalized through the "$DIR" placeholder in both directions. The
29
+ // turn-failure guard's lastActivityAtMs is wall-clock-stamped inside the TS
30
+ // original, so both impls report it as a PRESENCE flag only.
31
+ //
32
+ // SECRETS: the oracle only ever sees the conformance suite's synthetic
33
+ // payloads; nothing here logs case contents.
34
+ //
35
+ // The output is a local test artifact (.differential/ is gitignored, never
36
+ // published) and stays unminified for debuggability.
37
+ import { copyFile, mkdir } from 'node:fs/promises';
38
+ import { dirname, join } from 'node:path';
39
+ import { fileURLToPath } from 'node:url';
40
+ import { build } from 'esbuild';
41
+
42
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
43
+
44
+ const driverSource = `
45
+ import {
46
+ mkdtempSync,
47
+ readdirSync,
48
+ readFileSync,
49
+ realpathSync,
50
+ statSync,
51
+ writeFileSync,
52
+ } from 'node:fs';
53
+ import { tmpdir } from 'node:os';
54
+ import { join, relative } from 'node:path';
55
+ import {
56
+ appServerCrashLoopExitCode,
57
+ appServerCrashLoopExitLimit,
58
+ appServerCrashLoopWindowMs,
59
+ appServerLossCwdDiagnosis,
60
+ awaitLossReportsThenExit,
61
+ commitAppliedStartTurn,
62
+ connectionStaleForcedReconnectAfterMs,
63
+ connectionStaleForcedReconnectRetryMs,
64
+ connectionStaleHardResetAfterUninitiatedDispatches,
65
+ connectionStaleHardResetFailureLimit,
66
+ connectionStaleThresholdMs,
67
+ connectionUnrecoverableAfterMs,
68
+ createCodexTurnFailureGuard,
69
+ createConnectionStalenessTracker,
70
+ defaultAppServerExitLoopStatePath,
71
+ dispatchControlConfirmingStartTurn,
72
+ fileAppServerExitLoopStore,
73
+ recordAppServerExitAndClassify,
74
+ runnerOwnsRespawnableAppServer,
75
+ settleControlDispatch,
76
+ shouldSkipReplayedStartInstanceForKnownSession,
77
+ shouldSkipReplayedStartTurn,
78
+ staleStartInstanceAgeMs,
79
+ staleStartInstanceWindowMs,
80
+ } from './src/runner';
81
+
82
+ // --- Placeholder + snapshot plumbing --------------------------------------------
83
+
84
+ const dirPlaceholder = '$DIR';
85
+
86
+ function scratch() {
87
+ // realpath so macOS /var -> /private/var never splits the two impls'
88
+ // placeholder substitution.
89
+ return realpathSync(mkdtempSync(join(tmpdir(), 'linzumi-core-oracle-')));
90
+ }
91
+
92
+ function substitute(value, from, to) {
93
+ if (typeof value === 'string') {
94
+ return value.split(from).join(to);
95
+ }
96
+ if (Array.isArray(value)) {
97
+ return value.map((entry) => substitute(entry, from, to));
98
+ }
99
+ if (typeof value === 'object' && value !== null) {
100
+ return Object.fromEntries(
101
+ Object.entries(value).map(([key, entry]) => [
102
+ substitute(key, from, to),
103
+ substitute(entry, from, to),
104
+ ])
105
+ );
106
+ }
107
+ return value;
108
+ }
109
+
110
+ function snapshotFiles(dir) {
111
+ const files = {};
112
+ const walk = (current) => {
113
+ for (const name of readdirSync(current).sort()) {
114
+ const full = join(current, name);
115
+ if (statSync(full).isDirectory()) {
116
+ walk(full);
117
+ } else {
118
+ files[relative(dir, full)] = readFileSync(full, 'utf8');
119
+ }
120
+ }
121
+ };
122
+ walk(dir);
123
+ return files;
124
+ }
125
+
126
+ function outcome(run) {
127
+ try {
128
+ const value = run();
129
+ return value === undefined ? { defined: false } : { defined: true, value };
130
+ } catch (error) {
131
+ return {
132
+ threw: true,
133
+ message: error instanceof Error ? error.message : String(error),
134
+ };
135
+ }
136
+ }
137
+
138
+ function collectingLog(events) {
139
+ return (event, payload) => {
140
+ events.push({ trace: 'log', event, payload });
141
+ };
142
+ }
143
+
144
+ // Deterministic microtask flush: enough passes that a then-chain of any
145
+ // scripted depth settles before the trace is read on both impls.
146
+ async function flushMicrotasks() {
147
+ for (let i = 0; i < 8; i += 1) {
148
+ await Promise.resolve();
149
+ }
150
+ }
151
+
152
+ // --- Slice 1: control-dispatch dedup + commit-then-advance -----------------------
153
+
154
+ async function handleControlDispatch(testCase) {
155
+ switch (testCase.fn) {
156
+ case 'constants':
157
+ return { staleStartInstanceWindowMs };
158
+ case 'staleAge':
159
+ return outcome(() =>
160
+ staleStartInstanceAgeMs(testCase.control, testCase.nowMs)
161
+ );
162
+ case 'skipKnownSession': {
163
+ const lookups = [];
164
+ const known = new Set(testCase.knownThreadIds ?? []);
165
+ const result = outcome(() =>
166
+ shouldSkipReplayedStartInstanceForKnownSession(
167
+ testCase.control,
168
+ (threadId) => {
169
+ lookups.push(threadId);
170
+ return known.has(threadId);
171
+ }
172
+ )
173
+ );
174
+ return { result, lookups };
175
+ }
176
+ case 'skipStartTurn':
177
+ return outcome(() =>
178
+ shouldSkipReplayedStartTurn(
179
+ testCase.applied ?? undefined,
180
+ testCase.controlSeq
181
+ )
182
+ );
183
+ case 'commit': {
184
+ const calls = [];
185
+ const store = {
186
+ onAdvance: (key, seq) => calls.push({ call: 'onAdvance', key, seq }),
187
+ flush: () => calls.push({ call: 'flush' }),
188
+ };
189
+ const result = outcome(() =>
190
+ commitAppliedStartTurn(store, testCase.watermarkKey, testCase.controlSeq)
191
+ );
192
+ return { result, calls };
193
+ }
194
+ case 'dispatchConfirm': {
195
+ const trace = [];
196
+ const dispatch = () => {
197
+ trace.push({ trace: 'dispatch_called' });
198
+ return testCase.dispatch.kind === 'resolve'
199
+ ? Promise.resolve(testCase.dispatch.value ?? null)
200
+ : Promise.reject(
201
+ new Error(String(testCase.dispatch.message ?? 'scripted dispatch rejection'))
202
+ );
203
+ };
204
+ const commit =
205
+ testCase.hasCommit === true
206
+ ? () => trace.push({ trace: 'commit_called' })
207
+ : undefined;
208
+ try {
209
+ const value = await dispatchControlConfirmingStartTurn(dispatch, commit);
210
+ trace.push({ trace: 'resolved', value: value ?? null });
211
+ } catch (error) {
212
+ trace.push({
213
+ trace: 'rejected',
214
+ message: error instanceof Error ? error.message : String(error),
215
+ });
216
+ }
217
+ return { trace };
218
+ }
219
+ case 'settle': {
220
+ const trace = [];
221
+ const log = collectingLog(trace);
222
+ const handler = testCase.handler ?? { kind: 'syncUndefined' };
223
+ const handle = () => {
224
+ trace.push({ trace: 'handle_called' });
225
+ switch (handler.kind) {
226
+ case 'syncThrow':
227
+ throw new Error(String(handler.message ?? 'scripted sync throw'));
228
+ case 'syncObject':
229
+ return handler.value ?? {};
230
+ case 'syncUndefined':
231
+ return undefined;
232
+ case 'asyncResolve':
233
+ return Promise.resolve(handler.value ?? null);
234
+ case 'asyncReject':
235
+ return Promise.reject(
236
+ new Error(String(handler.message ?? 'scripted async rejection'))
237
+ );
238
+ default:
239
+ throw new Error('unknown settle handler kind ' + String(handler.kind));
240
+ }
241
+ };
242
+ const onRejected =
243
+ testCase.onRejected === undefined
244
+ ? undefined
245
+ : (error) => {
246
+ trace.push({
247
+ trace: 'on_rejected',
248
+ message: error instanceof Error ? error.message : String(error),
249
+ });
250
+ return testCase.onRejected;
251
+ };
252
+ settleControlDispatch(
253
+ testCase.controlType ?? 'scripted_control',
254
+ handle,
255
+ () => trace.push({ trace: 'ack' }),
256
+ log,
257
+ onRejected
258
+ );
259
+ trace.push({ trace: 'settle_returned' });
260
+ await flushMicrotasks();
261
+ return { trace };
262
+ }
263
+ default:
264
+ return { threw: true, message: 'unknown controlDispatch fn ' + String(testCase.fn) };
265
+ }
266
+ }
267
+
268
+ // --- Slice 2: connection-staleness tracker ----------------------------------------
269
+
270
+ function handleStaleness(testCase) {
271
+ if (testCase.fn === 'constants') {
272
+ return {
273
+ connectionStaleThresholdMs,
274
+ connectionStaleForcedReconnectAfterMs,
275
+ connectionStaleForcedReconnectRetryMs,
276
+ connectionStaleHardResetAfterUninitiatedDispatches,
277
+ connectionStaleHardResetFailureLimit,
278
+ connectionUnrecoverableAfterMs,
279
+ };
280
+ }
281
+ // fn === 'scenario': a scripted clock + scripted escalation callbacks.
282
+ const trace = [];
283
+ const log = collectingLog(trace);
284
+ let nowMs = testCase.startMs ?? 0;
285
+ const escalationSpec = testCase.escalation;
286
+ const reconnectReturns = [...(escalationSpec?.reconnectReturns ?? [])];
287
+ const hardResetReturns = [...(escalationSpec?.hardResetReturns ?? [])];
288
+ const escalation =
289
+ escalationSpec === undefined
290
+ ? undefined
291
+ : {
292
+ ...(escalationSpec.hasReconnect === true
293
+ ? {
294
+ onReconnectNeeded: (details) => {
295
+ trace.push({ trace: 'reconnect_needed', details });
296
+ const scripted = reconnectReturns.shift();
297
+ return scripted === null || scripted === undefined
298
+ ? undefined
299
+ : scripted;
300
+ },
301
+ }
302
+ : {}),
303
+ ...(escalationSpec.hasHardReset === true
304
+ ? {
305
+ onHardResetNeeded: (details) => {
306
+ trace.push({ trace: 'hard_reset_needed', details });
307
+ const scripted = hardResetReturns.shift();
308
+ return scripted === null || scripted === undefined
309
+ ? undefined
310
+ : scripted;
311
+ },
312
+ }
313
+ : {}),
314
+ ...(escalationSpec.forcedReconnectAfterMs === undefined
315
+ ? {}
316
+ : { forcedReconnectAfterMs: escalationSpec.forcedReconnectAfterMs }),
317
+ ...(escalationSpec.forcedReconnectRetryMs === undefined
318
+ ? {}
319
+ : { forcedReconnectRetryMs: escalationSpec.forcedReconnectRetryMs }),
320
+ onUnrecoverable: (details) => {
321
+ trace.push({ trace: 'unrecoverable', details });
322
+ },
323
+ ...(escalationSpec.afterMs === undefined
324
+ ? {}
325
+ : { afterMs: escalationSpec.afterMs }),
326
+ };
327
+ const tracker = createConnectionStalenessTracker(
328
+ log,
329
+ () => nowMs,
330
+ testCase.thresholdMs ?? undefined,
331
+ escalation
332
+ );
333
+ for (const step of testCase.steps ?? []) {
334
+ switch (step.fn) {
335
+ case 'advance':
336
+ nowMs += step.byMs;
337
+ break;
338
+ case 'ack':
339
+ tracker.recordServerAck();
340
+ break;
341
+ case 'check':
342
+ tracker.check();
343
+ break;
344
+ case 'isStale':
345
+ trace.push({ trace: 'is_stale', value: tracker.isStale() });
346
+ break;
347
+ default:
348
+ trace.push({ trace: 'unknown_step', fn: String(step.fn) });
349
+ }
350
+ }
351
+ return { trace };
352
+ }
353
+
354
+ // --- Slice 3: respawn ladder ---------------------------------------------------------
355
+
356
+ async function handleRespawn(testCase) {
357
+ switch (testCase.fn) {
358
+ case 'constants':
359
+ return {
360
+ appServerCrashLoopWindowMs,
361
+ appServerCrashLoopExitLimit,
362
+ appServerCrashLoopExitCode,
363
+ };
364
+ case 'ownsRespawnable':
365
+ return outcome(() =>
366
+ runnerOwnsRespawnableAppServer({
367
+ codexUrl: testCase.codexUrl ?? undefined,
368
+ threadProcessRole: testCase.threadProcessRole ?? undefined,
369
+ ownsStartedAppServer: testCase.ownsStartedAppServer === true,
370
+ })
371
+ );
372
+ case 'statePath':
373
+ return outcome(() =>
374
+ defaultAppServerExitLoopStatePath(testCase.runnerId, testCase.env ?? {})
375
+ );
376
+ case 'cwdDiagnosis': {
377
+ const existsMode = testCase.existsMode ?? 'real';
378
+ const existsFn =
379
+ existsMode === 'real'
380
+ ? undefined
381
+ : () => {
382
+ if (existsMode === 'throw') {
383
+ throw new Error('scripted exists probe failure');
384
+ }
385
+ return existsMode === 'true';
386
+ };
387
+ return outcome(() =>
388
+ existsFn === undefined
389
+ ? appServerLossCwdDiagnosis(testCase.cwd)
390
+ : appServerLossCwdDiagnosis(testCase.cwd, existsFn)
391
+ );
392
+ }
393
+ case 'exitLoopScenario': {
394
+ const dir = scratch();
395
+ const path = join(dir, 'exit-loop.json');
396
+ if (testCase.initialContents !== undefined) {
397
+ writeFileSync(path, testCase.initialContents, 'utf8');
398
+ }
399
+ const writeErrors = [];
400
+ const store = fileAppServerExitLoopStore(path, (message) =>
401
+ writeErrors.push(message)
402
+ );
403
+ const results = (testCase.steps ?? []).map((step) => {
404
+ switch (step.fn) {
405
+ case 'record':
406
+ return outcome(() =>
407
+ recordAppServerExitAndClassify(
408
+ store,
409
+ step.nowMs,
410
+ step.windowMs ?? undefined,
411
+ step.limit ?? undefined
412
+ )
413
+ );
414
+ case 'load':
415
+ return outcome(() => store.load());
416
+ case 'save':
417
+ return outcome(() => store.save(step.record));
418
+ default:
419
+ return { threw: true, message: 'unknown exitLoop fn ' + String(step.fn) };
420
+ }
421
+ });
422
+ return substitute(
423
+ { results, writeErrors, files: snapshotFiles(dir) },
424
+ dir,
425
+ dirPlaceholder
426
+ );
427
+ }
428
+ case 'guardScenario': {
429
+ const trace = [];
430
+ const log = collectingLog(trace);
431
+ const sessionSpecs = testCase.sessions ?? [];
432
+ const sessions = sessionSpecs.map((spec, index) => ({
433
+ handleCodexNotification: (method, params) => {
434
+ trace.push({ trace: 'session_dispatch', session: index, method, params });
435
+ if (spec.throws === true) {
436
+ throw new Error('scripted session dispatch failure ' + String(index));
437
+ }
438
+ },
439
+ }));
440
+ const guard = createCodexTurnFailureGuard({
441
+ sessions: () => sessions,
442
+ log,
443
+ ...(testCase.hasOnActiveThreadsFailed === true
444
+ ? {
445
+ onActiveThreadsFailed: (threadIds) => {
446
+ trace.push({ trace: 'active_threads_failed', threadIds: [...threadIds] });
447
+ },
448
+ }
449
+ : {}),
450
+ });
451
+ for (const step of testCase.steps ?? []) {
452
+ switch (step.fn) {
453
+ case 'observe':
454
+ guard.observeNotification(step.method, step.params ?? {});
455
+ break;
456
+ case 'fail':
457
+ guard.failActiveTurns(String(step.cause ?? 'scripted cause'));
458
+ break;
459
+ case 'failPerTurn': {
460
+ const causes = step.causes ?? {};
461
+ guard.failActiveTurns(
462
+ (turn) =>
463
+ causes[turn.threadId + '|' + turn.turnId] ??
464
+ String(step.defaultCause ?? 'scripted default cause')
465
+ );
466
+ break;
467
+ }
468
+ case 'count':
469
+ trace.push({ trace: 'count', value: guard.activeTurnCount() });
470
+ break;
471
+ case 'turns':
472
+ trace.push({ trace: 'turns', value: guard.activeTurns().map((t) => ({ ...t })) });
473
+ break;
474
+ case 'lastActivity':
475
+ // Wall-clock stamped inside the TS original: presence only.
476
+ trace.push({
477
+ trace: 'last_activity_defined',
478
+ value: guard.lastActivityAtMs() !== undefined,
479
+ });
480
+ break;
481
+ default:
482
+ trace.push({ trace: 'unknown_step', fn: String(step.fn) });
483
+ }
484
+ }
485
+ return { trace };
486
+ }
487
+ case 'awaitLossReports': {
488
+ const trace = [];
489
+ const pushes = (testCase.pushes ?? []).map((push) => {
490
+ if (push.kind === 'resolve') {
491
+ return Promise.resolve(undefined);
492
+ }
493
+ if (push.kind === 'reject') {
494
+ const rejected = Promise.reject(
495
+ new Error(String(push.message ?? 'scripted push rejection'))
496
+ );
497
+ // Pre-observed so the scripted rejection can never trip the
498
+ // unhandled-rejection handler before allSettled adopts it.
499
+ rejected.catch(() => {});
500
+ return rejected;
501
+ }
502
+ // 'never': a push whose ack never comes back.
503
+ return new Promise(() => {});
504
+ });
505
+ await awaitLossReportsThenExit({
506
+ pushes,
507
+ exit: () => trace.push({ trace: 'exit' }),
508
+ safetyCapMs: testCase.safetyCapMs,
509
+ setTimeoutFn: (callback, ms) => {
510
+ trace.push({ trace: 'cap_scheduled', ms });
511
+ if (testCase.capFires === true) {
512
+ setImmediate(callback);
513
+ }
514
+ return undefined;
515
+ },
516
+ });
517
+ trace.push({ trace: 'returned' });
518
+ return { trace };
519
+ }
520
+ default:
521
+ return { threw: true, message: 'unknown respawn fn ' + String(testCase.fn) };
522
+ }
523
+ }
524
+
525
+ // --- Dispatch --------------------------------------------------------------------------------
526
+
527
+ async function handle(testCase) {
528
+ switch (testCase.op) {
529
+ case 'controlDispatch':
530
+ return await handleControlDispatch(testCase);
531
+ case 'staleness':
532
+ return handleStaleness(testCase);
533
+ case 'respawn':
534
+ return await handleRespawn(testCase);
535
+ default:
536
+ return { threw: true, message: 'unknown op ' + String(testCase.op) };
537
+ }
538
+ }
539
+
540
+ let stdin = '';
541
+ process.stdin.setEncoding('utf8');
542
+ for await (const chunk of process.stdin) {
543
+ stdin += chunk;
544
+ }
545
+ const cases = JSON.parse(stdin);
546
+ const results = [];
547
+ for (const testCase of cases) {
548
+ results.push(await handle(testCase));
549
+ }
550
+ // Exit only after stdout is fully flushed: the runner graph can leave
551
+ // unref'd handles alive, so the driver exits explicitly - but a bare
552
+ // process.exit(0) truncates large result arrays mid-pipe.
553
+ process.stdout.write(JSON.stringify(results), () => {
554
+ process.exit(0);
555
+ });
556
+ `;
557
+
558
+ await mkdir(join(packageRoot, '.differential'), { recursive: true });
559
+
560
+ // runner.ts's graph resolves assets/ relative to the bundle at import time
561
+ // (same requirement as build-transport-parity-oracle.mjs).
562
+ await mkdir(join(packageRoot, '.differential/assets'), { recursive: true });
563
+ await copyFile(
564
+ join(packageRoot, 'src/assets/linzumi-logo.svg'),
565
+ join(packageRoot, '.differential/assets/linzumi-logo.svg')
566
+ );
567
+
568
+ await build({
569
+ stdin: {
570
+ contents: driverSource,
571
+ resolveDir: packageRoot,
572
+ sourcefile: 'core-commander-seams-parity-driver.ts',
573
+ loader: 'ts',
574
+ },
575
+ bundle: true,
576
+ platform: 'node',
577
+ target: 'node20',
578
+ format: 'esm',
579
+ outfile: join(packageRoot, '.differential/core-commander-seams-parity-oracle.mjs'),
580
+ minify: false,
581
+ sourcemap: false,
582
+ legalComments: 'none',
583
+ // runner.ts pulls the wider commander graph; the same runtime externals
584
+ // as the differential entry build stay external.
585
+ external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
586
+ });