@linzumi/cli 1.0.272 → 1.0.273

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.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/dist/impl-res/cloud-supervisor.mjs +69 -69
  3. package/dist/impl-res/index.js +353 -312
  4. package/dist/impl-res/mcp-server.mjs +34 -34
  5. package/dist/impl-ts/cloud-supervisor.mjs +69 -69
  6. package/dist/impl-ts/index.js +488 -488
  7. package/dist/impl-ts/mcp-server.mjs +1 -1
  8. package/dist/mcp-server.mjs +1 -1
  9. package/package.json +3 -2
  10. package/scripts/build-auth-parity-oracle.mjs +0 -709
  11. package/scripts/build-canonical-codex-transpiler-oracle.mjs +0 -93
  12. package/scripts/build-config-identity-parity-oracle.mjs +0 -526
  13. package/scripts/build-core-commander-seams-parity-oracle.mjs +0 -799
  14. package/scripts/build-core-daemon-parity-oracle.mjs +0 -573
  15. package/scripts/build-core-dispatch-input-queue-parity-oracle.mjs +0 -225
  16. package/scripts/build-core-fate-ladder-parity-oracle.mjs +0 -350
  17. package/scripts/build-core-generation-overlap-parity-oracle.mjs +0 -292
  18. package/scripts/build-core-integration-parity-oracle.mjs +0 -453
  19. package/scripts/build-core-lifecycle-guards-parity-oracle.mjs +0 -443
  20. package/scripts/build-core-lock-parity-oracle.mjs +0 -462
  21. package/scripts/build-core-reconnect-resume-parity-oracle.mjs +0 -365
  22. package/scripts/build-core-startup-resume-scan-parity-oracle.mjs +0 -296
  23. package/scripts/build-core-supervisor-parity-oracle.mjs +0 -781
  24. package/scripts/build-cross-harness-failover-oracle.mjs +0 -349
  25. package/scripts/build-editor-parity-oracle.mjs +0 -1525
  26. package/scripts/build-forwarding-parity-oracle.mjs +0 -1390
  27. package/scripts/build-harness-claude-parity-oracle.mjs +0 -407
  28. package/scripts/build-harness-codex-parity-oracle.mjs +0 -479
  29. package/scripts/build-mcp-parity-oracle.mjs +0 -412
  30. package/scripts/build-mcp-selector-parity-oracle.mjs +0 -65
  31. package/scripts/build-onboarding-discovery-parity-oracle.mjs +0 -388
  32. package/scripts/build-onboarding-flow-parity-oracle.mjs +0 -562
  33. package/scripts/build-onboarding-gateway-parity-oracle.mjs +0 -313
  34. package/scripts/build-onboarding-support-parity-oracle.mjs +0 -519
  35. package/scripts/build-pipeline-parity-oracle.mjs +0 -614
  36. package/scripts/build-protocol-parity-oracle.mjs +0 -398
  37. package/scripts/build-queue-parity-oracle.mjs +0 -1012
  38. package/scripts/build-session-registry-parity-oracle.mjs +0 -582
  39. package/scripts/build-transport-parity-oracle.mjs +0 -694
  40. package/scripts/build-tui-parity-oracle.mjs +0 -577
  41. package/scripts/build-vm-sandbox-parity-oracle.mjs +0 -1281
  42. package/scripts/build-worker-entry-parity-oracle.mjs +0 -424
  43. package/scripts/build-worker-spawn-parity-oracle.mjs +0 -823
@@ -1,365 +0,0 @@
1
- // Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
2
- // (M3 cluster 10, commander core - the reconnect-resume recovery arm).
3
- // Relationship: Builds the TS-side PARITY ORACLE for the reconnect-path
4
- // resume orchestration of the ReScript commander port, the sibling of
5
- // build-pipeline-parity-oracle.mjs (cluster 9) and
6
- // build-harness-codex-parity-oracle.mjs (cluster 7). The oracle bundles
7
- // the REAL runner.ts reconnect-resume exports - the I200 resume decision
8
- // ladder (resumeCodexThreadForReconnect, transitively
9
- // unarchiveAndResumeCodexThread, the archived/missing recognizers, and
10
- // channelSession.ts's withCodexStartRequestTimeout) and the two rehydration
11
- // fetch legs (fetchThreadRehydrationMessagesForTest /
12
- // fetchThreadTranscriptForRehydrationForTest, transitively the
13
- // reconnectContext.ts message parser and the transcriptCompiler.ts
14
- // canonical-page parser) - behind the cluster 1/2 one-shot BATCH driver (a
15
- // JSON array of cases on stdin, a JSON array of results on stdout).
16
- //
17
- // Scripting: the codex JSON-RPC request seam and the Phoenix push seam are
18
- // per-case scripted reply queues (result / json-rpc error / rejection /
19
- // never-settle hang); the rehydration builders are scripted
20
- // (return/undefined/throw). Timeouts: the REAL withCodexStartRequestTimeout
21
- // runs inside the bundle, bounded per case through its own
22
- // LINZUMI_CODEX_START_REQUEST_TIMEOUT_MS knob (set from the case input
23
- // before each run), so a scripted hang fails with the genuine sentinel
24
- // bytes after a genuinely elapsed (small) bound. The batch loop holds a
25
- // keepAlive interval while awaiting - the sentinel timer is unref'd by
26
- // design and node would otherwise exit mid-hang (the cluster 7 lesson).
27
- //
28
- // Byte parity: every request (with params), push (with payload), log
29
- // event (with payload), builder invocation, and the final
30
- // outcome/thrown-message ride the returned trace VERBATIM (the TS
31
- // objects' own key order rides JSON.stringify), so the ReScript side's
32
- // trace serialization must reproduce field presence AND insertion order
33
- // exactly.
34
- //
35
- // Import-time side effects: runner.ts's graph resolves assets/ relative
36
- // to the bundle at import time (the hello-project scaffold's inlined
37
- // logo), so the asset is copied next to the emitted oracle - the
38
- // build-harness-codex-parity-oracle precedent. No other stubs are needed;
39
- // the externals list matches the other oracles.
40
- //
41
- // SECRETS: the oracle only ever sees the conformance suite's synthetic
42
- // payloads. The output is a local test artifact (.differential/ is
43
- // gitignored, never published) and stays unminified for debuggability.
44
- import { copyFile, mkdir } from 'node:fs/promises';
45
- import { dirname, join } from 'node:path';
46
- import { fileURLToPath } from 'node:url';
47
- import { build } from 'esbuild';
48
-
49
- const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
50
-
51
- const driverSource = `
52
- import {
53
- fetchThreadRehydrationMessagesForTest,
54
- fetchThreadTranscriptForRehydrationForTest,
55
- resumeCodexThreadForReconnect,
56
- } from './src/runner';
57
- import {
58
- resumeOrRehydrateCodexThread,
59
- withCodexCompletionSnapshotRequestTimeout,
60
- } from './src/channelSession';
61
-
62
- // Scripted codex JSON-RPC request seam: replies are consumed in call
63
- // order; every call is traced with its params. An exhausted script
64
- // REJECTS loudly (mirrored byte-for-byte by the ReScript scripted seam).
65
- function scriptedRequest(script, trace) {
66
- let index = 0;
67
- return (method, params) => {
68
- trace.push({ t: 'request', method, params: params ?? null });
69
- const step = script[index];
70
- index += 1;
71
- if (step === undefined) {
72
- return Promise.reject(new Error('unscripted request: ' + method));
73
- }
74
- if (step.hang === true) {
75
- return new Promise(() => {});
76
- }
77
- if (step.reject !== undefined) {
78
- return Promise.reject(new Error(step.reject));
79
- }
80
- if (step.error !== undefined) {
81
- return Promise.resolve({ error: step.error });
82
- }
83
- return Promise.resolve({ result: step.result ?? {} });
84
- };
85
- }
86
-
87
- async function runResumeCase(input) {
88
- // The REAL wrapper resolves its bound per call through this knob.
89
- process.env.LINZUMI_CODEX_START_REQUEST_TIMEOUT_MS = String(
90
- input.timeoutMs
91
- );
92
- const trace = [];
93
- const request = scriptedRequest(input.requestScript ?? [], trace);
94
- const rehydration = {
95
- forceRehydrate: input.forceRehydrate === true,
96
- buildRehydrationText: async (resumeFailure) => {
97
- trace.push({ t: 'buildText', resumeFailure });
98
- const script = input.textScript ?? {};
99
- if (script.throw !== undefined) {
100
- throw new Error(script.throw);
101
- }
102
- return script.text;
103
- },
104
- buildRehydrationItems:
105
- input.itemsScript === undefined
106
- ? undefined
107
- : async (resumeFailure) => {
108
- trace.push({ t: 'buildItems', resumeFailure });
109
- const script = input.itemsScript;
110
- if (script.throw !== undefined) {
111
- throw new Error(script.throw);
112
- }
113
- return script.items;
114
- },
115
- log: (event, payload) => trace.push({ t: 'log', event, payload }),
116
- };
117
- const outcome = await resumeCodexThreadForReconnect(
118
- { request },
119
- input.codexThreadId,
120
- input.resumeOverrides ?? {},
121
- rehydration
122
- ).then(
123
- (value) => ({ ok: true, value }),
124
- (error) => ({
125
- ok: false,
126
- message: error instanceof Error ? error.message : String(error),
127
- })
128
- );
129
- trace.push({ t: 'outcome', outcome });
130
- return { trace };
131
- }
132
-
133
- // I200 hosted-agent narrow resume: the same shared core as reconnect, but
134
- // with the deliberately narrow missing-rollout-only recovery gate and the
135
- // path-specific loud error prefix/final log used by
136
- // narrowExistingLinzumiAgentThread.
137
- async function runNarrowCase(input) {
138
- process.env.LINZUMI_CODEX_START_REQUEST_TIMEOUT_MS = String(
139
- input.timeoutMs
140
- );
141
- const trace = [];
142
- const request = scriptedRequest(input.requestScript ?? [], trace);
143
- const rehydration = {
144
- forceRehydrate: false,
145
- buildRehydrationText: async (resumeFailure) => {
146
- trace.push({ t: 'buildText', resumeFailure });
147
- return input.textScript?.text;
148
- },
149
- buildRehydrationItems:
150
- input.itemsScript === undefined
151
- ? undefined
152
- : async (resumeFailure) => {
153
- trace.push({ t: 'buildItems', resumeFailure });
154
- return input.itemsScript.items;
155
- },
156
- log: (event, payload) => trace.push({ t: 'log', event, payload }),
157
- };
158
- const outcome = await resumeOrRehydrateCodexThread({
159
- codex: { request },
160
- codexThreadId: input.codexThreadId,
161
- resumeOverrides: input.resumeOverrides ?? {},
162
- rebuildWhen: 'missing-rollout-only',
163
- rehydration,
164
- }).then(
165
- (value) => {
166
- trace.push({
167
- t: 'log',
168
- event: 'linzumi.agent_thread_narrowed',
169
- payload: {
170
- codex_thread_id: input.codexThreadId,
171
- approval_policy: 'never',
172
- sandbox: 'read-only',
173
- rehydrated: value.mode === 'rehydrated',
174
- },
175
- });
176
- return { ok: true, value };
177
- },
178
- (error) => ({
179
- ok: false,
180
- message:
181
- 'failed to narrow existing @linzumi Codex thread: ' +
182
- (error instanceof Error ? error.message : String(error)),
183
- })
184
- );
185
- trace.push({ t: 'outcome', outcome });
186
- return { trace };
187
- }
188
-
189
- // Scripted Phoenix push seam shared by the two fetch legs.
190
- function scriptedPush(script, trace) {
191
- let index = 0;
192
- return {
193
- push: async (topic, event, payload) => {
194
- trace.push({ t: 'push', topic, event, payload });
195
- const step = script[index];
196
- index += 1;
197
- if (step === undefined) {
198
- throw new Error('unscripted push: ' + event);
199
- }
200
- if (step.throw !== undefined) {
201
- throw new Error(step.throw);
202
- }
203
- return step.reply;
204
- },
205
- };
206
- }
207
-
208
- function fetchTarget(input) {
209
- return {
210
- workspace: input.target.workspace ?? undefined,
211
- channel: input.target.channel ?? undefined,
212
- threadId: input.target.threadId ?? undefined,
213
- };
214
- }
215
-
216
- async function runFetchMessagesCase(input) {
217
- const trace = [];
218
- const client = scriptedPush(input.pushScript ?? [], trace);
219
- const messages = await fetchThreadRehydrationMessagesForTest(
220
- client,
221
- input.topic,
222
- fetchTarget(input),
223
- (event, payload) => trace.push({ t: 'log', event, payload })
224
- );
225
- trace.push({ t: 'messages', messages });
226
- return { trace };
227
- }
228
-
229
- async function runFetchTranscriptCase(input) {
230
- const trace = [];
231
- const client = scriptedPush(input.pushScript ?? [], trace);
232
- const transcript = await fetchThreadTranscriptForRehydrationForTest(
233
- client,
234
- input.topic,
235
- fetchTarget(input),
236
- (event, payload) => trace.push({ t: 'log', event, payload })
237
- );
238
- trace.push({ t: 'transcript', value: transcript ?? null });
239
- return { trace };
240
- }
241
-
242
- // The completion-snapshot bound (channelSession.ts
243
- // withCodexCompletionSnapshotRequestTimeout - the 2026-07-28 impl-res
244
- // silent-wedge law): the REAL wrapper runs against a scripted request
245
- // (result / rejection / never-settle hang). When the case carries an
246
- // explicit boundMs, it is passed as the third argument (the non-positive
247
- // unbounded guard); otherwise the wrapper resolves its bound through the
248
- // SAME LINZUMI_CODEX_START_REQUEST_TIMEOUT_MS knob as the start bound
249
- // (codexCompletionSnapshotRequestTimeoutMs delegates to it), set per case
250
- // - a hang genuinely waits the (small) bound out.
251
- async function runSnapshotTimeoutCase(input) {
252
- process.env.LINZUMI_CODEX_START_REQUEST_TIMEOUT_MS = String(
253
- input.timeoutMs
254
- );
255
- const trace = [];
256
- const step = input.requestStep ?? {};
257
- const request =
258
- step.hang === true
259
- ? new Promise(() => {})
260
- : step.reject !== undefined
261
- ? Promise.reject(new Error(step.reject))
262
- : Promise.resolve({ result: step.result ?? {} });
263
- const outcome = await (input.boundMs === undefined
264
- ? withCodexCompletionSnapshotRequestTimeout(input.method, request)
265
- : withCodexCompletionSnapshotRequestTimeout(
266
- input.method,
267
- request,
268
- input.boundMs
269
- )
270
- ).then(
271
- (value) => ({ ok: true, value }),
272
- (error) => ({
273
- ok: false,
274
- message: error instanceof Error ? error.message : String(error),
275
- })
276
- );
277
- trace.push({ t: 'outcome', outcome });
278
- return { trace };
279
- }
280
-
281
- async function runCase(input) {
282
- switch (input.kind) {
283
- case 'resumeRun':
284
- return runResumeCase(input);
285
- case 'narrowRun':
286
- return runNarrowCase(input);
287
- case 'fetchMessagesRun':
288
- return runFetchMessagesCase(input);
289
- case 'fetchTranscriptRun':
290
- return runFetchTranscriptCase(input);
291
- case 'snapshotTimeoutRun':
292
- return runSnapshotTimeoutCase(input);
293
- default:
294
- return { error: 'unknown case kind ' + String(input.kind) };
295
- }
296
- }
297
-
298
- async function runBatch() {
299
- const chunks = [];
300
- for await (const chunk of process.stdin) chunks.push(chunk);
301
- const cases = JSON.parse(Buffer.concat(chunks).toString('utf8'));
302
- const results = [];
303
- for (const testCase of cases) {
304
- // The sentinel timer is unref'd by design; hold the event loop open
305
- // while a hang case waits it out or node would exit mid-await.
306
- const keepAlive = setInterval(() => {}, 1000);
307
- try {
308
- results.push(await runCase(testCase));
309
- } finally {
310
- clearInterval(keepAlive);
311
- }
312
- }
313
- process.stdout.write(JSON.stringify(results), () => {
314
- process.exit(0);
315
- });
316
- }
317
-
318
- runBatch().then(
319
- () => {},
320
- (error) => {
321
- process.stderr.write(String(error && error.stack ? error.stack : error) + '\\n');
322
- process.exit(1);
323
- }
324
- );
325
- `;
326
-
327
- const outDir = join(packageRoot, '.differential');
328
- const outPath = join(outDir, 'core-reconnect-resume-parity-oracle.mjs');
329
-
330
- await mkdir(outDir, { recursive: true });
331
- // runner.ts's graph resolves assets/ relative to the bundle at import time
332
- // (the hello-project scaffold's inlined logo), so the asset must sit next
333
- // to the emitted oracle - the build-harness-codex-parity-oracle precedent.
334
- await mkdir(join(outDir, 'assets'), { recursive: true });
335
- await copyFile(
336
- join(packageRoot, 'src/assets/linzumi-logo.svg'),
337
- join(outDir, 'assets/linzumi-logo.svg')
338
- );
339
-
340
- await build({
341
- stdin: {
342
- contents: driverSource,
343
- resolveDir: packageRoot,
344
- sourcefile: 'core-reconnect-resume-parity-driver.ts',
345
- loader: 'ts',
346
- },
347
- bundle: true,
348
- // Spec: plans/2026-09-09-ts-native-account-continuation.md. Match the
349
- // production/auth-oracle ESM bridge for reachable CJS runtime dependencies;
350
- // the distinct banner binding cannot collide with bundled createRequire.
351
- banner: {
352
- js: "import { createRequire as __coreReconnectResumeOracleCreateRequire } from 'node:module';\nconst require = __coreReconnectResumeOracleCreateRequire(import.meta.url);",
353
- },
354
- platform: 'node',
355
- target: 'node20',
356
- format: 'esm',
357
- outfile: outPath,
358
- minify: false,
359
- sourcemap: false,
360
- legalComments: 'none',
361
- logLevel: 'silent',
362
- external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
363
- });
364
-
365
- process.stdout.write(outPath + '\n');
@@ -1,296 +0,0 @@
1
- // Spec: kandan/plans/2026-07-10-compute-nodes-rescript-commander-zero-ts-program.md
2
- // (M3 cluster 10, commander core).
3
- // Relationship: Builds the TS-side PARITY ORACLE for the startup
4
- // resurrection scan of the ReScript commander port, the sibling of
5
- // build-queue-parity-oracle.mjs (cluster 5). The oracle bundles the REAL
6
- // TS module (src/startupResumeScan.ts - the 2026-07-11
7
- // lease-adopted-no-resurrection incident fix; dossier:
8
- // kandan/server_v2/test/fixtures/prod-failures/
9
- // runner_restart_lease_adopted_no_resurrection_2026-07-11.md) behind the
10
- // cluster 1/2 one-shot batch driver: a JSON array of cases on stdin, a
11
- // JSON array of results on stdout. The ReScript package's
12
- // CoreStartupResumeScanParityConformance drives the SAME inputs through
13
- // the ported module and asserts:
14
- // - constants parity (terminal statuses, age gate, concurrency /
15
- // stagger / retry-backoff ladders);
16
- // - fold parity (foldOutboxTurnSignals over raw journal bytes, encoded
17
- // in map-insertion order so even the fold's internal ordering is
18
- // pinned);
19
- // - classification parity (classifyUnfinishedThreadWork matrices);
20
- // - scripted SCAN RUNS: runStartupResumeScan with every effect
21
- // (inspect/drain/respawn/publish/seq-in-flight/shutdown/sleep/log)
22
- // scripted, a virtual clock advanced only by the scripted sleep, and
23
- // ONE shared trace of effect calls + log events in order - compared
24
- // byte-for-byte across impls, so the bounded scheduler's interleaving
25
- // itself (worker pool, shared stagger chain, retry ladders, shutdown
26
- // aborts) is the contract.
27
- //
28
- // Determinism: nothing in a scan case touches the real clock or timers -
29
- // sleep records the requested delay, advances the virtual clock, and
30
- // resolves immediately; scripted effect outcomes resolve in order;
31
- // shutdown flips on a scripted effect-call count. The driver's effect
32
- // functions return plain resolved/rejected promises (never async
33
- // functions) so both impls award identical microtask hops.
34
- //
35
- // SECRETS: the oracle only ever sees the conformance suite's synthetic
36
- // fixtures; nothing here logs case contents.
37
- //
38
- // The output is a local test artifact (.differential/ is gitignored,
39
- // never published) and stays unminified for debuggability.
40
- import { mkdir } from 'node:fs/promises';
41
- import { dirname, join } from 'node:path';
42
- import { fileURLToPath } from 'node:url';
43
- import { build } from 'esbuild';
44
-
45
- const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
46
-
47
- const driverSource = `
48
- import {
49
- classifyUnfinishedThreadWork,
50
- defaultResumeSignalMaxAgeMs,
51
- defaultStartupResumeConcurrency,
52
- defaultStartupResumeRetryBackoffMs,
53
- defaultStartupResumeStaggerMs,
54
- defaultStartupRespawnRetryBackoffMs,
55
- foldOutboxTurnSignals,
56
- runStartupResumeScan,
57
- terminalMessageStateStatuses,
58
- } from './src/startupResumeScan';
59
-
60
- // --- Encoding helpers -------------------------------------------------------------
61
-
62
- // Signals encoded in MAP-INSERTION ORDER: the fold's internal ordering is
63
- // part of the parity contract (classification iterates the map).
64
- function encodeSignals(map) {
65
- return Array.from(map.values()).map((signal) => ({
66
- sourceSeq: signal.sourceSeq,
67
- sawActivity: signal.sawActivity,
68
- ...(signal.lastMessageStateStatus === undefined
69
- ? {}
70
- : { lastMessageStateStatus: signal.lastMessageStateStatus }),
71
- ...(signal.newestAtMs === undefined ? {} : { newestAtMs: signal.newestAtMs }),
72
- }));
73
- }
74
-
75
- function signalMapOf(spec) {
76
- if (typeof spec.journalRaw === 'string') {
77
- return foldOutboxTurnSignals(spec.journalRaw);
78
- }
79
- const map = new Map();
80
- for (const signal of spec.signals ?? []) {
81
- map.set(signal.sourceSeq, {
82
- sourceSeq: signal.sourceSeq,
83
- sawActivity: signal.sawActivity ?? false,
84
- lastMessageStateStatus: signal.lastMessageStateStatus ?? undefined,
85
- newestAtMs: signal.newestAtMs ?? undefined,
86
- });
87
- }
88
- return map;
89
- }
90
-
91
- function inputsOf(spec) {
92
- return {
93
- pendingInboundSeqs: spec.pendingInboundSeqs ?? [],
94
- ackedInboundSeqs: spec.ackedInboundSeqs ?? [],
95
- inboundHighWaterSeq: spec.inboundHighWaterSeq ?? undefined,
96
- appliedFloorSeq: spec.appliedFloorSeq ?? undefined,
97
- outboxTurnSignals: signalMapOf(spec),
98
- };
99
- }
100
-
101
- function encodeWork(work) {
102
- return {
103
- unfinished: work.unfinished,
104
- seqs: work.seqs.map((entry) => ({ sourceSeq: entry.sourceSeq, shape: entry.shape })),
105
- hasPendingInbound: work.hasPendingInbound,
106
- loudTerminalSeqs: [...work.loudTerminalSeqs],
107
- watermarkGapOnly: work.watermarkGapOnly,
108
- };
109
- }
110
-
111
- // --- Case handlers ----------------------------------------------------------------
112
-
113
- function handleConstants() {
114
- return {
115
- terminalMessageStateStatuses: Array.from(terminalMessageStateStatuses),
116
- defaultResumeSignalMaxAgeMs,
117
- defaultStartupResumeConcurrency,
118
- defaultStartupResumeStaggerMs,
119
- defaultStartupResumeRetryBackoffMs: [...defaultStartupResumeRetryBackoffMs],
120
- defaultStartupRespawnRetryBackoffMs: [...defaultStartupRespawnRetryBackoffMs],
121
- };
122
- }
123
-
124
- function handleFold(testCase) {
125
- return { signals: encodeSignals(foldOutboxTurnSignals(testCase.raw ?? '')) };
126
- }
127
-
128
- function handleClassify(testCase) {
129
- return encodeWork(
130
- classifyUnfinishedThreadWork(inputsOf(testCase.inputs ?? {}), {
131
- nowMs: testCase.nowMs ?? 0,
132
- maxSignalAgeMs: testCase.maxSignalAgeMs ?? undefined,
133
- })
134
- );
135
- }
136
-
137
- // One scripted scan run. Every effect resolves from the case script; the
138
- // shared events array is the trace both impls must reproduce byte for
139
- // byte (virtual-time stamps included).
140
- async function handleScan(testCase) {
141
- const options = testCase.options ?? {};
142
- const threads = testCase.threads ?? [];
143
- const events = [];
144
- let vnow = 0;
145
- let effectOps = 0;
146
- const shutdownAfterOps = options.shutdownAfterOps ?? undefined;
147
- const scripts = new Map(
148
- threads.map((thread) => [
149
- thread.threadId,
150
- {
151
- drain: [...(thread.drainScript ?? [])],
152
- respawn: [...(thread.respawnScript ?? [])],
153
- publish: [...(thread.publishScript ?? [])],
154
- inFlight: new Set(thread.inFlightSeqs ?? []),
155
- },
156
- ])
157
- );
158
-
159
- const summary = await runStartupResumeScan({
160
- records: threads,
161
- threadId: (record) => record.threadId,
162
- inspect: (record) => {
163
- events.push({ e: 'inspect', vt: vnow, thread: record.threadId });
164
- const spec = record.inspect ?? {};
165
- if (typeof spec.throw === 'string') {
166
- throw new Error(spec.throw);
167
- }
168
- return inputsOf(spec);
169
- },
170
- resumePendingInbound: (record) => {
171
- effectOps += 1;
172
- const step = scripts.get(record.threadId).drain.shift() ?? 'clear';
173
- if (typeof step === 'object' && step !== null) {
174
- events.push({
175
- e: 'drain',
176
- vt: vnow,
177
- thread: record.threadId,
178
- outcome: 'throw:' + step.throw,
179
- });
180
- return Promise.reject(new Error(step.throw));
181
- }
182
- events.push({ e: 'drain', vt: vnow, thread: record.threadId, outcome: step });
183
- return Promise.resolve(step);
184
- },
185
- respawnWorker: (record) => {
186
- effectOps += 1;
187
- const step = scripts.get(record.threadId).respawn.shift() ?? true;
188
- if (typeof step === 'object' && step !== null) {
189
- events.push({
190
- e: 'respawn',
191
- vt: vnow,
192
- thread: record.threadId,
193
- outcome: 'throw:' + step.throw,
194
- });
195
- return Promise.reject(new Error(step.throw));
196
- }
197
- events.push({ e: 'respawn', vt: vnow, thread: record.threadId, outcome: step });
198
- return Promise.resolve(step);
199
- },
200
- seqInFlight: (record, sourceSeq) => scripts.get(record.threadId).inFlight.has(sourceSeq),
201
- publishInterruptedTurnState: (record, sourceSeq, shape) => {
202
- effectOps += 1;
203
- const step = scripts.get(record.threadId).publish.shift() ?? 'ok';
204
- if (typeof step === 'object' && step !== null) {
205
- events.push({
206
- e: 'publish',
207
- vt: vnow,
208
- thread: record.threadId,
209
- seq: sourceSeq,
210
- shape,
211
- outcome: 'throw:' + step.throw,
212
- });
213
- return Promise.reject(new Error(step.throw));
214
- }
215
- events.push({
216
- e: 'publish',
217
- vt: vnow,
218
- thread: record.threadId,
219
- seq: sourceSeq,
220
- shape,
221
- outcome: step,
222
- });
223
- return Promise.resolve(undefined);
224
- },
225
- shuttingDown: () => shutdownAfterOps !== undefined && effectOps >= shutdownAfterOps,
226
- log: (event, fields) => {
227
- events.push({ e: 'log', vt: vnow, event, fields });
228
- },
229
- nowMs: options.nowMs ?? 0,
230
- maxSignalAgeMs: options.maxSignalAgeMs ?? undefined,
231
- concurrency: options.concurrency ?? undefined,
232
- staggerMs: options.staggerMs ?? undefined,
233
- retryBackoffMs: options.retryBackoffMs ?? undefined,
234
- respawnRetryBackoffMs: options.respawnRetryBackoffMs ?? undefined,
235
- sleep: (ms) => {
236
- effectOps += 1;
237
- events.push({ e: 'sleep', vt: vnow, ms });
238
- vnow += ms;
239
- return Promise.resolve(undefined);
240
- },
241
- });
242
-
243
- return { summary, events };
244
- }
245
-
246
- async function handle(testCase) {
247
- switch (testCase.op) {
248
- case 'constants':
249
- return handleConstants();
250
- case 'fold':
251
- return handleFold(testCase);
252
- case 'classify':
253
- return handleClassify(testCase);
254
- case 'scan':
255
- return await handleScan(testCase);
256
- default:
257
- return { threw: true, message: 'unknown op ' + String(testCase.op) };
258
- }
259
- }
260
-
261
- let stdin = '';
262
- process.stdin.setEncoding('utf8');
263
- for await (const chunk of process.stdin) {
264
- stdin += chunk;
265
- }
266
- const cases = JSON.parse(stdin);
267
- const results = [];
268
- for (const testCase of cases) {
269
- results.push(await handle(testCase));
270
- }
271
- // Exit after stdout is fully flushed (the cluster 5 lesson: a bare
272
- // process.exit(0) truncates large result arrays mid-pipe).
273
- process.stdout.write(JSON.stringify(results), () => {
274
- process.exit(0);
275
- });
276
- `;
277
-
278
- await mkdir(join(packageRoot, '.differential'), { recursive: true });
279
-
280
- await build({
281
- stdin: {
282
- contents: driverSource,
283
- resolveDir: packageRoot,
284
- sourcefile: 'core-startup-resume-scan-parity-driver.ts',
285
- loader: 'ts',
286
- },
287
- bundle: true,
288
- platform: 'node',
289
- target: 'node20',
290
- format: 'esm',
291
- outfile: join(packageRoot, '.differential/core-startup-resume-scan-parity-oracle.mjs'),
292
- minify: false,
293
- sourcemap: false,
294
- legalComments: 'none',
295
- external: ['ws', 'undici', 'blessed', '@anthropic-ai/claude-agent-sdk'],
296
- });