@linzumi/cli 0.0.67-beta → 0.0.69-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 +560 -98
  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.67-beta --version
61
+ npx -y @linzumi/cli@0.0.69-beta --version
62
62
  linzumi --version
63
63
  ```
64
64
 
package/dist/index.js CHANGED
@@ -286,6 +286,7 @@ var init_channelSessionSupport = __esm({
286
286
 
287
287
  // src/commanderAttachments.ts
288
288
  import { readFile, realpath, stat, writeFile, mkdir } from "node:fs/promises";
289
+ import { homedir } from "node:os";
289
290
  import {
290
291
  basename,
291
292
  extname,
@@ -402,13 +403,14 @@ async function uploadedFileIdsForCommanderOutput(runtime, body, structured) {
402
403
  return [];
403
404
  }
404
405
  const cwdRealPath = await realpath(runtime.cwd);
406
+ const uploadRoots = await commanderAttachmentUploadRoots(cwdRealPath);
405
407
  const paths = await extractKandanAttachmentPaths(body, cwdRealPath);
406
408
  if (paths.length === 0) {
407
409
  return [];
408
410
  }
409
411
  const files = await Promise.all(
410
412
  paths.map(async (path2) => {
411
- assertCommanderAttachmentUploadAllowed(cwdRealPath, path2);
413
+ assertCommanderAttachmentUploadAllowed(uploadRoots, path2);
412
414
  const info = await stat(path2);
413
415
  if (!info.isFile()) {
414
416
  throw new Error(`Kandan attachment path is not a file: ${path2}`);
@@ -556,13 +558,19 @@ async function extractKandanAttachmentPaths(body, cwdRealPath) {
556
558
  const paths = [];
557
559
  for (const line of lines.slice(headingIndex + 1)) {
558
560
  const trimmed = line.trim();
559
- if (!trimmed.startsWith("- ")) {
560
- if (trimmed !== "") {
561
- break;
562
- }
561
+ const rawPath = kandanAttachmentRawPath(trimmed);
562
+ if (rawPath === "") {
563
+ continue;
564
+ }
565
+ if (rawPath === void 0) {
566
+ break;
567
+ }
568
+ if (commanderAttachmentRawPathHasHiddenSegment(rawPath)) {
563
569
  continue;
564
570
  }
565
- const rawPath = trimmed.slice(2).trim().replace(/^`|`$/g, "");
571
+ if (!isCommanderAttachmentUploadPathCandidate(rawPath)) {
572
+ break;
573
+ }
566
574
  const resolvedPath = await resolveCommanderAttachmentPath(
567
575
  cwdRealPath,
568
576
  rawPath
@@ -575,27 +583,34 @@ async function extractKandanAttachmentPaths(body, cwdRealPath) {
575
583
  }
576
584
  return paths;
577
585
  }
586
+ function kandanAttachmentRawPath(trimmedLine) {
587
+ if (trimmedLine === "") {
588
+ return "";
589
+ }
590
+ const rawPath = trimmedLine.startsWith("- ") ? trimmedLine.slice(2).trim() : trimmedLine;
591
+ if (rawPath === "") {
592
+ return void 0;
593
+ }
594
+ return rawPath.replace(/^`|`$/g, "");
595
+ }
578
596
  async function resolveCommanderAttachmentPath(cwdRealPath, rawPath) {
579
597
  if (rawPath === "") {
580
598
  throw new Error("Kandan attachment path must not be empty");
581
599
  }
582
600
  const candidatePath = isAbsolute(rawPath) ? resolve(rawPath) : resolve(cwdRealPath, rawPath);
583
- const path2 = await realpath(candidatePath);
584
- const relativePath = relative(cwdRealPath, path2);
585
- if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
586
- throw new Error(
587
- `Kandan attachment path must resolve inside the runner cwd: ${rawPath}`
588
- );
589
- }
590
- return path2;
601
+ return await realpath(candidatePath);
591
602
  }
592
- function assertCommanderAttachmentUploadAllowed(cwdRealPath, filePath) {
593
- const relativePath = relative(cwdRealPath, filePath);
594
- if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
603
+ function assertCommanderAttachmentUploadAllowed(uploadRoots, filePath) {
604
+ const matchedRoot = uploadRoots.find(
605
+ (root) => pathIsInsideRoot(root.path, filePath)
606
+ );
607
+ if (matchedRoot === void 0) {
608
+ const rootLabels = uploadRoots.map((root) => root.label).join(", ");
595
609
  throw new Error(
596
- `Kandan attachment path must resolve inside the runner cwd: ${filePath}`
610
+ `Kandan attachment path must resolve inside an allowed upload root (${rootLabels}): ${filePath}`
597
611
  );
598
612
  }
613
+ const relativePath = relative(matchedRoot.path, filePath);
599
614
  const hiddenSegment = relativePath.split(/[\\/]+/).find((segment) => segment.startsWith("."));
600
615
  if (hiddenSegment !== void 0) {
601
616
  throw new Error(
@@ -609,6 +624,25 @@ function assertCommanderAttachmentUploadAllowed(cwdRealPath, filePath) {
609
624
  );
610
625
  }
611
626
  }
627
+ async function commanderAttachmentUploadRoots(cwdRealPath) {
628
+ const roots = [
629
+ { path: cwdRealPath, label: "runner cwd" }
630
+ ];
631
+ const downloadsPath = resolve(homedir(), "Downloads");
632
+ try {
633
+ const downloadsRealPath = await realpath(downloadsPath);
634
+ if (!roots.some((root) => root.path === downloadsRealPath)) {
635
+ roots.push({ path: downloadsRealPath, label: "~/Downloads" });
636
+ }
637
+ } catch (_error) {
638
+ return roots;
639
+ }
640
+ return roots;
641
+ }
642
+ function pathIsInsideRoot(rootPath, filePath) {
643
+ const relativePath = relative(rootPath, filePath);
644
+ return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
645
+ }
612
646
  function contentTypeForFileName(fileName) {
613
647
  const lower = fileName.toLowerCase();
614
648
  if (lower.endsWith(".png")) {
@@ -634,6 +668,69 @@ function contentTypeForFileName(fileName) {
634
668
  }
635
669
  return "application/octet-stream";
636
670
  }
671
+ function bodyWithoutKandanAttachmentFooter(body) {
672
+ const lines = body.split(/\r?\n/);
673
+ const headingIndex = lines.findIndex(
674
+ (line) => line.trim().toLowerCase() === "kandan attachments:"
675
+ );
676
+ if (headingIndex === -1) {
677
+ return body;
678
+ }
679
+ const visibleAfterLines = [];
680
+ let sawVisibleAfterLine = false;
681
+ for (const line of lines.slice(headingIndex + 1)) {
682
+ if (line.trim() === "") {
683
+ switch (sawVisibleAfterLine) {
684
+ case true:
685
+ visibleAfterLines.push(line);
686
+ break;
687
+ case false:
688
+ break;
689
+ }
690
+ continue;
691
+ }
692
+ if (isKandanAttachmentDisplayDirectiveLine(line)) {
693
+ continue;
694
+ }
695
+ visibleAfterLines.push(line);
696
+ sawVisibleAfterLine = true;
697
+ }
698
+ const visibleLines = [...lines.slice(0, headingIndex), ...visibleAfterLines];
699
+ while (visibleLines.length > 0 && visibleLines[visibleLines.length - 1]?.trim() === "") {
700
+ visibleLines.pop();
701
+ }
702
+ return visibleLines.join("\n");
703
+ }
704
+ function displayBodyForCommanderOutput(body, uploadedFileIds) {
705
+ const displayBody = bodyWithoutKandanAttachmentFooter(body);
706
+ switch (displayBody.trim() === "" && uploadedFileIds.length > 0) {
707
+ case true:
708
+ return "Attached file.";
709
+ case false:
710
+ return displayBody;
711
+ }
712
+ }
713
+ function structuredWithAssistantContent(structured, content) {
714
+ return stringValue(structured.kind) === "codex_assistant_message" ? { ...structured, content } : structured;
715
+ }
716
+ function isKandanAttachmentDisplayDirectiveLine(line) {
717
+ const rawPath = kandanAttachmentRawPath(line.trim());
718
+ if (rawPath === "") {
719
+ return true;
720
+ }
721
+ if (rawPath === void 0) {
722
+ return false;
723
+ }
724
+ return !commanderAttachmentRawPathHasHiddenSegment(rawPath) && commanderAttachmentUploadExtensions.has(extname(rawPath).toLowerCase());
725
+ }
726
+ function isCommanderAttachmentUploadPathCandidate(rawPath) {
727
+ return commanderAttachmentUploadExtensions.has(
728
+ extname(rawPath).toLowerCase()
729
+ );
730
+ }
731
+ function commanderAttachmentRawPathHasHiddenSegment(rawPath) {
732
+ return rawPath.split(/[\\/]+/).some((segment) => segment.startsWith("."));
733
+ }
637
734
  async function pushOk(kandan, topic, event, payload) {
638
735
  const reply = await kandan.push(topic, event, payload);
639
736
  if (isJsonObject(reply) && reply.status === "ok" && isJsonObject(reply.response)) {
@@ -754,6 +851,7 @@ var init_codexRuntimeOptions = __esm({
754
851
 
755
852
  // src/codexOutput.ts
756
853
  import { Buffer as Buffer2 } from "node:buffer";
854
+ import { fileURLToPath } from "node:url";
757
855
  function codexOutputMessagesForTurn(response, turnId) {
758
856
  if ("error" in response) {
759
857
  return [];
@@ -803,19 +901,10 @@ function codexOutputMessagesForItem(item, index) {
803
901
  }
804
902
  ];
805
903
  }
806
- case "agentMessage": {
807
- const text2 = assistantVisibleText(item);
808
- return [
809
- {
810
- itemKey,
811
- body: text2,
812
- structured: baseStructured("codex_assistant_message", {
813
- content: text2,
814
- phase: stringValue(item.phase) ?? "final_answer"
815
- })
816
- }
817
- ];
818
- }
904
+ case "agentMessage":
905
+ return codexAssistantMessageForItem(item, itemKey, baseStructured);
906
+ case "message":
907
+ return stringValue(item.role) === "assistant" ? codexAssistantMessageForItem(item, itemKey, baseStructured) : [];
819
908
  case "commandExecution": {
820
909
  const command = stringValue(item.command) ?? "command";
821
910
  const output = nonBlankStringValue(item.aggregatedOutput) ?? "";
@@ -1121,7 +1210,26 @@ function codexUserInputMessageForItem(item, index) {
1121
1210
  const body = textParts.length > 0 ? textParts.join("\n\n") : nonBlankStringValue(item.text) ?? nonBlankStringValue(item.message) ?? "";
1122
1211
  return [{ itemKey, body }];
1123
1212
  }
1213
+ function codexAssistantMessageForItem(item, itemKey, baseStructured) {
1214
+ const text2 = assistantVisibleText(item);
1215
+ const attachmentPaths = assistantImageAttachmentPaths(item);
1216
+ const body = assistantBodyWithAttachmentPaths(text2, attachmentPaths);
1217
+ return [
1218
+ {
1219
+ itemKey,
1220
+ body,
1221
+ structured: baseStructured("codex_assistant_message", {
1222
+ content: text2,
1223
+ phase: stringValue(item.phase) ?? "final_answer"
1224
+ })
1225
+ }
1226
+ ];
1227
+ }
1124
1228
  function assistantVisibleText(item) {
1229
+ const contentText = assistantContentText(item);
1230
+ if (contentText !== "") {
1231
+ return contentText;
1232
+ }
1125
1233
  const direct = stringValue(item.reply) ?? stringValue(item.summary) ?? stringValue(item.content) ?? stringValue(item.text) ?? "";
1126
1234
  const parsed = parseJsonObjectOrUndefined(direct);
1127
1235
  if (parsed === void 0) {
@@ -1129,6 +1237,52 @@ function assistantVisibleText(item) {
1129
1237
  }
1130
1238
  return stringValue(parsed.reply) ?? stringValue(parsed.summary) ?? stringValue(parsed.content) ?? stringValue(parsed.message) ?? stringValue(parsed.output_text) ?? direct;
1131
1239
  }
1240
+ function assistantContentText(item) {
1241
+ return (arrayValue(item.content) ?? []).filter(isJsonObject).flatMap((part) => {
1242
+ const type = stringValue(part.type);
1243
+ if (type !== "text" && type !== "output_text" && type !== "input_text") {
1244
+ return [];
1245
+ }
1246
+ const text2 = nonBlankStringValue(part.text) ?? nonBlankStringValue(part.content) ?? nonBlankStringValue(part.output_text);
1247
+ return text2 === void 0 ? [] : [text2];
1248
+ }).join("\n\n");
1249
+ }
1250
+ function assistantBodyWithAttachmentPaths(text2, attachmentPaths) {
1251
+ const uniqueAttachmentPaths = [...new Set(attachmentPaths)];
1252
+ if (uniqueAttachmentPaths.length === 0) {
1253
+ return text2;
1254
+ }
1255
+ const attachmentFooter = [
1256
+ "Kandan attachments:",
1257
+ ...uniqueAttachmentPaths.map((path2) => `- ${path2}`)
1258
+ ].join("\n");
1259
+ const normalizedText = text2.trim();
1260
+ return normalizedText === "" ? attachmentFooter : [text2, "", attachmentFooter].join("\n");
1261
+ }
1262
+ function assistantImageAttachmentPaths(item) {
1263
+ return (arrayValue(item.content) ?? []).filter(isJsonObject).flatMap((part) => {
1264
+ const type = stringValue(part.type)?.toLowerCase();
1265
+ if (type !== "image" && type !== "output_image" && type !== "input_image") {
1266
+ return [];
1267
+ }
1268
+ const rawPath = stringValue(part.path) ?? stringValue(part.file_path) ?? stringValue(part.filePath) ?? stringValue(part.image_url) ?? stringValue(part.imageUrl) ?? stringValue(part.url);
1269
+ const localPath = rawPath === void 0 ? void 0 : localAttachmentPath(rawPath);
1270
+ return localPath === void 0 ? [] : [localPath];
1271
+ });
1272
+ }
1273
+ function localAttachmentPath(rawPath) {
1274
+ if (/^https?:\/\//i.test(rawPath) || /^data:/i.test(rawPath)) {
1275
+ return void 0;
1276
+ }
1277
+ if (/^file:\/\//i.test(rawPath)) {
1278
+ try {
1279
+ return fileURLToPath(rawPath);
1280
+ } catch (_error) {
1281
+ return void 0;
1282
+ }
1283
+ }
1284
+ return rawPath;
1285
+ }
1132
1286
  function assistantDeltaText(params) {
1133
1287
  return textDeltaFromParams(params);
1134
1288
  }
@@ -3292,23 +3446,25 @@ async function attachChannelSession(args) {
3292
3446
  });
3293
3447
  }
3294
3448
  break;
3295
- case "turn/completed":
3296
- if (turnId !== void 0) {
3297
- enqueueWebSearchProgressCompletion(args, state, turnId);
3298
- enqueueFileChangeCompletion(args, state, turnId);
3449
+ case "turn/completed": {
3450
+ const completedTurnId = turnId ?? inferThreadOnlyCompletedTurnId(args, state, threadId);
3451
+ if (completedTurnId !== void 0) {
3452
+ enqueueWebSearchProgressCompletion(args, state, completedTurnId);
3453
+ enqueueFileChangeCompletion(args, state, completedTurnId);
3299
3454
  void forwardCompletedCodexTurn(
3300
3455
  args,
3301
3456
  state,
3302
- turnId,
3457
+ completedTurnId,
3303
3458
  payloadContext
3304
3459
  ).catch((error) => {
3305
3460
  args.log("codex.turn_forward_failed", {
3306
- turn_id: turnId,
3461
+ turn_id: completedTurnId,
3307
3462
  message: error instanceof Error ? error.message : String(error)
3308
3463
  });
3309
3464
  });
3310
3465
  }
3311
3466
  break;
3467
+ }
3312
3468
  case "item/agentMessage/delta":
3313
3469
  enqueueAssistantDelta(args, state, params, payloadContext);
3314
3470
  break;
@@ -3331,6 +3487,32 @@ async function attachChannelSession(args) {
3331
3487
  case "item/completed":
3332
3488
  enqueueWebSearchProgress(args, state, params, payloadContext);
3333
3489
  enqueueCompletedFileChange(args, state, params, payloadContext);
3490
+ const completedAssistantItemForward = forwardCompletedAssistantItem(
3491
+ args,
3492
+ state,
3493
+ params,
3494
+ payloadContext
3495
+ );
3496
+ if (turnId !== void 0) {
3497
+ rememberPendingCompletedAssistantItemForward(
3498
+ state,
3499
+ turnId,
3500
+ completedAssistantItemForward
3501
+ );
3502
+ }
3503
+ void completedAssistantItemForward.catch((error) => {
3504
+ args.log("codex.completed_assistant_item_forward_failed", {
3505
+ message: error instanceof Error ? error.message : String(error)
3506
+ });
3507
+ }).finally(() => {
3508
+ if (turnId !== void 0) {
3509
+ forgetPendingCompletedAssistantItemForward(
3510
+ state,
3511
+ turnId,
3512
+ completedAssistantItemForward
3513
+ );
3514
+ }
3515
+ });
3334
3516
  if (turnId !== void 0) {
3335
3517
  const promise = mirrorLocalTuiInputFromNotification(
3336
3518
  args,
@@ -3424,6 +3606,8 @@ function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThread
3424
3606
  forwardedTurnIds: /* @__PURE__ */ new Set(),
3425
3607
  forwardingTurnIds: /* @__PURE__ */ new Set(),
3426
3608
  retryableTurnIds: /* @__PURE__ */ new Set(),
3609
+ completedAssistantItemKeys: /* @__PURE__ */ new Set(),
3610
+ pendingCompletedAssistantItemForwards: /* @__PURE__ */ new Map(),
3427
3611
  claimedKandanMessageKeys: /* @__PURE__ */ new Set(),
3428
3612
  localTuiTurnIds: /* @__PURE__ */ new Set(),
3429
3613
  mirroredTuiInputProjections: createBoundedCache(maxForwardedTurnIds),
@@ -4695,7 +4879,16 @@ async function processKandanChatEvent(args, state, runnerIdentity, event, payloa
4695
4879
  }
4696
4880
  }
4697
4881
  async function drainKandanMessageQueue(args, state, payloadContext) {
4698
- if (state.closed || state.turn.status !== "idle" || localTuiTurnIsActive(state)) {
4882
+ if (state.closed) {
4883
+ logQueuedDrainBlocked(args, state, "session_closed");
4884
+ return;
4885
+ }
4886
+ if (state.turn.status !== "idle") {
4887
+ logQueuedDrainBlocked(args, state, "turn_not_idle");
4888
+ return;
4889
+ }
4890
+ if (localTuiTurnIsActive(state)) {
4891
+ logQueuedDrainBlocked(args, state, "local_tui_turn_active");
4699
4892
  return;
4700
4893
  }
4701
4894
  const resumed = await tryResumeCodexThreadForPendingRuntimeSettings(
@@ -4703,6 +4896,7 @@ async function drainKandanMessageQueue(args, state, payloadContext) {
4703
4896
  state
4704
4897
  );
4705
4898
  if (!resumed) {
4899
+ logQueuedDrainBlocked(args, state, "runtime_settings_resume_pending");
4706
4900
  return;
4707
4901
  }
4708
4902
  const next = dequeuePendingKandanMessage(state.queue);
@@ -4823,6 +5017,22 @@ async function drainKandanMessageQueue(args, state, payloadContext) {
4823
5017
  });
4824
5018
  }
4825
5019
  }
5020
+ function logQueuedDrainBlocked(args, state, reason) {
5021
+ const queueDepth = pendingKandanMessageQueueLength(state.queue);
5022
+ if (queueDepth === 0) {
5023
+ return;
5024
+ }
5025
+ args.log("kandan.queue_drain_blocked", {
5026
+ reason,
5027
+ queue_depth: queueDepth,
5028
+ turn_status: state.turn.status,
5029
+ active_turn_id: activeTurnId(state.turn) ?? null,
5030
+ active_queued_seq: state.turn.status === "active" || state.turn.status === "completing" ? state.turn.queuedSeq : state.turn.status === "starting" ? state.turn.queuedSeq : null,
5031
+ codex_thread_id: state.codexThreadId ?? null,
5032
+ kandan_thread_id: state.kandanThreadId ?? null,
5033
+ local_tui_turn_count: state.localTuiTurnIds.size
5034
+ });
5035
+ }
4826
5036
  async function fetchReconnectContextInjection(args, state) {
4827
5037
  if (state.kandanThreadId === void 0) {
4828
5038
  throw new Error(
@@ -4986,6 +5196,7 @@ async function forwardCompletedCodexTurn(args, state, turnId, payloadContext) {
4986
5196
  };
4987
5197
  }
4988
5198
  await waitForPendingTuiInputMirror(state, turnId);
5199
+ await waitForPendingCompletedAssistantItemForwards(state, turnId);
4989
5200
  await waitForStreamingForwardChains(args, state, payloadContext);
4990
5201
  rememberForwardingTurnId(state, turnId);
4991
5202
  forgetRetryableTurnId(state, turnId);
@@ -5096,9 +5307,17 @@ async function forwardCompletedCodexTurn(args, state, turnId, payloadContext) {
5096
5307
  }
5097
5308
  }
5098
5309
  function completedOutputProjectionsForTurn(state, turnId, messages) {
5099
- const snapshotOutputs = messages.flatMap(
5100
- (message, snapshotIndex) => completedSnapshotOutputProjection(state, turnId, message, snapshotIndex)
5101
- );
5310
+ const snapshotOutputs = messages.flatMap((message, snapshotIndex) => {
5311
+ if (completedAssistantItemForwarded(state, message.itemKey)) {
5312
+ return [];
5313
+ }
5314
+ return completedSnapshotOutputProjection(
5315
+ state,
5316
+ turnId,
5317
+ message,
5318
+ snapshotIndex
5319
+ );
5320
+ });
5102
5321
  const matchedStructuredKeys = new Set(
5103
5322
  snapshotOutputs.flatMap((output) => {
5104
5323
  switch (output.match.kind) {
@@ -5367,7 +5586,101 @@ function logCompletedCodexOutput(args, turnId, message) {
5367
5586
  file_paths: fileChangePaths(message.structured)
5368
5587
  });
5369
5588
  }
5589
+ async function forwardCompletedAssistantItem(args, state, params, payloadContext) {
5590
+ if (state.kandanThreadId === void 0 || state.codexThreadId === void 0) {
5591
+ return;
5592
+ }
5593
+ const turnId = stringValue(params.turnId) ?? stringValue(params.turn_id) ?? stringValue(objectValue(params.turn)?.id);
5594
+ const item = objectValue(params.item) ?? params;
5595
+ const completedItemKey = stringValue(params.itemId) ?? stringValue(params.item_id) ?? stringValue(objectValue(params.item)?.id);
5596
+ if (turnId !== void 0) {
5597
+ await waitForPendingTuiInputMirror(state, turnId);
5598
+ await waitForStreamingForwardChains(args, state, payloadContext);
5599
+ }
5600
+ const parsedMessage = codexOutputMessagesForItem(item, 0).find(
5601
+ (candidate) => stringValue(candidate.structured.kind) === "codex_assistant_message"
5602
+ );
5603
+ const fallbackStream = parsedMessage === void 0 ? resolveStreamingAssistantOutputForCompletedSignal(
5604
+ state,
5605
+ turnId,
5606
+ completedItemKey
5607
+ ) : { status: "none" };
5608
+ if (fallbackStream.status === "ambiguous") {
5609
+ throw new LogicalProjectionError(
5610
+ `Cannot reconcile completed assistant item signal from item notification; ${fallbackStream.candidateCount} active streamed assistant outputs exist`
5611
+ );
5612
+ }
5613
+ const message = parsedMessage ?? (fallbackStream.status === "matched" ? {
5614
+ itemKey: fallbackStream.output.itemKey,
5615
+ body: fallbackStream.output.content,
5616
+ structured: codexAssistantStructuredMessage(
5617
+ fallbackStream.output.itemKey,
5618
+ fallbackStream.output.content,
5619
+ "completed"
5620
+ )
5621
+ } : void 0);
5622
+ const messageTurnId = turnId ?? (fallbackStream.status === "matched" ? fallbackStream.output.turnId : void 0);
5623
+ if (message === void 0 || completedAssistantItemForwarded(state, message.itemKey)) {
5624
+ return;
5625
+ }
5626
+ const sourceMessageSeq = messageTurnId === void 0 ? void 0 : sourceMessageSeqForTurn(state, messageTurnId);
5627
+ if (messageTurnId !== void 0 && sourceMessageSeq === void 0) {
5628
+ args.log("codex.completed_assistant_item_without_source_message", {
5629
+ turn_id: messageTurnId,
5630
+ item_key: message.itemKey
5631
+ });
5632
+ return;
5633
+ }
5634
+ const structured = codexAssistantStructuredMessage(
5635
+ message.itemKey,
5636
+ message.body,
5637
+ "completed"
5638
+ );
5639
+ const streamed = resolveStreamingAssistantOutputForCompletedMessage(
5640
+ state,
5641
+ messageTurnId ?? `item:${message.itemKey}`,
5642
+ message.itemKey,
5643
+ message.body,
5644
+ structured
5645
+ );
5646
+ switch (streamed.status) {
5647
+ case "none":
5648
+ await streamCompletedCodexOutput(args, state, payloadContext, {
5649
+ turnId: turnId ?? `item:${message.itemKey}`,
5650
+ sourceMessageSeq,
5651
+ rootSeq: state.rootSeq,
5652
+ message: {
5653
+ ...message,
5654
+ structured
5655
+ }
5656
+ });
5657
+ break;
5658
+ case "matched":
5659
+ await editStreamedCodexOutput(
5660
+ args,
5661
+ state,
5662
+ streamed.output.seq,
5663
+ message.itemKey,
5664
+ message.body,
5665
+ "completed"
5666
+ );
5667
+ forgetStreamingAssistantOutput(state, streamed.output.itemKey);
5668
+ break;
5669
+ case "ambiguous":
5670
+ throw new LogicalProjectionError(
5671
+ `Cannot reconcile completed assistant item ${message.itemKey} from item notification; ${streamed.candidateCount} active streamed assistant outputs exist`
5672
+ );
5673
+ }
5674
+ rememberCompletedAssistantItemKey(state, message.itemKey);
5675
+ logCompletedCodexOutput(args, messageTurnId ?? `item:${message.itemKey}`, {
5676
+ ...message,
5677
+ structured
5678
+ });
5679
+ }
5370
5680
  function compareCompletedOutputProjection(left, right) {
5681
+ if (left.kind === "snapshot" && right.kind === "snapshot" && left.match.kind !== right.match.kind && (left.match.kind === "none" || right.match.kind === "none")) {
5682
+ return left.snapshotIndex - right.snapshotIndex;
5683
+ }
5371
5684
  return left.sortOrder === right.sortOrder ? left.snapshotIndex - right.snapshotIndex : left.sortOrder - right.sortOrder;
5372
5685
  }
5373
5686
  function fallbackSnapshotOutputBase(sortOrders) {
@@ -6240,12 +6553,20 @@ async function streamCompletedCodexOutput(args, state, payloadContext, params) {
6240
6553
  params.message.body,
6241
6554
  structured
6242
6555
  );
6556
+ const displayBody = displayBodyForCommanderOutput(
6557
+ params.message.body,
6558
+ uploadedFileIds
6559
+ );
6560
+ const displayStructured = structuredWithAssistantContent(
6561
+ structured,
6562
+ displayBody
6563
+ );
6243
6564
  await pushOk2(args.kandan, args.topic, "session:stream_thread_message", {
6244
6565
  workspace: session.workspaceSlug,
6245
6566
  channel: session.channelSlug,
6246
6567
  thread_id: state.kandanThreadId,
6247
6568
  stream_key: streamKey,
6248
- body: params.message.body,
6569
+ body: displayBody,
6249
6570
  payload: {
6250
6571
  ...localRunnerPayload(
6251
6572
  args.options,
@@ -6259,7 +6580,7 @@ async function streamCompletedCodexOutput(args, state, payloadContext, params) {
6259
6580
  ...params.rootSeq === void 0 ? {} : { reply_to_seq: params.rootSeq },
6260
6581
  ...params.sourceMessageSeq === void 0 ? {} : { source_message_seq: params.sourceMessageSeq },
6261
6582
  ...streamMetadata,
6262
- structured
6583
+ structured: displayStructured
6263
6584
  },
6264
6585
  ...uploadedFileIds.length === 0 ? {} : { uploaded_file_ids: uploadedFileIds },
6265
6586
  client_message_id: streamKey
@@ -6282,13 +6603,36 @@ async function editCodexStructuredOutput(args, state, targetSeq, content, struct
6282
6603
  return;
6283
6604
  }
6284
6605
  const session = args.options.channelSession;
6606
+ const uploadedFileIds = stringValue(structured.stream_state) === "completed" ? await uploadedFileIdsForCommanderOutput(
6607
+ {
6608
+ kandan: args.kandan,
6609
+ topic: args.topic,
6610
+ workspace: session.workspaceSlug,
6611
+ channel: session.channelSlug,
6612
+ cwd: args.options.cwd,
6613
+ kandanUrl: args.options.kandanUrl,
6614
+ token: args.options.token,
6615
+ fetch: args.options.fetch
6616
+ },
6617
+ content,
6618
+ structured
6619
+ ) : [];
6620
+ const displayContent = displayBodyForCommanderOutput(
6621
+ content,
6622
+ uploadedFileIds
6623
+ );
6624
+ const displayStructured = structuredWithAssistantContent(
6625
+ structured,
6626
+ displayContent
6627
+ );
6285
6628
  await pushOk2(args.kandan, args.topic, "session:edit_thread_message", {
6286
6629
  workspace: session.workspaceSlug,
6287
6630
  channel: session.channelSlug,
6288
6631
  thread_id: state.kandanThreadId,
6289
6632
  target_seq: targetSeq,
6290
- body: content,
6291
- structured
6633
+ body: displayContent,
6634
+ structured: displayStructured,
6635
+ ...uploadedFileIds.length === 0 ? {} : { uploaded_file_ids: uploadedFileIds }
6292
6636
  });
6293
6637
  }
6294
6638
  async function mirrorLocalTuiInputFromNotification(args, state, turnId, params, payloadContext) {
@@ -6464,6 +6808,30 @@ function resolveStreamingAssistantOutputForCompletedMessage(state, turnId, itemK
6464
6808
  }
6465
6809
  }
6466
6810
  }
6811
+ function resolveStreamingAssistantOutputForCompletedSignal(state, turnId, itemKey) {
6812
+ if (itemKey !== void 0) {
6813
+ const exact = findStreamingAssistantOutput(state, itemKey);
6814
+ if (exact !== void 0) {
6815
+ return { status: "matched", output: exact };
6816
+ }
6817
+ }
6818
+ if (turnId === void 0) {
6819
+ return { status: "none" };
6820
+ }
6821
+ const candidates = boundedCacheValues(state.streamingAssistantOutputs).filter(
6822
+ (output) => output.turnId === turnId
6823
+ );
6824
+ switch (candidates.length) {
6825
+ case 0:
6826
+ return { status: "none" };
6827
+ case 1: {
6828
+ const [output] = candidates;
6829
+ return output === void 0 ? { status: "none" } : { status: "matched", output };
6830
+ }
6831
+ default:
6832
+ return { status: "ambiguous", candidateCount: candidates.length };
6833
+ }
6834
+ }
6467
6835
  function normalizedTranscriptText(value) {
6468
6836
  return value.trim().replace(/\s+/g, " ");
6469
6837
  }
@@ -6480,6 +6848,16 @@ function rememberStreamingAssistantOutput(state, output) {
6480
6848
  function forgetStreamingAssistantOutput(state, itemKey) {
6481
6849
  forgetBoundedCacheValue(state.streamingAssistantOutputs, itemKey);
6482
6850
  }
6851
+ function completedAssistantItemForwarded(state, itemKey) {
6852
+ return state.completedAssistantItemKeys.has(itemKey);
6853
+ }
6854
+ function rememberCompletedAssistantItemKey(state, itemKey) {
6855
+ rememberBoundedStringSet(
6856
+ state.completedAssistantItemKeys,
6857
+ itemKey,
6858
+ maxForwardedTurnIds
6859
+ );
6860
+ }
6483
6861
  function rememberStreamingReasoningOutput(state, output) {
6484
6862
  rememberBoundedCacheValue(
6485
6863
  state.streamingReasoningOutputs,
@@ -6559,6 +6937,30 @@ function codexNotificationBelongsToSession(state, threadId, turnId) {
6559
6937
  }
6560
6938
  return activeTurnId(state.turn) === turnId || isLocalTuiTurn(state, turnId);
6561
6939
  }
6940
+ function inferThreadOnlyCompletedTurnId(args, state, threadId) {
6941
+ if (threadId === void 0 || state.codexThreadId !== threadId) {
6942
+ return void 0;
6943
+ }
6944
+ switch (state.turn.status) {
6945
+ case "active":
6946
+ case "completing":
6947
+ args.log("codex.turn_completion_inferred", {
6948
+ thread_id: threadId,
6949
+ turn_id: state.turn.turnId,
6950
+ queued_seq: state.turn.queuedSeq,
6951
+ turn_status: state.turn.status
6952
+ });
6953
+ return state.turn.turnId;
6954
+ case "idle":
6955
+ case "starting":
6956
+ args.log("codex.turn_completion_without_turn_id", {
6957
+ thread_id: threadId,
6958
+ turn_status: state.turn.status,
6959
+ queued_seq: state.turn.status === "starting" ? state.turn.queuedSeq : null
6960
+ });
6961
+ return void 0;
6962
+ }
6963
+ }
6562
6964
  function codexNotificationActiveTurnFallback(method, state, params) {
6563
6965
  if (method !== "response_item" || codexCompletedFileChangeFromNotification(params) === void 0) {
6564
6966
  return void 0;
@@ -6607,6 +7009,32 @@ async function waitForPendingTuiInputMirror(state, turnId) {
6607
7009
  await pending;
6608
7010
  }
6609
7011
  }
7012
+ function rememberPendingCompletedAssistantItemForward(state, turnId, promise) {
7013
+ const existing = state.pendingCompletedAssistantItemForwards.get(turnId);
7014
+ const pending = existing ?? /* @__PURE__ */ new Set();
7015
+ pending.add(promise);
7016
+ state.pendingCompletedAssistantItemForwards.set(turnId, pending);
7017
+ trimBoundedMap(
7018
+ state.pendingCompletedAssistantItemForwards,
7019
+ maxForwardedTurnIds
7020
+ );
7021
+ }
7022
+ function forgetPendingCompletedAssistantItemForward(state, turnId, promise) {
7023
+ const pending = state.pendingCompletedAssistantItemForwards.get(turnId);
7024
+ if (pending === void 0) {
7025
+ return;
7026
+ }
7027
+ pending.delete(promise);
7028
+ if (pending.size === 0) {
7029
+ state.pendingCompletedAssistantItemForwards.delete(turnId);
7030
+ }
7031
+ }
7032
+ async function waitForPendingCompletedAssistantItemForwards(state, turnId) {
7033
+ const pending = state.pendingCompletedAssistantItemForwards.get(turnId);
7034
+ if (pending !== void 0) {
7035
+ await Promise.allSettled([...pending]);
7036
+ }
7037
+ }
6610
7038
  async function waitForStreamingForwardChains(args, state, payloadContext) {
6611
7039
  flushPendingStreamingDeltas(args, state, payloadContext);
6612
7040
  await Promise.all([
@@ -7120,7 +7548,7 @@ var init_channelSession = __esm({
7120
7548
 
7121
7549
  // src/claudeCodeSession.ts
7122
7550
  import { existsSync, readFileSync } from "node:fs";
7123
- import { homedir } from "node:os";
7551
+ import { homedir as homedir2 } from "node:os";
7124
7552
  import { join as join2 } from "node:path";
7125
7553
  function claudeCodeSettingSources() {
7126
7554
  return ["user", "project", "local"];
@@ -7128,7 +7556,7 @@ function claudeCodeSettingSources() {
7128
7556
  async function probeClaudeCodeAvailability(args) {
7129
7557
  if (!hasClaudeCodeAuthHint(process.env, {
7130
7558
  cwd: args.cwd,
7131
- homeDir: homedir(),
7559
+ homeDir: homedir2(),
7132
7560
  platform: process.platform,
7133
7561
  fileExists: existsSync,
7134
7562
  readTextFile: readTextFileIfPresent
@@ -7793,7 +8221,7 @@ var init_claudeCodeSession = __esm({
7793
8221
  // src/runnerLogger.ts
7794
8222
  import { appendFileSync, openSync } from "node:fs";
7795
8223
  import { createWriteStream } from "node:fs";
7796
- import { homedir as homedir2 } from "node:os";
8224
+ import { homedir as homedir3 } from "node:os";
7797
8225
  import { dirname, join as join3 } from "node:path";
7798
8226
  import { mkdirSync } from "node:fs";
7799
8227
  function createRunnerLogger(logFile, consoleReporter) {
@@ -7835,10 +8263,10 @@ function writeCliAuditEvent(event, payload, options = {}) {
7835
8263
  }
7836
8264
  function defaultCliAuditLogFile() {
7837
8265
  const override = process.env.LINZUMI_CLI_AUDIT_LOG?.trim();
7838
- return override === void 0 || override === "" ? join3(homedir2(), ".linzumi", "logs", "command-events.jsonl") : override;
8266
+ return override === void 0 || override === "" ? join3(homedir3(), ".linzumi", "logs", "command-events.jsonl") : override;
7839
8267
  }
7840
8268
  function defaultRunnerLogFile() {
7841
- return join3(homedir2(), ".linzumi", "logs", "runner-events.jsonl");
8269
+ return join3(homedir3(), ".linzumi", "logs", "runner-events.jsonl");
7842
8270
  }
7843
8271
  function redactForCliLog(value) {
7844
8272
  return redactObject(value);
@@ -8498,11 +8926,11 @@ import {
8498
8926
  realpathSync,
8499
8927
  writeFileSync
8500
8928
  } from "node:fs";
8501
- import { homedir as homedir3 } from "node:os";
8929
+ import { homedir as homedir4 } from "node:os";
8502
8930
  import { join as join4, resolve as resolve2 } from "node:path";
8503
8931
  function ensureCodexProjectTrusted(projectPath, options = {}) {
8504
8932
  const trustedPath = realpathSync(resolve2(projectPath));
8505
- const configHome = options.configHome ?? process.env.CODEX_HOME ?? join4(homedir3(), ".codex");
8933
+ const configHome = options.configHome ?? process.env.CODEX_HOME ?? join4(homedir4(), ".codex");
8506
8934
  const configPath = join4(configHome, "config.toml");
8507
8935
  const currentConfig = existsSync2(configPath) ? readFileSync2(configPath, "utf8") : "";
8508
8936
  const nextConfig = codexConfigWithTrustedProject(currentConfig, trustedPath);
@@ -8557,7 +8985,7 @@ var init_codexProjectTrust = __esm({
8557
8985
 
8558
8986
  // src/localCapabilities.ts
8559
8987
  import { realpathSync as realpathSync2 } from "node:fs";
8560
- import { homedir as homedir4 } from "node:os";
8988
+ import { homedir as homedir5 } from "node:os";
8561
8989
  import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3 } from "node:path";
8562
8990
  function parseAllowedCwdList(value) {
8563
8991
  if (value === void 0) {
@@ -8597,10 +9025,10 @@ function assertConfiguredAllowedCwds(paths) {
8597
9025
  }
8598
9026
  function expandUserPath(pathValue) {
8599
9027
  if (pathValue === "~") {
8600
- return homedir4();
9028
+ return homedir5();
8601
9029
  }
8602
9030
  if (pathValue.startsWith("~/")) {
8603
- return resolve3(homedir4(), pathValue.slice(2));
9031
+ return resolve3(homedir5(), pathValue.slice(2));
8604
9032
  }
8605
9033
  return pathValue;
8606
9034
  }
@@ -8986,11 +9414,11 @@ import {
8986
9414
  unlinkSync,
8987
9415
  writeFileSync as writeFileSync2
8988
9416
  } from "node:fs";
8989
- import { homedir as homedir5 } from "node:os";
9417
+ import { homedir as homedir6 } from "node:os";
8990
9418
  import { basename as basename3, dirname as dirname2, join as join5, resolve as resolve4 } from "node:path";
8991
9419
  function localConfigPath(env = process.env) {
8992
9420
  const override = env.LINZUMI_CONFIG_FILE;
8993
- return override !== void 0 && override.trim() !== "" ? resolve4(expandUserPath(override)) : resolve4(homedir5(), ".linzumi", "config.json");
9421
+ return override !== void 0 && override.trim() !== "" ? resolve4(expandUserPath(override)) : resolve4(homedir6(), ".linzumi", "config.json");
8994
9422
  }
8995
9423
  function localConfigScopeKey(linzumiUrl) {
8996
9424
  const normalizedUrl = kandanHttpBaseUrl(linzumiUrl);
@@ -10686,7 +11114,7 @@ import {
10686
11114
  rmSync,
10687
11115
  writeFileSync as writeFileSync4
10688
11116
  } from "node:fs";
10689
- import { homedir as homedir6 } from "node:os";
11117
+ import { homedir as homedir7 } from "node:os";
10690
11118
  import { dirname as dirname4, join as join7, resolve as resolve5 } from "node:path";
10691
11119
  import { Readable } from "node:stream";
10692
11120
  import { pipeline } from "node:stream/promises";
@@ -11172,7 +11600,7 @@ function fileSha256Sync(path2) {
11172
11600
  return createHash2("sha256").update(readFileSync5(path2)).digest("hex");
11173
11601
  }
11174
11602
  function defaultEditorRuntimeCacheRoot() {
11175
- return join7(homedir6(), ".linzumi", "editor-runtimes");
11603
+ return join7(homedir7(), ".linzumi", "editor-runtimes");
11176
11604
  }
11177
11605
  function nonEmptyString(value) {
11178
11606
  return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
@@ -11695,7 +12123,7 @@ var linzumiCliVersion, linzumiCliVersionText;
11695
12123
  var init_version = __esm({
11696
12124
  "src/version.ts"() {
11697
12125
  "use strict";
11698
- linzumiCliVersion = "0.0.67-beta";
12126
+ linzumiCliVersion = "0.0.69-beta";
11699
12127
  linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
11700
12128
  }
11701
12129
  });
@@ -12124,10 +12552,10 @@ var init_runnerConsoleReporter = __esm({
12124
12552
 
12125
12553
  // src/authCache.ts
12126
12554
  import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
12127
- import { homedir as homedir7 } from "node:os";
12555
+ import { homedir as homedir8 } from "node:os";
12128
12556
  import { dirname as dirname6, join as join9 } from "node:path";
12129
12557
  function defaultAuthFilePath() {
12130
- const base = process.env.KANDAN_HOME ?? join9(homedir7(), ".kandan");
12558
+ const base = process.env.KANDAN_HOME ?? join9(homedir8(), ".kandan");
12131
12559
  return join9(base, "auth.json");
12132
12560
  }
12133
12561
  function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePath()) {
@@ -12830,7 +13258,7 @@ import {
12830
13258
  statSync
12831
13259
  } from "node:fs";
12832
13260
  import { createServer as createServer3 } from "node:http";
12833
- import { homedir as homedir8, hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
13261
+ import { homedir as homedir9, hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
12834
13262
  import { dirname as dirname7, join as join11, resolve as resolve6 } from "node:path";
12835
13263
  async function runLocalCodexRunner(options) {
12836
13264
  const log = makeRunnerLogger(options);
@@ -16501,16 +16929,24 @@ async function streamClaudeCodeStructuredMessage(args, sessionId, message) {
16501
16929
  message.body,
16502
16930
  message.structured
16503
16931
  ) : [];
16932
+ const displayBody = displayBodyForCommanderOutput(
16933
+ message.body,
16934
+ uploadedFileIds
16935
+ );
16936
+ const displayStructured = structuredWithAssistantContent(
16937
+ message.structured,
16938
+ displayBody
16939
+ );
16504
16940
  await args.kandan.push(args.topic, "session:stream_thread_message", {
16505
16941
  workspace: args.workspace,
16506
16942
  channel: args.channel,
16507
16943
  thread_id: args.threadId,
16508
16944
  stream_key: streamKey,
16509
- body: message.body,
16945
+ body: displayBody,
16510
16946
  payload: claudeCodePayload(
16511
16947
  { ...args, sourceSeq: args.sourceSeq() },
16512
16948
  sessionId,
16513
- message.structured
16949
+ displayStructured
16514
16950
  ),
16515
16951
  ...uploadedFileIds.length === 0 ? {} : { uploaded_file_ids: uploadedFileIds },
16516
16952
  client_message_id: streamKey
@@ -16535,15 +16971,23 @@ async function postClaudeCodeStructuredMessage(args, sessionId, message) {
16535
16971
  message.body,
16536
16972
  message.structured
16537
16973
  );
16974
+ const displayBody = displayBodyForCommanderOutput(
16975
+ message.body,
16976
+ uploadedFileIds
16977
+ );
16978
+ const displayStructured = structuredWithAssistantContent(
16979
+ message.structured,
16980
+ displayBody
16981
+ );
16538
16982
  await args.kandan.push(args.topic, "session:post_thread_message", {
16539
16983
  workspace: args.workspace,
16540
16984
  channel: args.channel,
16541
16985
  thread_id: args.threadId,
16542
- body: message.body,
16986
+ body: displayBody,
16543
16987
  payload: claudeCodePayload(
16544
16988
  { ...args, sourceSeq: args.sourceSeq() },
16545
16989
  sessionId,
16546
- message.structured
16990
+ displayStructured
16547
16991
  ),
16548
16992
  ...uploadedFileIds.length === 0 ? {} : { uploaded_file_ids: uploadedFileIds },
16549
16993
  client_message_id: claudeCodeMessageClientId(
@@ -17468,7 +17912,7 @@ function isGitProjectDirectory(cwd) {
17468
17912
  }
17469
17913
  function browseRunnerDirectory(control, options) {
17470
17914
  const requestId = stringValue(control.requestId) ?? null;
17471
- const requestedPath = stringValue(control.path) ?? homedir8();
17915
+ const requestedPath = stringValue(control.path) ?? homedir9();
17472
17916
  try {
17473
17917
  const currentPath = realpathSync5(resolve6(expandUserPath(requestedPath)));
17474
17918
  const stats = statSync(currentPath);
@@ -17499,7 +17943,7 @@ function browseRunnerDirectory(control, options) {
17499
17943
  ok: true,
17500
17944
  currentPath,
17501
17945
  parentPath: parent === currentPath ? null : parent,
17502
- homePath: homedir8(),
17946
+ homePath: homedir9(),
17503
17947
  runnerCwd: resolve6(options.cwd),
17504
17948
  entries,
17505
17949
  isGit: isGitProjectDirectory(currentPath)
@@ -17534,7 +17978,7 @@ function createRunnerProject(control, options, allowedCwds) {
17534
17978
  error: "invalid_project_name"
17535
17979
  };
17536
17980
  }
17537
- const projectsRoot = join11(homedir8(), "linzumi");
17981
+ const projectsRoot = join11(homedir9(), "linzumi");
17538
17982
  const projectPath = join11(projectsRoot, projectDirName);
17539
17983
  let createdProjectPath = false;
17540
17984
  try {
@@ -17751,7 +18195,7 @@ var init_kandanTls = __esm({
17751
18195
  // src/helloLinzumiProject.ts
17752
18196
  import { existsSync as existsSync10, mkdirSync as mkdirSync10, readFileSync as readFileSync11, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
17753
18197
  import { dirname as dirname9, join as join13, resolve as resolve7 } from "node:path";
17754
- import { fileURLToPath } from "node:url";
18198
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
17755
18199
  function createHelloLinzumiProject(input = {}) {
17756
18200
  const options = typeof input === "string" ? { rootPath: input } : input;
17757
18201
  const root = resolveHelloProjectRoot(options);
@@ -17840,7 +18284,7 @@ var init_helloLinzumiProject = __esm({
17840
18284
  defaultHelloLinzumiPort = 8787;
17841
18285
  defaultHelloLinzumiHost = "0.0.0.0";
17842
18286
  markerFile = ".linzumi-demo-project";
17843
- moduleDir = dirname9(fileURLToPath(import.meta.url));
18287
+ moduleDir = dirname9(fileURLToPath2(import.meta.url));
17844
18288
  linzumiLogoSvg = readFileSync11(join13(moduleDir, "assets", "linzumi-logo.svg"), "utf8");
17845
18289
  packageJson = `${JSON.stringify(
17846
18290
  {
@@ -38571,7 +39015,7 @@ import {
38571
39015
  writeFileSync as writeFileSync11
38572
39016
  } from "node:fs";
38573
39017
  import { access } from "node:fs/promises";
38574
- import { homedir as homedir11, tmpdir as tmpdir4 } from "node:os";
39018
+ import { homedir as homedir12, tmpdir as tmpdir4 } from "node:os";
38575
39019
  import { delimiter as delimiter2, dirname as dirname11, join as join15, resolve as resolve9 } from "node:path";
38576
39020
  import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
38577
39021
  import { emitKeypressEvents } from "node:readline";
@@ -40874,7 +41318,7 @@ async function runSignupPreflights(runtime) {
40874
41318
  function defaultPreflightRuntime() {
40875
41319
  return {
40876
41320
  cwd: process.cwd(),
40877
- homeDir: homedir11(),
41321
+ homeDir: homedir12(),
40878
41322
  probeTool,
40879
41323
  readGitEmail,
40880
41324
  discoverCodeRoots,
@@ -41083,7 +41527,7 @@ function codexTaskSuggestionProcess2(args) {
41083
41527
  function signupCodexTaskSuggestionProcessForTest(args) {
41084
41528
  return codexTaskSuggestionProcess2(args);
41085
41529
  }
41086
- async function resolveSignupCodexCommand(env = process.env, homeDir = homedir11(), executableExists = fileIsExecutable) {
41530
+ async function resolveSignupCodexCommand(env = process.env, homeDir = homedir12(), executableExists = fileIsExecutable) {
41087
41531
  const override = firstConfiguredValue([
41088
41532
  env.LINZUMI_SIGNUP_CODEX_BIN,
41089
41533
  env.LINZUMI_CODEX_BIN
@@ -41722,10 +42166,10 @@ function directoryExists(path2) {
41722
42166
  }
41723
42167
  function expandHomePath(path2) {
41724
42168
  if (path2 === "~") {
41725
- return homedir11();
42169
+ return homedir12();
41726
42170
  }
41727
42171
  if (path2.startsWith("~/")) {
41728
- return join15(homedir11(), path2.slice(2));
42172
+ return join15(homedir12(), path2.slice(2));
41729
42173
  }
41730
42174
  return resolve9(path2);
41731
42175
  }
@@ -41813,9 +42257,9 @@ init_runner();
41813
42257
  init_claudeCodeSession();
41814
42258
  init_authCache();
41815
42259
  import { existsSync as existsSync13, readFileSync as readFileSync15, realpathSync as realpathSync6 } from "node:fs";
41816
- import { homedir as homedir12 } from "node:os";
42260
+ import { homedir as homedir13 } from "node:os";
41817
42261
  import { resolve as resolve10 } from "node:path";
41818
- import { fileURLToPath as fileURLToPath3 } from "node:url";
42262
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
41819
42263
 
41820
42264
  // src/authResolution.ts
41821
42265
  init_authCache();
@@ -41876,7 +42320,7 @@ init_json();
41876
42320
  init_defaultUrls();
41877
42321
  import { existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
41878
42322
  import { dirname as dirname8, join as join12 } from "node:path";
41879
- import { homedir as homedir9 } from "node:os";
42323
+ import { homedir as homedir10 } from "node:os";
41880
42324
  async function runAgentCliCommand(args, deps = {
41881
42325
  fetchImpl: fetch,
41882
42326
  stdout: process.stdout,
@@ -42584,7 +43028,7 @@ function agentTokenFile(flags) {
42584
43028
  return flags.get("agent-token-file") ?? defaultAgentTokenFilePath();
42585
43029
  }
42586
43030
  function defaultAgentTokenFilePath() {
42587
- return join12(homedir9(), ".linzumi", "agent-token.json");
43031
+ return join12(homedir10(), ".linzumi", "agent-token.json");
42588
43032
  }
42589
43033
  function normalizedApiUrl(apiUrl) {
42590
43034
  return apiUrl.endsWith("/") ? apiUrl : `${apiUrl}/`;
@@ -42692,13 +43136,13 @@ import {
42692
43136
  watch,
42693
43137
  writeFileSync as writeFileSync9
42694
43138
  } from "node:fs";
42695
- import { homedir as homedir10 } from "node:os";
43139
+ import { homedir as homedir11 } from "node:os";
42696
43140
  import { dirname as dirname10, join as join14, resolve as resolve8 } from "node:path";
42697
43141
  import { execFileSync, spawn as spawn8 } from "node:child_process";
42698
- import { fileURLToPath as fileURLToPath2 } from "node:url";
43142
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
42699
43143
  var connectedMarkers = ["Connected to Linzumi", "Runner connected:"];
42700
43144
  function commanderStatusDir() {
42701
- return join14(homedir10(), ".linzumi", "commanders");
43145
+ return join14(homedir11(), ".linzumi", "commanders");
42702
43146
  }
42703
43147
  function commanderStatusFile(runnerId, statusDir = commanderStatusDir()) {
42704
43148
  return join14(statusDir, `${safeRunnerId(runnerId)}.json`);
@@ -42804,7 +43248,7 @@ async function waitForCommanderDaemon(options) {
42804
43248
  if (commanderLogIsConnected(log)) {
42805
43249
  return { ok: true, record: status.record };
42806
43250
  }
42807
- await waitForFileChangeOrTimeout(
43251
+ const readyAfterWait = await waitForFileChangeOrTimeout(
42808
43252
  status.record.logFile,
42809
43253
  deadline,
42810
43254
  now,
@@ -42813,6 +43257,24 @@ async function waitForCommanderDaemon(options) {
42813
43257
  return updatedLog !== void 0 && commanderLogIsConnected(updatedLog);
42814
43258
  }
42815
43259
  );
43260
+ if (readyAfterWait) {
43261
+ const refreshedStatus = statusImpl(
43262
+ options.runnerId,
43263
+ options.statusDir
43264
+ );
43265
+ switch (refreshedStatus.status) {
43266
+ case "missing":
43267
+ return { ok: false, reason: "missing" };
43268
+ case "stopped":
43269
+ return { ok: false, reason: "stopped" };
43270
+ case "running": {
43271
+ const refreshedLog = readTextFile(refreshedStatus.record.logFile);
43272
+ if (refreshedLog !== void 0 && commanderLogIsConnected(refreshedLog)) {
43273
+ return { ok: true, record: refreshedStatus.record };
43274
+ }
43275
+ }
43276
+ }
43277
+ }
42816
43278
  }
42817
43279
  }
42818
43280
  }
@@ -42830,7 +43292,7 @@ function currentEntrypoint() {
42830
43292
  if (scriptPath !== void 0) {
42831
43293
  return scriptPath;
42832
43294
  }
42833
- return fileURLToPath2(import.meta.url);
43295
+ return fileURLToPath3(import.meta.url);
42834
43296
  }
42835
43297
  function parseRecord(content) {
42836
43298
  const parsed = JSON.parse(content);
@@ -42936,31 +43398,31 @@ function safeRunnerId(runnerId) {
42936
43398
  }
42937
43399
  async function waitForFileChangeOrTimeout(path2, deadline, now, ready2 = () => false) {
42938
43400
  const remaining = Math.max(0, deadline - now());
42939
- await new Promise((resolve11) => {
43401
+ return new Promise((resolve11) => {
42940
43402
  let resolved = false;
42941
43403
  let watcher;
42942
- const finish = () => {
43404
+ const finish = (connected) => {
42943
43405
  if (resolved) {
42944
43406
  return;
42945
43407
  }
42946
43408
  resolved = true;
42947
43409
  watcher?.close();
42948
43410
  clearTimeout(timer);
42949
- resolve11();
43411
+ resolve11(connected);
42950
43412
  };
42951
- const timer = setTimeout(finish, remaining);
43413
+ const timer = setTimeout(() => finish(false), remaining);
42952
43414
  try {
42953
- watcher = watch(path2, finish);
42954
- watcher.on("error", finish);
43415
+ watcher = watch(path2, () => finish(ready2()));
43416
+ watcher.on("error", () => finish(ready2()));
42955
43417
  if (ready2()) {
42956
- finish();
43418
+ finish(true);
42957
43419
  }
42958
43420
  } catch (_error) {
42959
43421
  if (ready2()) {
42960
- finish();
43422
+ finish(true);
42961
43423
  return;
42962
43424
  }
42963
- finish();
43425
+ finish(false);
42964
43426
  }
42965
43427
  });
42966
43428
  }
@@ -55142,7 +55604,7 @@ function isMainModule() {
55142
55604
  if (scriptPath === void 0) {
55143
55605
  return false;
55144
55606
  }
55145
- return fileURLToPath3(import.meta.url) === resolve10(scriptPath);
55607
+ return fileURLToPath4(import.meta.url) === resolve10(scriptPath);
55146
55608
  }
55147
55609
  async function main(args) {
55148
55610
  const parsed = parseCommand(args);
@@ -56033,10 +56495,10 @@ function rejectStartTargetingFlags(values) {
56033
56495
  }
56034
56496
  function resolveUserPath(pathValue) {
56035
56497
  if (pathValue === "~") {
56036
- return homedir12();
56498
+ return homedir13();
56037
56499
  }
56038
56500
  if (pathValue.startsWith("~/")) {
56039
- return resolve10(homedir12(), pathValue.slice(2));
56501
+ return resolve10(homedir13(), pathValue.slice(2));
56040
56502
  }
56041
56503
  return resolve10(pathValue);
56042
56504
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.67-beta",
3
+ "version": "0.0.69-beta",
4
4
  "description": "Linzumi CLI — 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": {