@linzumi/cli 0.0.105-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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +90 -24
  3. package/package.json +1 -1
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@0.0.105-beta --version
61
+ npx -y @linzumi/cli@0.0.106-beta --version
62
62
  linzumi --version
63
63
  ```
64
64
 
package/dist/index.js CHANGED
@@ -19972,7 +19972,7 @@ var linzumiCliVersion, linzumiCliVersionText;
19972
19972
  var init_version = __esm({
19973
19973
  "src/version.ts"() {
19974
19974
  "use strict";
19975
- linzumiCliVersion = "0.0.105-beta";
19975
+ linzumiCliVersion = "0.0.106-beta";
19976
19976
  linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
19977
19977
  }
19978
19978
  });
@@ -25472,6 +25472,17 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
25472
25472
  (port) => port.port
25473
25473
  )
25474
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
+ };
25475
25486
  switch (control.type) {
25476
25487
  case "start_instance": {
25477
25488
  const result = await startCodexProviderInstance({
@@ -25487,27 +25498,29 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
25487
25498
  setStartupStage: () => void 0,
25488
25499
  setStartedCodexThreadId: () => void 0
25489
25500
  });
25490
- return result.controlResponse;
25501
+ return await evictOnFailedResult(result.controlResponse);
25491
25502
  }
25492
25503
  case "reconnect_thread":
25493
- return await applyControl(
25494
- threadCodex,
25495
- kandan,
25496
- topic,
25497
- instanceId,
25498
- options,
25499
- agentProviders,
25500
- allowedCwds,
25501
- remoteCodexSandboxRunner,
25502
- activeRemoteCodexExecRequests,
25503
- activeClaudeCodeSessions,
25504
- pendingClaudeCodeApprovals,
25505
- ensureClaudeCodeForwardPortSession,
25506
- disposeClaudeCodeForwardPortSession,
25507
- control,
25508
- log2,
25509
- onStartedThread,
25510
- void 0
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
+ )
25511
25524
  );
25512
25525
  }
25513
25526
  } catch (error) {
@@ -25520,6 +25533,7 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
25520
25533
  }
25521
25534
  };
25522
25535
  const spawnOnMissInFlight = /* @__PURE__ */ new Map();
25536
+ const spawnOnMissHandledMessages = /* @__PURE__ */ new Map();
25523
25537
  const replaySpawnOnMissMessageOnLiveSession = async (event, record, reconnectControl) => {
25524
25538
  try {
25525
25539
  log2("runner.spawn_on_miss_replay_on_sibling", {
@@ -25564,13 +25578,24 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
25564
25578
  }
25565
25579
  };
25566
25580
  const handleUnroutedThreadMessage = async (event) => {
25567
- if (!shouldUseThreadProcesses(options)) {
25568
- return "ignored";
25569
- }
25570
25581
  const kandanThreadId = event.threadId;
25571
- if (kandanThreadId === void 0) {
25582
+ if (!shouldUseThreadProcesses(options) || kandanThreadId === void 0) {
25572
25583
  return "ignored";
25573
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) => {
25574
25599
  const existing = threadRunnerProcesses.get(kandanThreadId);
25575
25600
  const existingLiveness = existing === void 0 ? void 0 : threadRunnerEntryLiveness(existing);
25576
25601
  if (existingLiveness === "live") {
@@ -25586,6 +25611,12 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
25586
25611
  ...baseControl,
25587
25612
  sourceSeq: event.seq,
25588
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) },
25589
25620
  ...providerBinding?.modelProvider === void 0 ? {} : { modelProvider: providerBinding.modelProvider },
25590
25621
  ...providerBinding?.llmProxy === void 0 ? {} : { llmProxy: providerBinding.llmProxy }
25591
25622
  };
@@ -25658,6 +25689,23 @@ async function openLocalCodexRunner(options, log2, cleanup, close) {
25658
25689
  );
25659
25690
  return "spawned";
25660
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
+ }
25661
25709
  spawnSettle("spawned");
25662
25710
  return "spawned";
25663
25711
  } catch (error) {
@@ -27717,11 +27765,15 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
27717
27765
  })
27718
27766
  );
27719
27767
  const identity = identityFromAccessToken(options.token);
27768
+ const reconnectAttachments = parseKandanChatAttachments(
27769
+ arrayValue(control.attachments) ?? []
27770
+ );
27720
27771
  await startedThreadSession.startThreadMessageTurn({
27721
27772
  seq: sourceSeq,
27722
27773
  body: workDescription,
27723
27774
  actorSlug: identity.actorUsername,
27724
27775
  actorUserId: identity.actorUserId,
27776
+ attachments: reconnectAttachments,
27725
27777
  boundStartTimeout: true
27726
27778
  });
27727
27779
  }
@@ -28396,6 +28448,9 @@ async function startCodexProviderInstance(args) {
28396
28448
  args.setStartupStage("starting_first_turn");
28397
28449
  const rootSeq = integerValue(args.control.rootSeq);
28398
28450
  const sourceSeq = integerValue(args.control.sourceSeq) ?? rootSeq;
28451
+ const firstTurnAttachments = parseKandanChatAttachments(
28452
+ arrayValue(args.control.attachments) ?? []
28453
+ );
28399
28454
  if (startedThreadSession !== void 0 && sourceSeq !== void 0) {
28400
28455
  const identity = identityFromAccessToken(args.options.token);
28401
28456
  await startedThreadSession.startThreadMessageTurn({
@@ -28403,6 +28458,7 @@ async function startCodexProviderInstance(args) {
28403
28458
  body: workDescription,
28404
28459
  actorSlug: identity.actorUsername,
28405
28460
  actorUserId: identity.actorUserId,
28461
+ attachments: firstTurnAttachments,
28406
28462
  boundStartTimeout: true
28407
28463
  });
28408
28464
  } else {
@@ -29761,6 +29817,16 @@ function rehydrationControlSnapshot(args) {
29761
29817
  ...runtimeSettings.fast === void 0 ? {} : { fast: runtimeSettings.fast }
29762
29818
  };
29763
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
+ }
29764
29830
  function rehydrationReconnectControl(record) {
29765
29831
  const snapshot = record.control;
29766
29832
  const rootSeq = integerValue(snapshot.rootSeq);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.105-beta",
3
+ "version": "0.0.106-beta",
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": {