@linzumi/cli 0.0.104-beta → 0.0.106-beta
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 +1 -1
- package/dist/index.js +375 -30
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -10637,6 +10637,28 @@ async function handleKandanChatEvent(args, state, runnerIdentity, payloadContext
|
|
|
10637
10637
|
}
|
|
10638
10638
|
startPortForwardWatchIfEnabled(args, state, payloadContext);
|
|
10639
10639
|
if (event.threadId !== state.kandanThreadId) {
|
|
10640
|
+
if (args.options.onUnroutedThreadMessage !== void 0) {
|
|
10641
|
+
try {
|
|
10642
|
+
const outcome = await args.options.onUnroutedThreadMessage(event);
|
|
10643
|
+
if (outcome === "spawned") {
|
|
10644
|
+
args.log("kandan.message_spawn_on_miss", {
|
|
10645
|
+
seq: event.seq,
|
|
10646
|
+
actor_slug: event.actorSlug ?? null,
|
|
10647
|
+
actor_user_id: event.actorUserId ?? null,
|
|
10648
|
+
thread_id: event.threadId ?? null,
|
|
10649
|
+
bound_thread_id: state.kandanThreadId ?? null,
|
|
10650
|
+
body_preview: runnerConsoleBodyPreview(event.body)
|
|
10651
|
+
});
|
|
10652
|
+
return;
|
|
10653
|
+
}
|
|
10654
|
+
} catch (error) {
|
|
10655
|
+
args.log("kandan.message_spawn_on_miss_failed", {
|
|
10656
|
+
seq: event.seq,
|
|
10657
|
+
thread_id: event.threadId ?? null,
|
|
10658
|
+
message: error instanceof Error ? error.message : String(error)
|
|
10659
|
+
});
|
|
10660
|
+
}
|
|
10661
|
+
}
|
|
10640
10662
|
args.log("kandan.message_ignored", {
|
|
10641
10663
|
seq: event.seq,
|
|
10642
10664
|
actor_slug: event.actorSlug ?? null,
|
|
@@ -10750,7 +10772,9 @@ async function startThreadMessageTurn(args, state, payloadContext, message) {
|
|
|
10750
10772
|
actorSlug: message.actorSlug,
|
|
10751
10773
|
actorUserId: message.actorUserId,
|
|
10752
10774
|
body: message.body,
|
|
10753
|
-
attachments
|
|
10775
|
+
// Forward the user's attachments (files/images) just like the normal routed
|
|
10776
|
+
// path; a replayed follow-up with files must not lose them.
|
|
10777
|
+
attachments: message.attachments ?? [],
|
|
10754
10778
|
receivedAtMs: Date.now()
|
|
10755
10779
|
};
|
|
10756
10780
|
enqueuePendingKandanMessage(state.queue, queued);
|
|
@@ -19948,7 +19972,7 @@ var linzumiCliVersion, linzumiCliVersionText;
|
|
|
19948
19972
|
var init_version = __esm({
|
|
19949
19973
|
"src/version.ts"() {
|
|
19950
19974
|
"use strict";
|
|
19951
|
-
linzumiCliVersion = "0.0.
|
|
19975
|
+
linzumiCliVersion = "0.0.106-beta";
|
|
19952
19976
|
linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
19953
19977
|
}
|
|
19954
19978
|
});
|
|
@@ -24749,6 +24773,10 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24749
24773
|
clearForwardPortAttribution(port);
|
|
24750
24774
|
return capabilitiesPayload();
|
|
24751
24775
|
},
|
|
24776
|
+
// Catch unrouted thread.message events even when no dynamic worker
|
|
24777
|
+
// session is attached (the durable thread's worker fully died). Late-
|
|
24778
|
+
// bound to dodge the const's temporal dead zone (defined below).
|
|
24779
|
+
onUnroutedThreadMessage: (event) => handleUnroutedThreadMessage(event),
|
|
24752
24780
|
channelSession: options.channelSession
|
|
24753
24781
|
},
|
|
24754
24782
|
log: log2
|
|
@@ -24879,6 +24907,34 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
24879
24907
|
);
|
|
24880
24908
|
threadRunnerProcesses.clear();
|
|
24881
24909
|
});
|
|
24910
|
+
const reapStaleThreadRunnerProcesses = () => {
|
|
24911
|
+
for (const [kandanThreadId, entry] of Array.from(
|
|
24912
|
+
threadRunnerProcesses.entries()
|
|
24913
|
+
)) {
|
|
24914
|
+
if (threadRunnerEntryLiveness(entry) !== "dead") {
|
|
24915
|
+
continue;
|
|
24916
|
+
}
|
|
24917
|
+
if (threadRunnerProcesses.get(kandanThreadId) !== entry) {
|
|
24918
|
+
continue;
|
|
24919
|
+
}
|
|
24920
|
+
threadRunnerProcesses.delete(kandanThreadId);
|
|
24921
|
+
log2("runner.thread_process_stale_entry_reaped", {
|
|
24922
|
+
kandanThreadId,
|
|
24923
|
+
pid: entry.kind === "running" ? entry.handle.processPid ?? null : null,
|
|
24924
|
+
prior_kind: entry.kind
|
|
24925
|
+
});
|
|
24926
|
+
void closeThreadRunnerEntry(entry).catch(() => void 0);
|
|
24927
|
+
}
|
|
24928
|
+
};
|
|
24929
|
+
if (shouldUseThreadProcesses(options)) {
|
|
24930
|
+
const reaperIntervalMs = options.threadRunnerReaperIntervalMs ?? THREAD_RUNNER_REAPER_INTERVAL_MS;
|
|
24931
|
+
const reaperInterval = setInterval(
|
|
24932
|
+
reapStaleThreadRunnerProcesses,
|
|
24933
|
+
reaperIntervalMs
|
|
24934
|
+
);
|
|
24935
|
+
reaperInterval.unref?.();
|
|
24936
|
+
cleanup.actions.push(() => clearInterval(reaperInterval));
|
|
24937
|
+
}
|
|
24882
24938
|
const threadModelProviderBindings = /* @__PURE__ */ new Map();
|
|
24883
24939
|
const threadRebuildSerializer = createThreadRebuildSerializer();
|
|
24884
24940
|
const evictStaleDynamicChannelSession = async (codexThreadId, expectClient) => {
|
|
@@ -25081,6 +25137,10 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25081
25137
|
fast: control.fast ?? options.fast,
|
|
25082
25138
|
launchTui: false,
|
|
25083
25139
|
rebuildOnDeadCodexWorker,
|
|
25140
|
+
// Late-bound (defined after startThreadRunnerProcess); the arrow keeps
|
|
25141
|
+
// it out of the const's temporal dead zone while still resolving the
|
|
25142
|
+
// live handler when an unrouted chat event actually arrives.
|
|
25143
|
+
onUnroutedThreadMessage: (event) => handleUnroutedThreadMessage(event),
|
|
25084
25144
|
pipelinePersistDir: commanderOutboxPersistDir(),
|
|
25085
25145
|
enablePortForwardWatch: true,
|
|
25086
25146
|
initialForwardPorts: allowedForwardPorts,
|
|
@@ -25277,15 +25337,29 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25277
25337
|
}
|
|
25278
25338
|
const existing = threadRunnerProcesses.get(kandanThreadId);
|
|
25279
25339
|
if (existing !== void 0) {
|
|
25280
|
-
|
|
25281
|
-
|
|
25282
|
-
|
|
25283
|
-
|
|
25284
|
-
|
|
25285
|
-
|
|
25340
|
+
const liveness = threadRunnerEntryLiveness(existing);
|
|
25341
|
+
if (liveness !== "dead") {
|
|
25342
|
+
return {
|
|
25343
|
+
instanceId,
|
|
25344
|
+
controlType: control.type,
|
|
25345
|
+
ok: true,
|
|
25346
|
+
delegated: true,
|
|
25347
|
+
threadProcess: "already_started",
|
|
25348
|
+
kandanThreadId,
|
|
25349
|
+
cwd
|
|
25350
|
+
};
|
|
25351
|
+
}
|
|
25352
|
+
log2("runner.thread_process_stale_entry_evicted", {
|
|
25286
25353
|
kandanThreadId,
|
|
25287
|
-
|
|
25288
|
-
|
|
25354
|
+
codex_thread_id: control.type === "reconnect_thread" ? stringValue(control.codexThreadId) ?? null : null,
|
|
25355
|
+
prior_kind: existing.kind,
|
|
25356
|
+
pid: existing.kind === "running" ? existing.handle.processPid ?? null : null,
|
|
25357
|
+
control_type: control.type
|
|
25358
|
+
});
|
|
25359
|
+
if (threadRunnerProcesses.get(kandanThreadId) === existing) {
|
|
25360
|
+
threadRunnerProcesses.delete(kandanThreadId);
|
|
25361
|
+
}
|
|
25362
|
+
void closeThreadRunnerEntry(existing).catch(() => void 0);
|
|
25289
25363
|
}
|
|
25290
25364
|
const spawnThreadRunner = options.spawnThreadRunner ?? spawnLocalThreadRunnerProcess;
|
|
25291
25365
|
const readyTimeoutMs = threadRunnerReadyTimeoutMs(options);
|
|
@@ -25398,6 +25472,17 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25398
25472
|
(port) => port.port
|
|
25399
25473
|
)
|
|
25400
25474
|
});
|
|
25475
|
+
const evictOnFailedResult = async (result) => {
|
|
25476
|
+
if (result !== void 0 && result.ok !== false) {
|
|
25477
|
+
return result;
|
|
25478
|
+
}
|
|
25479
|
+
const entry = threadRunnerProcesses.get(kandanThreadId);
|
|
25480
|
+
if (entry === startingEntry || entry?.kind === "running") {
|
|
25481
|
+
threadRunnerProcesses.delete(kandanThreadId);
|
|
25482
|
+
}
|
|
25483
|
+
await (entry?.kind === "running" ? entry.handle.close() : handle.close()).catch(() => void 0);
|
|
25484
|
+
return result;
|
|
25485
|
+
};
|
|
25401
25486
|
switch (control.type) {
|
|
25402
25487
|
case "start_instance": {
|
|
25403
25488
|
const result = await startCodexProviderInstance({
|
|
@@ -25413,27 +25498,29 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25413
25498
|
setStartupStage: () => void 0,
|
|
25414
25499
|
setStartedCodexThreadId: () => void 0
|
|
25415
25500
|
});
|
|
25416
|
-
return result.controlResponse;
|
|
25501
|
+
return await evictOnFailedResult(result.controlResponse);
|
|
25417
25502
|
}
|
|
25418
25503
|
case "reconnect_thread":
|
|
25419
|
-
return await
|
|
25420
|
-
|
|
25421
|
-
|
|
25422
|
-
|
|
25423
|
-
|
|
25424
|
-
|
|
25425
|
-
|
|
25426
|
-
|
|
25427
|
-
|
|
25428
|
-
|
|
25429
|
-
|
|
25430
|
-
|
|
25431
|
-
|
|
25432
|
-
|
|
25433
|
-
|
|
25434
|
-
|
|
25435
|
-
|
|
25436
|
-
|
|
25504
|
+
return await evictOnFailedResult(
|
|
25505
|
+
await applyControl(
|
|
25506
|
+
threadCodex,
|
|
25507
|
+
kandan,
|
|
25508
|
+
topic,
|
|
25509
|
+
instanceId,
|
|
25510
|
+
options,
|
|
25511
|
+
agentProviders,
|
|
25512
|
+
allowedCwds,
|
|
25513
|
+
remoteCodexSandboxRunner,
|
|
25514
|
+
activeRemoteCodexExecRequests,
|
|
25515
|
+
activeClaudeCodeSessions,
|
|
25516
|
+
pendingClaudeCodeApprovals,
|
|
25517
|
+
ensureClaudeCodeForwardPortSession,
|
|
25518
|
+
disposeClaudeCodeForwardPortSession,
|
|
25519
|
+
control,
|
|
25520
|
+
log2,
|
|
25521
|
+
onStartedThread,
|
|
25522
|
+
void 0
|
|
25523
|
+
)
|
|
25437
25524
|
);
|
|
25438
25525
|
}
|
|
25439
25526
|
} catch (error) {
|
|
@@ -25445,6 +25532,222 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
|
|
|
25445
25532
|
throw error;
|
|
25446
25533
|
}
|
|
25447
25534
|
};
|
|
25535
|
+
const spawnOnMissInFlight = /* @__PURE__ */ new Map();
|
|
25536
|
+
const spawnOnMissHandledMessages = /* @__PURE__ */ new Map();
|
|
25537
|
+
const replaySpawnOnMissMessageOnLiveSession = async (event, record, reconnectControl) => {
|
|
25538
|
+
try {
|
|
25539
|
+
log2("runner.spawn_on_miss_replay_on_sibling", {
|
|
25540
|
+
kandanThreadId: event.threadId ?? null,
|
|
25541
|
+
codex_thread_id: record.codexThreadId,
|
|
25542
|
+
seq: event.seq
|
|
25543
|
+
});
|
|
25544
|
+
const liveSession = await withCodexStartRequestTimeout(
|
|
25545
|
+
"thread/attach",
|
|
25546
|
+
resolveLiveDynamicChannelSession(record.codexThreadId, {
|
|
25547
|
+
waitForSiblingSpawn: true
|
|
25548
|
+
})
|
|
25549
|
+
);
|
|
25550
|
+
if (liveSession === void 0) {
|
|
25551
|
+
throw new Error(
|
|
25552
|
+
`spawn-on-miss could not resolve a live session for codex thread ${record.codexThreadId}`
|
|
25553
|
+
);
|
|
25554
|
+
}
|
|
25555
|
+
await liveSession.startThreadMessageTurn({
|
|
25556
|
+
seq: event.seq,
|
|
25557
|
+
body: event.body,
|
|
25558
|
+
actorSlug: event.actorSlug,
|
|
25559
|
+
actorUserId: event.actorUserId,
|
|
25560
|
+
// Forward the user's attachments; a replayed follow-up with files/images
|
|
25561
|
+
// must not be processed as if they were never sent.
|
|
25562
|
+
attachments: event.attachments,
|
|
25563
|
+
boundStartTimeout: true
|
|
25564
|
+
});
|
|
25565
|
+
} catch (error) {
|
|
25566
|
+
log2("runner.spawn_on_miss_failed", {
|
|
25567
|
+
kandanThreadId: event.threadId ?? null,
|
|
25568
|
+
codex_thread_id: record.codexThreadId,
|
|
25569
|
+
seq: event.seq,
|
|
25570
|
+
message: error instanceof Error ? error.message : String(error)
|
|
25571
|
+
});
|
|
25572
|
+
await publishSpawnOnMissMessageState(
|
|
25573
|
+
reconnectControl,
|
|
25574
|
+
"failed",
|
|
25575
|
+
`could not resume the thread's coding session: ${error instanceof Error ? error.message : String(error)}`,
|
|
25576
|
+
record.codexThreadId
|
|
25577
|
+
);
|
|
25578
|
+
}
|
|
25579
|
+
};
|
|
25580
|
+
const handleUnroutedThreadMessage = async (event) => {
|
|
25581
|
+
const kandanThreadId = event.threadId;
|
|
25582
|
+
if (!shouldUseThreadProcesses(options) || kandanThreadId === void 0) {
|
|
25583
|
+
return "ignored";
|
|
25584
|
+
}
|
|
25585
|
+
const messageKey = `${kandanThreadId}:${event.seq}`;
|
|
25586
|
+
const inFlightMessage = spawnOnMissHandledMessages.get(messageKey);
|
|
25587
|
+
if (inFlightMessage !== void 0) {
|
|
25588
|
+
return await inFlightMessage;
|
|
25589
|
+
}
|
|
25590
|
+
const run = handleUnroutedThreadMessageOnce(event, kandanThreadId);
|
|
25591
|
+
spawnOnMissHandledMessages.set(messageKey, run);
|
|
25592
|
+
try {
|
|
25593
|
+
return await run;
|
|
25594
|
+
} finally {
|
|
25595
|
+
spawnOnMissHandledMessages.delete(messageKey);
|
|
25596
|
+
}
|
|
25597
|
+
};
|
|
25598
|
+
const handleUnroutedThreadMessageOnce = async (event, kandanThreadId) => {
|
|
25599
|
+
const existing = threadRunnerProcesses.get(kandanThreadId);
|
|
25600
|
+
const existingLiveness = existing === void 0 ? void 0 : threadRunnerEntryLiveness(existing);
|
|
25601
|
+
if (existingLiveness === "live") {
|
|
25602
|
+
return "ignored";
|
|
25603
|
+
}
|
|
25604
|
+
const record = threadSessionRegistry.list().find((entry) => entry.kandanThreadId === kandanThreadId);
|
|
25605
|
+
if (record === void 0) {
|
|
25606
|
+
return "ignored";
|
|
25607
|
+
}
|
|
25608
|
+
const baseControl = rehydrationReconnectControl(record);
|
|
25609
|
+
const providerBinding = threadModelProviderBindings.get(kandanThreadId);
|
|
25610
|
+
const reconnectControl = {
|
|
25611
|
+
...baseControl,
|
|
25612
|
+
sourceSeq: event.seq,
|
|
25613
|
+
workDescription: event.body,
|
|
25614
|
+
// Carry the user's attachments on the reconstructed control (Codex P2) so
|
|
25615
|
+
// the FIRST spawn-on-miss message - the one that owns the respawn and runs
|
|
25616
|
+
// its turn inside startThreadRunnerProcess, not via the replay helper -
|
|
25617
|
+
// still resumes with its files/images. Re-serialized to the wire shape the
|
|
25618
|
+
// resume turn parses (parseKandanChatAttachments).
|
|
25619
|
+
...event.attachments.length === 0 ? {} : { attachments: kandanChatAttachmentsToWire(event.attachments) },
|
|
25620
|
+
...providerBinding?.modelProvider === void 0 ? {} : { modelProvider: providerBinding.modelProvider },
|
|
25621
|
+
...providerBinding?.llmProxy === void 0 ? {} : { llmProxy: providerBinding.llmProxy }
|
|
25622
|
+
};
|
|
25623
|
+
const cwd = resolveAllowedCwd(record.cwd, allowedCwds.value);
|
|
25624
|
+
if (!cwd.ok) {
|
|
25625
|
+
log2("runner.spawn_on_miss_cwd_rejected", {
|
|
25626
|
+
kandanThreadId,
|
|
25627
|
+
cwd: record.cwd,
|
|
25628
|
+
reason: cwd.reason,
|
|
25629
|
+
seq: event.seq
|
|
25630
|
+
});
|
|
25631
|
+
await publishSpawnOnMissMessageState(
|
|
25632
|
+
reconnectControl,
|
|
25633
|
+
"failed",
|
|
25634
|
+
`could not resume the thread's coding session: working directory ${record.cwd} is not an allowed root (${cwd.reason})`,
|
|
25635
|
+
record.codexThreadId
|
|
25636
|
+
);
|
|
25637
|
+
return "spawned";
|
|
25638
|
+
}
|
|
25639
|
+
const resolvedCwd = cwd.cwd;
|
|
25640
|
+
const inFlightSpawn = spawnOnMissInFlight.get(kandanThreadId);
|
|
25641
|
+
if (inFlightSpawn !== void 0) {
|
|
25642
|
+
log2("runner.spawn_on_miss_resuming", {
|
|
25643
|
+
kandanThreadId,
|
|
25644
|
+
codex_thread_id: record.codexThreadId,
|
|
25645
|
+
provider_session_key: record.codexThreadId,
|
|
25646
|
+
seq: event.seq,
|
|
25647
|
+
decision: "await_in_flight_spawn"
|
|
25648
|
+
});
|
|
25649
|
+
const outcome = await inFlightSpawn;
|
|
25650
|
+
if (outcome === "no_session") {
|
|
25651
|
+
await publishSpawnOnMissMessageState(
|
|
25652
|
+
reconnectControl,
|
|
25653
|
+
"failed",
|
|
25654
|
+
`could not resume the thread's coding session: no live session after spawn`,
|
|
25655
|
+
record.codexThreadId
|
|
25656
|
+
);
|
|
25657
|
+
return "spawned";
|
|
25658
|
+
}
|
|
25659
|
+
await replaySpawnOnMissMessageOnLiveSession(
|
|
25660
|
+
event,
|
|
25661
|
+
record,
|
|
25662
|
+
reconnectControl
|
|
25663
|
+
);
|
|
25664
|
+
return "spawned";
|
|
25665
|
+
}
|
|
25666
|
+
log2("runner.spawn_on_miss_resuming", {
|
|
25667
|
+
kandanThreadId,
|
|
25668
|
+
codex_thread_id: record.codexThreadId,
|
|
25669
|
+
provider_session_key: record.codexThreadId,
|
|
25670
|
+
seq: event.seq,
|
|
25671
|
+
decision: "spawned_resume"
|
|
25672
|
+
});
|
|
25673
|
+
let spawnSettle = () => void 0;
|
|
25674
|
+
const spawnPromise = new Promise((resolve12) => {
|
|
25675
|
+
spawnSettle = resolve12;
|
|
25676
|
+
});
|
|
25677
|
+
spawnOnMissInFlight.set(kandanThreadId, spawnPromise);
|
|
25678
|
+
try {
|
|
25679
|
+
const startResult = await startThreadRunnerProcess(
|
|
25680
|
+
reconnectControl,
|
|
25681
|
+
resolvedCwd
|
|
25682
|
+
);
|
|
25683
|
+
if (threadStartDelegatedToSibling(startResult)) {
|
|
25684
|
+
spawnSettle("spawned");
|
|
25685
|
+
await replaySpawnOnMissMessageOnLiveSession(
|
|
25686
|
+
event,
|
|
25687
|
+
record,
|
|
25688
|
+
reconnectControl
|
|
25689
|
+
);
|
|
25690
|
+
return "spawned";
|
|
25691
|
+
}
|
|
25692
|
+
if (startResult === void 0 || startResult.ok === false) {
|
|
25693
|
+
spawnSettle("no_session");
|
|
25694
|
+
const startError = startResult !== void 0 && typeof startResult.error === "string" ? startResult.error : "the thread could not be resumed (no resumable codex session)";
|
|
25695
|
+
log2("runner.spawn_on_miss_failed", {
|
|
25696
|
+
kandanThreadId,
|
|
25697
|
+
codex_thread_id: record.codexThreadId,
|
|
25698
|
+
seq: event.seq,
|
|
25699
|
+
message: startError
|
|
25700
|
+
});
|
|
25701
|
+
await publishSpawnOnMissMessageState(
|
|
25702
|
+
reconnectControl,
|
|
25703
|
+
"failed",
|
|
25704
|
+
`could not resume the thread's coding session: ${startError}`,
|
|
25705
|
+
record.codexThreadId
|
|
25706
|
+
);
|
|
25707
|
+
return "spawned";
|
|
25708
|
+
}
|
|
25709
|
+
spawnSettle("spawned");
|
|
25710
|
+
return "spawned";
|
|
25711
|
+
} catch (error) {
|
|
25712
|
+
spawnSettle("no_session");
|
|
25713
|
+
log2("runner.spawn_on_miss_failed", {
|
|
25714
|
+
kandanThreadId,
|
|
25715
|
+
codex_thread_id: record.codexThreadId,
|
|
25716
|
+
seq: event.seq,
|
|
25717
|
+
message: error instanceof Error ? error.message : String(error)
|
|
25718
|
+
});
|
|
25719
|
+
await publishSpawnOnMissMessageState(
|
|
25720
|
+
reconnectControl,
|
|
25721
|
+
"failed",
|
|
25722
|
+
`could not resume the thread's coding session: ${error instanceof Error ? error.message : String(error)}`,
|
|
25723
|
+
record.codexThreadId
|
|
25724
|
+
);
|
|
25725
|
+
return "spawned";
|
|
25726
|
+
} finally {
|
|
25727
|
+
spawnSettle("spawned");
|
|
25728
|
+
if (spawnOnMissInFlight.get(kandanThreadId) === spawnPromise) {
|
|
25729
|
+
spawnOnMissInFlight.delete(kandanThreadId);
|
|
25730
|
+
}
|
|
25731
|
+
}
|
|
25732
|
+
};
|
|
25733
|
+
const publishSpawnOnMissMessageState = async (control, status, reason, codexThreadId) => {
|
|
25734
|
+
const payload = startInstanceMessageStatePayload(control, status, reason, {
|
|
25735
|
+
instanceId,
|
|
25736
|
+
codexThreadId
|
|
25737
|
+
});
|
|
25738
|
+
if (payload === void 0) {
|
|
25739
|
+
return;
|
|
25740
|
+
}
|
|
25741
|
+
try {
|
|
25742
|
+
await kandan.push(topic, "message_state", payload);
|
|
25743
|
+
} catch (error) {
|
|
25744
|
+
log2("runner.spawn_on_miss_state_push_failed", {
|
|
25745
|
+
thread_id: optionalThreadControlField(control, "threadId") ?? null,
|
|
25746
|
+
status,
|
|
25747
|
+
message: error instanceof Error ? error.message : String(error)
|
|
25748
|
+
});
|
|
25749
|
+
}
|
|
25750
|
+
};
|
|
25448
25751
|
const heartbeatPayload = () => ({
|
|
25449
25752
|
instanceId,
|
|
25450
25753
|
clientId,
|
|
@@ -27462,11 +27765,15 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
|
|
|
27462
27765
|
})
|
|
27463
27766
|
);
|
|
27464
27767
|
const identity = identityFromAccessToken(options.token);
|
|
27768
|
+
const reconnectAttachments = parseKandanChatAttachments(
|
|
27769
|
+
arrayValue(control.attachments) ?? []
|
|
27770
|
+
);
|
|
27465
27771
|
await startedThreadSession.startThreadMessageTurn({
|
|
27466
27772
|
seq: sourceSeq,
|
|
27467
27773
|
body: workDescription,
|
|
27468
27774
|
actorSlug: identity.actorUsername,
|
|
27469
27775
|
actorUserId: identity.actorUserId,
|
|
27776
|
+
attachments: reconnectAttachments,
|
|
27470
27777
|
boundStartTimeout: true
|
|
27471
27778
|
});
|
|
27472
27779
|
}
|
|
@@ -28141,6 +28448,9 @@ async function startCodexProviderInstance(args) {
|
|
|
28141
28448
|
args.setStartupStage("starting_first_turn");
|
|
28142
28449
|
const rootSeq = integerValue(args.control.rootSeq);
|
|
28143
28450
|
const sourceSeq = integerValue(args.control.sourceSeq) ?? rootSeq;
|
|
28451
|
+
const firstTurnAttachments = parseKandanChatAttachments(
|
|
28452
|
+
arrayValue(args.control.attachments) ?? []
|
|
28453
|
+
);
|
|
28144
28454
|
if (startedThreadSession !== void 0 && sourceSeq !== void 0) {
|
|
28145
28455
|
const identity = identityFromAccessToken(args.options.token);
|
|
28146
28456
|
await startedThreadSession.startThreadMessageTurn({
|
|
@@ -28148,6 +28458,7 @@ async function startCodexProviderInstance(args) {
|
|
|
28148
28458
|
body: workDescription,
|
|
28149
28459
|
actorSlug: identity.actorUsername,
|
|
28150
28460
|
actorUserId: identity.actorUserId,
|
|
28461
|
+
attachments: firstTurnAttachments,
|
|
28151
28462
|
boundStartTimeout: true
|
|
28152
28463
|
});
|
|
28153
28464
|
} else {
|
|
@@ -29506,6 +29817,16 @@ function rehydrationControlSnapshot(args) {
|
|
|
29506
29817
|
...runtimeSettings.fast === void 0 ? {} : { fast: runtimeSettings.fast }
|
|
29507
29818
|
};
|
|
29508
29819
|
}
|
|
29820
|
+
function kandanChatAttachmentsToWire(attachments) {
|
|
29821
|
+
return attachments.map((attachment) => ({
|
|
29822
|
+
...attachment.id === void 0 ? {} : { id: attachment.id },
|
|
29823
|
+
...attachment.kind === void 0 ? {} : { kind: attachment.kind },
|
|
29824
|
+
...attachment.fileName === void 0 ? {} : { file_name: attachment.fileName },
|
|
29825
|
+
...attachment.contentType === void 0 ? {} : { content_type: attachment.contentType },
|
|
29826
|
+
...attachment.sizeBytes === void 0 ? {} : { size_bytes: attachment.sizeBytes },
|
|
29827
|
+
...attachment.url === void 0 ? {} : { url: attachment.url }
|
|
29828
|
+
}));
|
|
29829
|
+
}
|
|
29509
29830
|
function rehydrationReconnectControl(record) {
|
|
29510
29831
|
const snapshot = record.control;
|
|
29511
29832
|
const rootSeq = integerValue(snapshot.rootSeq);
|
|
@@ -29696,6 +30017,29 @@ function threadRunnerOptions(args) {
|
|
|
29696
30017
|
channelSession: void 0
|
|
29697
30018
|
};
|
|
29698
30019
|
}
|
|
30020
|
+
function processIsAlive2(pid) {
|
|
30021
|
+
if (pid === void 0 || !Number.isInteger(pid) || pid <= 0) {
|
|
30022
|
+
return true;
|
|
30023
|
+
}
|
|
30024
|
+
try {
|
|
30025
|
+
process.kill(pid, 0);
|
|
30026
|
+
return true;
|
|
30027
|
+
} catch (error) {
|
|
30028
|
+
const code = error?.code;
|
|
30029
|
+
return code === "EPERM";
|
|
30030
|
+
}
|
|
30031
|
+
}
|
|
30032
|
+
function threadRunnerEntryLiveness(entry) {
|
|
30033
|
+
if (entry.kind === "starting") {
|
|
30034
|
+
return "starting";
|
|
30035
|
+
}
|
|
30036
|
+
const handle = entry.handle;
|
|
30037
|
+
const ipcClosed = handle.codex.isClosed?.() === true;
|
|
30038
|
+
if (ipcClosed) {
|
|
30039
|
+
return "dead";
|
|
30040
|
+
}
|
|
30041
|
+
return processIsAlive2(handle.processPid) ? "live" : "dead";
|
|
30042
|
+
}
|
|
29699
30043
|
async function closeThreadRunnerEntry(entry) {
|
|
29700
30044
|
switch (entry.kind) {
|
|
29701
30045
|
case "running":
|
|
@@ -31411,7 +31755,7 @@ async function suggestRunnerTasks(control, options, allowedCwds) {
|
|
|
31411
31755
|
};
|
|
31412
31756
|
}
|
|
31413
31757
|
}
|
|
31414
|
-
var THREAD_RUNNER_READY_TIMEOUT_MS, onboardingDiscoveryAgentStartKeys, onboardingConversationImportKeys, onboardingConversationDiscoveryReportLimit, staleStartInstanceWindowMs, connectionStaleThresholdMs, connectionUnrecoverableAfterMs, controlCursorWriteDebounceMs, controlCursorEpochKey, codexTurnTerminalNotificationMethods, claudeSessionStoreSequenceHighWater, claudeCodeLlmProxySessionHeader, onboardingConversationTitleFirstMessages, onboardingConversationTitleLastMessages, onboardingConversationTitleBodyMaxLength, onboardingConversationImportMessageMaxLength, onboardingConversationImportCandidateLimit, onboardingConversationImportProgressInterval;
|
|
31758
|
+
var THREAD_RUNNER_READY_TIMEOUT_MS, THREAD_RUNNER_REAPER_INTERVAL_MS, onboardingDiscoveryAgentStartKeys, onboardingConversationImportKeys, onboardingConversationDiscoveryReportLimit, staleStartInstanceWindowMs, connectionStaleThresholdMs, connectionUnrecoverableAfterMs, controlCursorWriteDebounceMs, controlCursorEpochKey, codexTurnTerminalNotificationMethods, claudeSessionStoreSequenceHighWater, claudeCodeLlmProxySessionHeader, onboardingConversationTitleFirstMessages, onboardingConversationTitleLastMessages, onboardingConversationTitleBodyMaxLength, onboardingConversationImportMessageMaxLength, onboardingConversationImportCandidateLimit, onboardingConversationImportProgressInterval;
|
|
31415
31759
|
var init_runner = __esm({
|
|
31416
31760
|
"src/runner.ts"() {
|
|
31417
31761
|
"use strict";
|
|
@@ -31462,6 +31806,7 @@ var init_runner = __esm({
|
|
|
31462
31806
|
init_userFacingErrors();
|
|
31463
31807
|
init_remoteCodexSandboxRunner();
|
|
31464
31808
|
THREAD_RUNNER_READY_TIMEOUT_MS = 3e4;
|
|
31809
|
+
THREAD_RUNNER_REAPER_INTERVAL_MS = 3e4;
|
|
31465
31810
|
onboardingDiscoveryAgentStartKeys = /* @__PURE__ */ new Set();
|
|
31466
31811
|
onboardingConversationImportKeys = /* @__PURE__ */ new Set();
|
|
31467
31812
|
onboardingConversationDiscoveryReportLimit = 100;
|
package/package.json
CHANGED