@linzumi/cli 0.0.72-beta → 0.0.74-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/dist/index.js CHANGED
@@ -140,6 +140,7 @@ function parseKandanChatEvent(value) {
140
140
  actorSlug: stringValue(actor?.slug),
141
141
  actorUserId: integerValue(value.actor_user_id),
142
142
  threadId: stringValue(payload.thread_id),
143
+ threadTitle: kandanThreadTitle(value, payload),
143
144
  replyToSeq: integerValue(payload.reply_to_seq),
144
145
  localRunnerEventType: localRunnerEventType(payload),
145
146
  body: stringValue(value.body) ?? stringValue(value.text) ?? stringValue(payload.body) ?? stringValue(payload.text) ?? "",
@@ -148,6 +149,11 @@ function parseKandanChatEvent(value) {
148
149
  )
149
150
  };
150
151
  }
152
+ function kandanThreadTitle(value, payload) {
153
+ const payloadThread = objectValue(payload.thread);
154
+ const eventThread = objectValue(value.thread);
155
+ return stringValue(payload.thread_title) ?? stringValue(payload.threadTitle) ?? stringValue(payload.title) ?? stringValue(payloadThread?.thread_title) ?? stringValue(payloadThread?.threadTitle) ?? stringValue(payloadThread?.title) ?? stringValue(value.thread_title) ?? stringValue(value.threadTitle) ?? stringValue(value.title) ?? stringValue(eventThread?.thread_title) ?? stringValue(eventThread?.threadTitle) ?? stringValue(eventThread?.title);
156
+ }
151
157
  function parseKandanChatAttachments(attachments) {
152
158
  return attachments.flatMap((attachment) => {
153
159
  const value = objectValue(attachment);
@@ -797,6 +803,130 @@ var init_commanderAttachments = __esm({
797
803
  }
798
804
  });
799
805
 
806
+ // src/codexNotificationDiagnostics.ts
807
+ function codexNotificationDiagnosticDetails(method, params) {
808
+ switch (method) {
809
+ case "warning":
810
+ case "error":
811
+ return {
812
+ diagnostic_summary: codexNotificationDiagnosticSummary(method, params) ?? `${method}: seen (no details)`,
813
+ diagnostic_payload: redactedJsonValue(params, 0),
814
+ diagnostic_payload_keys: Object.keys(params).sort()
815
+ };
816
+ default:
817
+ return {};
818
+ }
819
+ }
820
+ function codexNotificationDiagnosticSummary(method, params) {
821
+ const error = objectValue2(params.error);
822
+ const item = objectValue2(params.item);
823
+ const code = stringValue2(params.code) ?? numberStringValue(params.code) ?? stringValue2(error?.code) ?? numberStringValue(error?.code) ?? stringValue2(params.status) ?? numberStringValue(params.status);
824
+ const message = stringValue2(params.message) ?? stringValue2(params.detail) ?? stringValue2(params.reason) ?? stringValue2(error?.message) ?? stringValue2(error?.detail) ?? stringValue2(error?.reason);
825
+ const itemType = stringValue2(item?.type);
826
+ const redactedCode = code === void 0 ? void 0 : redactedDiagnosticText(code);
827
+ const prefix = redactedCode === void 0 ? method : `${method} ${redactedCode}`;
828
+ const detail = message === void 0 ? itemType === void 0 ? void 0 : `item.type=${itemType}` : redactedDiagnosticText(message);
829
+ return detail === void 0 ? void 0 : `${prefix}: ${detail}`;
830
+ }
831
+ function redactedJsonValue(value, depth) {
832
+ if (value === void 0) {
833
+ return null;
834
+ }
835
+ if (typeof value === "string") {
836
+ const redacted = redactedDiagnosticText(value);
837
+ return redacted.length <= maxDiagnosticStringLength ? redacted : `${redacted.slice(0, maxDiagnosticStringLength)}...`;
838
+ }
839
+ if (value === null || typeof value === "boolean" || typeof value === "number") {
840
+ return value;
841
+ }
842
+ if (depth >= maxDiagnosticDepth) {
843
+ return "[max-depth]";
844
+ }
845
+ if (Array.isArray(value)) {
846
+ return value.map((entry) => redactedJsonValue(entry, depth + 1));
847
+ }
848
+ return Object.fromEntries(
849
+ Object.entries(value).map(([key, entry]) => [
850
+ key,
851
+ sensitiveKey(key) ? redactedValue : redactedJsonValue(entry, depth + 1)
852
+ ])
853
+ );
854
+ }
855
+ function sensitiveKey(key) {
856
+ const normalizedKey = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
857
+ switch (normalizedKey) {
858
+ case "authorization":
859
+ case "cookie":
860
+ case "cookies":
861
+ case "credential":
862
+ case "credentials":
863
+ case "password":
864
+ case "secret":
865
+ case "token":
866
+ case "access_key":
867
+ case "api_key":
868
+ case "auth_token":
869
+ case "bearer_token":
870
+ case "client_secret":
871
+ case "id_token":
872
+ case "private_key":
873
+ case "proxy_authorization":
874
+ case "refresh_token":
875
+ case "secret_key":
876
+ case "session_token":
877
+ case "set_cookie":
878
+ case "sig":
879
+ case "signature":
880
+ case "signing_key":
881
+ case "x_amz_signature":
882
+ case "x_api_key":
883
+ return true;
884
+ default:
885
+ return normalizedKey.endsWith("_api_key") || normalizedKey.endsWith("_access_key") || normalizedKey.endsWith("_private_key") || normalizedKey.endsWith("_secret_key") || normalizedKey.endsWith("_authorization") || normalizedKey.endsWith("_cookie") || normalizedKey.endsWith("_cookies") || normalizedKey.endsWith("_token") || normalizedKey.endsWith("_secret") || normalizedKey.endsWith("_password") || normalizedKey.endsWith("_signature") || normalizedKey.endsWith("_sig") || normalizedKey.endsWith("_credential") || normalizedKey.endsWith("_credentials");
886
+ }
887
+ }
888
+ function redactedDiagnosticText(value) {
889
+ return value.replace(
890
+ /https?:\/\/\S*(?:token|secret|signature|sig|x-amz-signature|api[_-]?key|access[_-]?key|private[_-]?key|refresh)\S*/gi,
891
+ redactedUrl
892
+ ).replace(
893
+ /(^|[^A-Za-z0-9_-])(["']?)(set-cookie|cookie)\2\s*(:|=)\s*(["']?)[^\r\n"']*\5/gi,
894
+ (_match, prefix, keyQuote, key, separator, valueQuote) => {
895
+ const spacing = separator === ":" && valueQuote === "" ? " " : "";
896
+ return `${prefix}${keyQuote}${key}${keyQuote}${separator}${spacing}${valueQuote}${redactedValue}${valueQuote}`;
897
+ }
898
+ ).replace(
899
+ new RegExp(
900
+ `(^|[^A-Za-z0-9_-])(["']?)(${credentialTextKeyPattern})\\2\\s*(:|=)\\s*(["']?)(?:(?:Bearer|Basic|Digest|Token|ApiKey|OAuth|Negotiate)\\s+)?[^"'\\s,;}&]+\\5`,
901
+ "gi"
902
+ ),
903
+ (_match, prefix, keyQuote, key, separator, valueQuote) => {
904
+ const spacing = separator === ":" && valueQuote === "" ? " " : "";
905
+ return `${prefix}${keyQuote}${key}${keyQuote}${separator}${spacing}${valueQuote}${redactedValue}${valueQuote}`;
906
+ }
907
+ ).replace(/\bBearer\s+[^\s,;]+/gi, "Bearer [redacted]");
908
+ }
909
+ function objectValue2(value) {
910
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
911
+ }
912
+ function stringValue2(value) {
913
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
914
+ }
915
+ function numberStringValue(value) {
916
+ return typeof value === "number" && Number.isFinite(value) ? value.toString() : void 0;
917
+ }
918
+ var redactedValue, redactedUrl, maxDiagnosticDepth, maxDiagnosticStringLength, credentialTextKeyPattern;
919
+ var init_codexNotificationDiagnostics = __esm({
920
+ "src/codexNotificationDiagnostics.ts"() {
921
+ "use strict";
922
+ redactedValue = "[redacted]";
923
+ redactedUrl = "[redacted-url]";
924
+ maxDiagnosticDepth = 12;
925
+ maxDiagnosticStringLength = 8e3;
926
+ credentialTextKeyPattern = "(?:(?:[A-Za-z0-9]+[_-]+)*(?:proxy-authorization|authorization|set-cookie|cookie|x-api-key|api[_-]?key|access[_-]?key|access[_-]?token|private[_-]?key|refresh(?:[_-]?token)?|auth[_-]?token|id[_-]?token|client[_-]?secret|bearer[_-]?token|session[_-]?token|secret[_-]?key|signing[_-]?key|credential|credentials|token|secret|password|signature|sig))";
927
+ }
928
+ });
929
+
800
930
  // src/codexRuntimeOptions.ts
801
931
  function codexThreadRuntimeOverrides(options) {
802
932
  const session = options.channelSession;
@@ -1831,6 +1961,10 @@ function interruptQueuedMessages(queue, throughSeq) {
1831
1961
  }
1832
1962
  function fuseQueuedMessages(selected) {
1833
1963
  const last = selected[selected.length - 1];
1964
+ const receivedAtMs = selected.reduce(
1965
+ (earliest, message) => message.receivedAtMs === void 0 ? earliest : earliest === void 0 ? message.receivedAtMs : Math.min(earliest, message.receivedAtMs),
1966
+ void 0
1967
+ );
1834
1968
  if (last === void 0) {
1835
1969
  return void 0;
1836
1970
  }
@@ -1839,7 +1973,8 @@ function fuseQueuedMessages(selected) {
1839
1973
  actorSlug: "kandan",
1840
1974
  actorUserId: void 0,
1841
1975
  body: selected.map(codexInputForQueuedKandanMessage).join("\n\n---\n\n"),
1842
- attachments: selected.flatMap((message) => message.attachments)
1976
+ attachments: selected.flatMap((message) => message.attachments),
1977
+ receivedAtMs
1843
1978
  };
1844
1979
  }
1845
1980
  var init_kandanQueue = __esm({
@@ -2346,6 +2481,7 @@ function coalesceAssistantDeltas(deltas) {
2346
2481
  return coalesceTextDeltas(deltas, (delta) => ({
2347
2482
  itemKey: delta.itemKey,
2348
2483
  turnId: delta.turnId,
2484
+ sourceMessageSeq: delta.sourceMessageSeq,
2349
2485
  delta: delta.delta
2350
2486
  }));
2351
2487
  }
@@ -3648,8 +3784,15 @@ async function attachChannelSession(args) {
3648
3784
  handleCodexNotification: (method, params) => {
3649
3785
  const turnId = codexNotificationTurnId(params);
3650
3786
  const threadId = codexNotificationThreadId(params);
3651
- const routedTurnId = turnId ?? codexNotificationActiveTurnFallback(method, state, params);
3787
+ const routedTurnId = turnId ?? codexNotificationActiveTurnFallback(method, state, params, threadId);
3652
3788
  if (codexNotificationBelongsToSession(state, threadId, routedTurnId)) {
3789
+ rememberRecentCodexDiagnostic(
3790
+ state,
3791
+ method,
3792
+ params,
3793
+ threadId,
3794
+ routedTurnId
3795
+ );
3653
3796
  const processingReason = processingReasonForCodexNotification(
3654
3797
  method,
3655
3798
  params
@@ -3713,7 +3856,15 @@ async function attachChannelSession(args) {
3713
3856
  break;
3714
3857
  }
3715
3858
  case "item/agentMessage/delta":
3716
- enqueueAssistantDelta(args, state, params, payloadContext);
3859
+ const assistantDeltaSourceSeq = routedTurnId === void 0 ? startingSourceMessageSeq(state) : void 0;
3860
+ enqueueAssistantDelta(
3861
+ args,
3862
+ state,
3863
+ params,
3864
+ payloadContext,
3865
+ routedTurnId,
3866
+ assistantDeltaSourceSeq
3867
+ );
3717
3868
  break;
3718
3869
  case "item/reasoning/textDelta":
3719
3870
  enqueueReasoningDelta(args, state, params, payloadContext);
@@ -3734,16 +3885,26 @@ async function attachChannelSession(args) {
3734
3885
  case "item/completed":
3735
3886
  enqueueWebSearchProgress(args, state, params, payloadContext);
3736
3887
  enqueueCompletedFileChange(args, state, params, payloadContext);
3888
+ const pendingCompletedAssistantSourceSeq = routedTurnId === void 0 ? startingSourceMessageSeq(state) : void 0;
3737
3889
  const completedAssistantItemForward = forwardCompletedAssistantItem(
3738
3890
  args,
3739
3891
  state,
3740
3892
  params,
3741
- payloadContext
3893
+ payloadContext,
3894
+ routedTurnId,
3895
+ pendingCompletedAssistantSourceSeq
3742
3896
  );
3743
- if (turnId !== void 0) {
3897
+ if (routedTurnId !== void 0) {
3744
3898
  rememberPendingCompletedAssistantItemForward(
3745
3899
  state,
3746
- turnId,
3900
+ routedTurnId,
3901
+ completedAssistantItemForward
3902
+ );
3903
+ }
3904
+ if (pendingCompletedAssistantSourceSeq !== void 0) {
3905
+ rememberPendingCompletedAssistantItemSourceForward(
3906
+ state,
3907
+ pendingCompletedAssistantSourceSeq,
3747
3908
  completedAssistantItemForward
3748
3909
  );
3749
3910
  }
@@ -3752,29 +3913,38 @@ async function attachChannelSession(args) {
3752
3913
  message: error instanceof Error ? error.message : String(error)
3753
3914
  });
3754
3915
  }).finally(() => {
3755
- if (turnId !== void 0) {
3916
+ if (routedTurnId !== void 0) {
3756
3917
  forgetPendingCompletedAssistantItemForward(
3757
3918
  state,
3758
- turnId,
3919
+ routedTurnId,
3920
+ completedAssistantItemForward
3921
+ );
3922
+ }
3923
+ if (pendingCompletedAssistantSourceSeq !== void 0) {
3924
+ forgetPendingCompletedAssistantItemSourceForward(
3925
+ state,
3926
+ pendingCompletedAssistantSourceSeq,
3759
3927
  completedAssistantItemForward
3760
3928
  );
3761
3929
  }
3762
3930
  });
3763
- if (turnId !== void 0) {
3931
+ if (routedTurnId !== void 0) {
3764
3932
  const promise = mirrorLocalTuiInputFromNotification(
3765
3933
  args,
3766
3934
  state,
3767
- turnId,
3935
+ routedTurnId,
3768
3936
  params,
3769
3937
  payloadContext
3770
3938
  );
3771
- rememberPendingTuiInputMirror(state, turnId, promise);
3939
+ rememberPendingTuiInputMirror(state, routedTurnId, promise);
3772
3940
  void promise.catch((error) => {
3773
3941
  args.log("codex.tui_input_mirror_failed", {
3774
- turn_id: turnId,
3942
+ turn_id: routedTurnId,
3775
3943
  message: error instanceof Error ? error.message : String(error)
3776
3944
  });
3777
- }).finally(() => forgetPendingTuiInputMirror(state, turnId));
3945
+ }).finally(
3946
+ () => forgetPendingTuiInputMirror(state, routedTurnId)
3947
+ );
3778
3948
  }
3779
3949
  break;
3780
3950
  case "response_item":
@@ -3845,6 +4015,7 @@ function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThread
3845
4015
  return {
3846
4016
  rootSeq,
3847
4017
  kandanThreadId,
4018
+ kandanThreadTitle: void 0,
3848
4019
  codexThreadId,
3849
4020
  turn: { status: "idle" },
3850
4021
  closed: false,
@@ -3855,7 +4026,10 @@ function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThread
3855
4026
  recoveredCompletingTurnIds: /* @__PURE__ */ new Set(),
3856
4027
  retryableTurnIds: /* @__PURE__ */ new Set(),
3857
4028
  completedAssistantItemKeys: /* @__PURE__ */ new Set(),
4029
+ completedAssistantItemTurns: createBoundedCache(maxForwardedTurnIds),
4030
+ completedAssistantItemSourceSeqs: createBoundedCache(maxForwardedTurnIds),
3858
4031
  pendingCompletedAssistantItemForwards: /* @__PURE__ */ new Map(),
4032
+ pendingCompletedAssistantItemSourceForwards: /* @__PURE__ */ new Map(),
3859
4033
  claimedKandanMessageKeys: /* @__PURE__ */ new Set(),
3860
4034
  localTuiTurnIds: /* @__PURE__ */ new Set(),
3861
4035
  mirroredTuiInputProjections: createBoundedCache(maxForwardedTurnIds),
@@ -3867,6 +4041,7 @@ function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThread
3867
4041
  pendingCommandOutputs: createBoundedCache(maxForwardedTurnIds),
3868
4042
  observedCommandOutputCommands: createBoundedCache(maxForwardedTurnIds * 3),
3869
4043
  streamingFileChangeOutputs: createBoundedCache(maxForwardedTurnIds),
4044
+ recentCodexDiagnostics: createBoundedCache(maxForwardedTurnIds * 3),
3870
4045
  assistantDeltaQueue: createStreamDeltaQueue(),
3871
4046
  reasoningDeltaQueue: createStreamDeltaQueue(),
3872
4047
  commandOutputQueue: createStreamDeltaQueue(),
@@ -3969,6 +4144,7 @@ async function bindChannelSession(args, state, payloadContext) {
3969
4144
  state.minSeq = Math.max(state.minSeq, state.rootSeq);
3970
4145
  }
3971
4146
  } else if (state.codexThreadId !== void 0) {
4147
+ await hydrateKandanThreadTitleFromThreadContext(args, state);
3972
4148
  await bindCurrentCodexThread(args, state);
3973
4149
  } else {
3974
4150
  const resolved = await pushOk2(
@@ -3983,6 +4159,10 @@ async function bindChannelSession(args, state, payloadContext) {
3983
4159
  );
3984
4160
  state.rootSeq = integerValue(resolved.seq);
3985
4161
  state.codexThreadId = stringValue(resolved.codex_thread_id);
4162
+ updateKandanThreadTitle(
4163
+ state,
4164
+ kandanThreadTitleFromResolvedMessage(resolved.message)
4165
+ );
3986
4166
  if (state.codexThreadId === void 0) {
3987
4167
  throw new Error(
3988
4168
  "Kandan thread root metadata did not include a Codex thread id"
@@ -4914,6 +5094,50 @@ function parsePortForwardDecision(body) {
4914
5094
  requestId: match[2] ?? ""
4915
5095
  };
4916
5096
  }
5097
+ function runnerConsoleBodyPreview(body) {
5098
+ const normalized = body.replace(/\s+/g, " ").trim();
5099
+ return normalized.length <= 50 ? normalized : `${normalized.slice(0, 47)}...`;
5100
+ }
5101
+ function updateKandanThreadTitle(state, title) {
5102
+ if (title !== void 0) {
5103
+ state.kandanThreadTitle = title;
5104
+ }
5105
+ }
5106
+ function kandanThreadTitleFromResolvedMessage(message) {
5107
+ return message === void 0 ? void 0 : parseKandanChatEvent(message)?.threadTitle;
5108
+ }
5109
+ function kandanThreadTitleFromWireMessages(messages) {
5110
+ if (!Array.isArray(messages)) {
5111
+ return void 0;
5112
+ }
5113
+ for (const message of messages) {
5114
+ const title = parseKandanChatEvent(message)?.threadTitle;
5115
+ if (title !== void 0) {
5116
+ return title;
5117
+ }
5118
+ }
5119
+ return void 0;
5120
+ }
5121
+ async function hydrateKandanThreadTitleFromThreadContext(args, state) {
5122
+ if (state.kandanThreadId === void 0 || state.kandanThreadTitle !== void 0) {
5123
+ return;
5124
+ }
5125
+ const session = args.options.channelSession;
5126
+ const reply = await pushOk2(
5127
+ args.kandan,
5128
+ args.topic,
5129
+ "session:thread_context",
5130
+ {
5131
+ workspace: session.workspaceSlug,
5132
+ channel: session.channelSlug,
5133
+ thread_id: state.kandanThreadId
5134
+ }
5135
+ );
5136
+ updateKandanThreadTitle(
5137
+ state,
5138
+ kandanThreadTitleFromWireMessages(reply.messages)
5139
+ );
5140
+ }
4917
5141
  async function handleKandanChatEvent(args, state, runnerIdentity, payloadContext, event) {
4918
5142
  const session = args.options.channelSession;
4919
5143
  if (event.type !== "thread.message") {
@@ -5023,6 +5247,7 @@ async function handleKandanChatEvent(args, state, runnerIdentity, payloadContext
5023
5247
  });
5024
5248
  return;
5025
5249
  }
5250
+ updateKandanThreadTitle(state, event.threadTitle);
5026
5251
  if (!claimKandanMessage(args, state, event)) {
5027
5252
  args.log("kandan.message_ignored", {
5028
5253
  seq: event.seq,
@@ -5032,17 +5257,26 @@ async function handleKandanChatEvent(args, state, runnerIdentity, payloadContext
5032
5257
  });
5033
5258
  return;
5034
5259
  }
5260
+ const receivedAtMs = Date.now();
5035
5261
  enqueuePendingKandanMessage(state.queue, {
5036
5262
  seq: event.seq,
5037
5263
  actorSlug: event.actorSlug,
5038
5264
  actorUserId: event.actorUserId,
5039
5265
  body: event.body,
5040
- attachments: event.attachments
5266
+ attachments: event.attachments,
5267
+ receivedAtMs
5041
5268
  });
5042
5269
  args.log("kandan.message_queued", {
5043
5270
  seq: event.seq,
5044
5271
  actor_slug: event.actorSlug ?? null,
5045
5272
  actor_user_id: event.actorUserId ?? null,
5273
+ linzumi_thread_id: state.kandanThreadId ?? null,
5274
+ codex_thread_id: state.codexThreadId ?? null,
5275
+ runner_console: {
5276
+ body_preview: runnerConsoleBodyPreview(event.body),
5277
+ message_received_at_ms: receivedAtMs,
5278
+ ...state.kandanThreadTitle === void 0 ? {} : { linzumi_thread_title: state.kandanThreadTitle }
5279
+ },
5046
5280
  queue_depth: pendingKandanMessageQueueLength(state.queue)
5047
5281
  });
5048
5282
  await publishKandanMessageState(args, event, { status: "queued" });
@@ -5111,13 +5345,21 @@ async function startThreadMessageTurn(args, state, payloadContext, message) {
5111
5345
  actorSlug: message.actorSlug,
5112
5346
  actorUserId: message.actorUserId,
5113
5347
  body: message.body,
5114
- attachments: []
5348
+ attachments: [],
5349
+ receivedAtMs: Date.now()
5115
5350
  };
5116
5351
  enqueuePendingKandanMessage(state.queue, queued);
5117
5352
  args.log("kandan.message_queued", {
5118
5353
  seq: queued.seq,
5119
5354
  actor_slug: queued.actorSlug ?? null,
5120
5355
  actor_user_id: queued.actorUserId ?? null,
5356
+ linzumi_thread_id: state.kandanThreadId ?? null,
5357
+ codex_thread_id: state.codexThreadId ?? null,
5358
+ runner_console: {
5359
+ body_preview: runnerConsoleBodyPreview(queued.body),
5360
+ message_received_at_ms: queued.receivedAtMs ?? Date.now(),
5361
+ ...state.kandanThreadTitle === void 0 ? {} : { linzumi_thread_title: state.kandanThreadTitle }
5362
+ },
5121
5363
  queue_depth: pendingKandanMessageQueueLength(state.queue)
5122
5364
  });
5123
5365
  await publishQueuedMessageState(args, state, queued, { status: "queued" });
@@ -5141,11 +5383,15 @@ async function bindUnboundHistoricalThread(args, state, event) {
5141
5383
  );
5142
5384
  const rootSeq = integerValue(resolved.seq);
5143
5385
  const codexThreadId = stringValue(resolved.codex_thread_id);
5386
+ const resolvedThreadTitle = kandanThreadTitleFromResolvedMessage(
5387
+ resolved.message
5388
+ );
5144
5389
  if (rootSeq !== event.replyToSeq || codexThreadId === void 0) {
5145
5390
  return false;
5146
5391
  }
5147
5392
  state.rootSeq = rootSeq;
5148
5393
  state.kandanThreadId = event.threadId;
5394
+ updateKandanThreadTitle(state, event.threadTitle ?? resolvedThreadTitle);
5149
5395
  state.codexThreadId = codexThreadId;
5150
5396
  await bindCurrentCodexThread(args, state);
5151
5397
  args.log("kandan.thread_bound_from_historical_reply", {
@@ -5240,7 +5486,13 @@ async function drainKandanMessageQueue(args, state, payloadContext) {
5240
5486
  queued_seq: next.seq,
5241
5487
  actor_slug: next.actorSlug ?? null,
5242
5488
  actor_user_id: next.actorUserId ?? null,
5489
+ linzumi_thread_id: state.kandanThreadId ?? null,
5243
5490
  codex_thread_id: state.codexThreadId ?? null,
5491
+ runner_console: {
5492
+ body_preview: runnerConsoleBodyPreview(next.body),
5493
+ message_received_at_ms: next.receivedAtMs ?? Date.now(),
5494
+ ...state.kandanThreadTitle === void 0 ? {} : { linzumi_thread_title: state.kandanThreadTitle }
5495
+ },
5244
5496
  queue_depth: pendingKandanMessageQueueLength(state.queue)
5245
5497
  });
5246
5498
  await publishQueuedMessageState(args, state, next, {
@@ -5306,7 +5558,7 @@ async function drainKandanMessageQueue(args, state, payloadContext) {
5306
5558
  state.codexThreadId = newCodexThreadId;
5307
5559
  await bindCurrentCodexThread(args, state);
5308
5560
  args.log("codex.thread_rebound", {
5309
- kandan_thread_id: state.kandanThreadId,
5561
+ linzumi_thread_id: state.kandanThreadId,
5310
5562
  old_codex_thread_id: oldCodexThreadId ?? null,
5311
5563
  new_codex_thread_id: newCodexThreadId
5312
5564
  });
@@ -5552,6 +5804,15 @@ async function forwardCompletedCodexTurn(args, state, turnId, payloadContext) {
5552
5804
  if (completingTurnWasRecovered(state, turnId)) {
5553
5805
  return;
5554
5806
  }
5807
+ if (completingQueuedSeq !== void 0) {
5808
+ await waitForPendingCompletedAssistantItemSourceForwards(
5809
+ state,
5810
+ completingQueuedSeq
5811
+ );
5812
+ }
5813
+ if (completingTurnWasRecovered(state, turnId)) {
5814
+ return;
5815
+ }
5555
5816
  await waitForStreamingForwardChains(args, state, payloadContext);
5556
5817
  if (completingTurnWasRecovered(state, turnId)) {
5557
5818
  return;
@@ -5614,6 +5875,13 @@ async function forwardCompletedCodexTurn(args, state, turnId, payloadContext) {
5614
5875
  );
5615
5876
  const sourceMessageSeq = sourceMessageSeqForTurn(state, turnId);
5616
5877
  const rootSeq = state.rootSeq;
5878
+ const completedAssistantOutputCount = completedOutputs.filter(
5879
+ completedOutputProjectionIsAssistant
5880
+ ).length;
5881
+ const completedForwardedAssistantSnapshotCount = forwardedAssistantSnapshotOutputCount(state, messages);
5882
+ const completedSourceSeqAssistantItemCount = sourceMessageSeq === void 0 ? 0 : completedAssistantItemForwardedForSourceSeq(state, sourceMessageSeq);
5883
+ const completedStreamingAssistantOutputCount = completedAssistantOutputCount === 0 ? await completeStreamingAssistantOutputsForTurn(args, state, turnId) : 0;
5884
+ const completedAssistantResponseSeen = completedAssistantOutputCount > 0 || completedForwardedAssistantSnapshotCount > 0 || completedSourceSeqAssistantItemCount > 0 || completedStreamingAssistantOutputCount > 0 || completedAssistantItemForwardedForTurn(state, turnId);
5617
5885
  for (const output of completedOutputs) {
5618
5886
  if (completingTurnWasRecovered(state, turnId)) {
5619
5887
  return;
@@ -5631,6 +5899,35 @@ async function forwardCompletedCodexTurn(args, state, turnId, payloadContext) {
5631
5899
  if (completingTurnWasRecovered(state, turnId)) {
5632
5900
  return;
5633
5901
  }
5902
+ if (!completedAssistantResponseSeen) {
5903
+ const recentError = recentCodexDiagnosticSummaryForTurn(
5904
+ state,
5905
+ "error",
5906
+ state.codexThreadId,
5907
+ turnId
5908
+ );
5909
+ const recentWarning = recentCodexDiagnosticSummaryForTurn(
5910
+ state,
5911
+ "warning",
5912
+ state.codexThreadId,
5913
+ turnId
5914
+ );
5915
+ args.log("codex.turn_completed_without_assistant_output", {
5916
+ turn_id: turnId,
5917
+ codex_thread_id: state.codexThreadId,
5918
+ kandan_thread_id: state.kandanThreadId,
5919
+ source_seq: sourceMessageSeq ?? null,
5920
+ output_count: completedOutputs.length,
5921
+ assistant_output_count: completedAssistantOutputCount,
5922
+ forwarded_assistant_snapshot_output_count: completedForwardedAssistantSnapshotCount,
5923
+ source_seq_assistant_item_count: completedSourceSeqAssistantItemCount,
5924
+ completed_streaming_assistant_output_count: completedStreamingAssistantOutputCount,
5925
+ thread_read_output_count: readMessages.length,
5926
+ session_log_output_count: sessionLogMessages.length,
5927
+ recent_error: recentError ?? null,
5928
+ recent_warning: recentWarning ?? null
5929
+ });
5930
+ }
5634
5931
  rememberForwardedTurnId(state, turnId);
5635
5932
  forgetLocalTuiTurnId(state, turnId);
5636
5933
  if (completingQueuedSeq !== void 0) {
@@ -5638,8 +5935,9 @@ async function forwardCompletedCodexTurn(args, state, turnId, payloadContext) {
5638
5935
  args,
5639
5936
  state.kandanThreadId,
5640
5937
  completingQueuedSeq,
5641
- {
5642
- status: "processed"
5938
+ completedAssistantResponseSeen ? { status: "processed" } : {
5939
+ status: "failed",
5940
+ reason: "Codex completed without producing a response."
5643
5941
  }
5644
5942
  );
5645
5943
  clearActiveProcessingState(state, completingQueuedSeq);
@@ -5789,6 +6087,21 @@ function completedOutputProjectionsForTurn(state, turnId, messages) {
5789
6087
  compareCompletedOutputProjection
5790
6088
  );
5791
6089
  }
6090
+ function completedOutputProjectionIsAssistant(output) {
6091
+ switch (output.kind) {
6092
+ case "snapshot":
6093
+ return stringValue(output.message.structured.kind) === "codex_assistant_message";
6094
+ case "cached_reasoning":
6095
+ case "cached_command":
6096
+ case "cached_file_change":
6097
+ return false;
6098
+ }
6099
+ }
6100
+ function forwardedAssistantSnapshotOutputCount(state, messages) {
6101
+ return messages.filter(
6102
+ (message) => stringValue(message.structured.kind) === "codex_assistant_message" && completedAssistantItemForwarded(state, message.itemKey)
6103
+ ).length;
6104
+ }
5792
6105
  async function sessionLogOutputMessagesForTurn(args, codexThreadId, turnId) {
5793
6106
  try {
5794
6107
  return await codexSessionLogOutputMessagesForTurn({
@@ -5805,39 +6118,76 @@ async function sessionLogOutputMessagesForTurn(args, codexThreadId, turnId) {
5805
6118
  }
5806
6119
  }
5807
6120
  function completedMessagesWithSessionLog(readMessages, sessionLogMessages) {
5808
- const readExactKeys = new Set(readMessages.map(completedMessageExactKey));
5809
- const readSemanticCounts = completedMessageSemanticCounts(readMessages);
5810
- const missingSessionStructuredMessages = [];
5811
- for (const message of sessionLogMessages) {
5812
- if (stringValue(message.structured.kind) === "codex_assistant_message") {
5813
- continue;
5814
- }
5815
- if (readExactKeys.has(completedMessageExactKey(message))) {
5816
- continue;
6121
+ if (sessionLogMessages.length === 0) {
6122
+ return [...readMessages];
6123
+ }
6124
+ const consumedReadIndexes = /* @__PURE__ */ new Set();
6125
+ const sessionOrderedEntries = sessionLogMessages.map((message) => {
6126
+ const readIndex = findUnconsumedCompletedMessageIndex(
6127
+ readMessages,
6128
+ consumedReadIndexes,
6129
+ completedMessageExactKey(message)
6130
+ );
6131
+ if (readIndex >= 0) {
6132
+ consumedReadIndexes.add(readIndex);
6133
+ return { kind: "read", readIndex };
5817
6134
  }
5818
- const semanticKey = completedMessageSemanticKey(message);
5819
- const remainingReadCount = readSemanticCounts.get(semanticKey) ?? 0;
5820
- if (remainingReadCount > 0) {
5821
- readSemanticCounts.set(semanticKey, remainingReadCount - 1);
5822
- continue;
6135
+ const semanticReadIndex = findUnconsumedCompletedMessageSemanticIndex(
6136
+ readMessages,
6137
+ consumedReadIndexes,
6138
+ completedMessageSemanticKey(message)
6139
+ );
6140
+ if (semanticReadIndex >= 0) {
6141
+ consumedReadIndexes.add(semanticReadIndex);
6142
+ return { kind: "read", readIndex: semanticReadIndex };
6143
+ }
6144
+ return { kind: "session", message };
6145
+ });
6146
+ const mergedMessages = [];
6147
+ let nextReadIndex = 0;
6148
+ for (const entry of sessionOrderedEntries) {
6149
+ switch (entry.kind) {
6150
+ case "read":
6151
+ while (nextReadIndex < entry.readIndex) {
6152
+ if (!consumedReadIndexes.has(nextReadIndex)) {
6153
+ const message = readMessages[nextReadIndex];
6154
+ if (message !== void 0) {
6155
+ mergedMessages.push(message);
6156
+ }
6157
+ }
6158
+ nextReadIndex += 1;
6159
+ }
6160
+ mergedMessages.push(readMessages[entry.readIndex]);
6161
+ nextReadIndex = Math.max(nextReadIndex, entry.readIndex + 1);
6162
+ break;
6163
+ case "session":
6164
+ mergedMessages.push(entry.message);
6165
+ break;
5823
6166
  }
5824
- missingSessionStructuredMessages.push(message);
5825
6167
  }
5826
- if (missingSessionStructuredMessages.length === 0) {
5827
- return [...readMessages];
6168
+ while (nextReadIndex < readMessages.length) {
6169
+ if (!consumedReadIndexes.has(nextReadIndex)) {
6170
+ const message = readMessages[nextReadIndex];
6171
+ if (message !== void 0) {
6172
+ mergedMessages.push(message);
6173
+ }
6174
+ }
6175
+ nextReadIndex += 1;
5828
6176
  }
5829
- return [...missingSessionStructuredMessages, ...readMessages];
6177
+ return mergedMessages;
5830
6178
  }
5831
6179
  function completedMessageExactKey(message) {
5832
6180
  return `${stringValue(message.structured.kind) ?? ""}:${message.itemKey}`;
5833
6181
  }
5834
- function completedMessageSemanticCounts(messages) {
5835
- const counts = /* @__PURE__ */ new Map();
5836
- for (const message of messages) {
5837
- const key = completedMessageSemanticKey(message);
5838
- counts.set(key, (counts.get(key) ?? 0) + 1);
5839
- }
5840
- return counts;
6182
+ function findUnconsumedCompletedMessageIndex(messages, consumedIndexes, exactKey) {
6183
+ return messages.findIndex(
6184
+ (message, index) => !consumedIndexes.has(index) && completedMessageExactKey(message) === exactKey
6185
+ );
6186
+ }
6187
+ function findUnconsumedCompletedMessageSemanticIndex(messages, consumedIndexes, semanticKey) {
6188
+ return messages.findIndex(
6189
+ (message, index) => !consumedIndexes.has(index) && completedMessageSemanticKey(message) === semanticKey
6190
+ );
5841
6191
  }
5842
6192
  function completedMessageSemanticKey(message) {
5843
6193
  const kind = stringValue(message.structured.kind) ?? "";
@@ -5966,7 +6316,12 @@ async function postCompletedOutputProjection(args, state, payloadContext, params
5966
6316
  );
5967
6317
  break;
5968
6318
  }
5969
- logCompletedCodexOutput(args, params.turnId, params.output.message);
6319
+ logCompletedCodexOutput(
6320
+ args,
6321
+ state,
6322
+ params.turnId,
6323
+ params.output.message
6324
+ );
5970
6325
  break;
5971
6326
  }
5972
6327
  case "cached_reasoning": {
@@ -5988,7 +6343,7 @@ async function postCompletedOutputProjection(args, state, payloadContext, params
5988
6343
  message.structured
5989
6344
  );
5990
6345
  forgetStreamingReasoningOutput(state, output.itemKey);
5991
- logCompletedCodexOutput(args, params.turnId, message);
6346
+ logCompletedCodexOutput(args, state, params.turnId, message);
5992
6347
  break;
5993
6348
  }
5994
6349
  case "cached_command": {
@@ -6015,7 +6370,7 @@ async function postCompletedOutputProjection(args, state, payloadContext, params
6015
6370
  message.structured
6016
6371
  );
6017
6372
  forgetStreamingCommandOutput(state, output.itemKey);
6018
- logCompletedCodexOutput(args, params.turnId, message);
6373
+ logCompletedCodexOutput(args, state, params.turnId, message);
6019
6374
  break;
6020
6375
  }
6021
6376
  case "cached_file_change": {
@@ -6038,26 +6393,32 @@ async function postCompletedOutputProjection(args, state, payloadContext, params
6038
6393
  message.structured
6039
6394
  );
6040
6395
  forgetStreamingFileChangeOutput(state, output.itemKey);
6041
- logCompletedCodexOutput(args, params.turnId, message);
6396
+ logCompletedCodexOutput(args, state, params.turnId, message);
6042
6397
  break;
6043
6398
  }
6044
6399
  }
6045
6400
  }
6046
- function logCompletedCodexOutput(args, turnId, message) {
6401
+ function logCompletedCodexOutput(args, state, turnId, message) {
6047
6402
  args.log("kandan.codex_output_forwarded", {
6048
6403
  turn_id: turnId,
6049
6404
  item_key: message.itemKey,
6405
+ linzumi_thread_id: state.kandanThreadId,
6406
+ codex_thread_id: state.codexThreadId,
6050
6407
  structured_kind: stringValue(message.structured.kind) ?? null,
6408
+ runner_console: {
6409
+ body_preview: runnerConsoleBodyPreview(message.body)
6410
+ },
6051
6411
  command: stringValue(message.structured.command) ?? null,
6052
6412
  file_paths: fileChangePaths(message.structured)
6053
6413
  });
6054
6414
  }
6055
- async function forwardCompletedAssistantItem(args, state, params, payloadContext) {
6415
+ async function forwardCompletedAssistantItem(args, state, params, payloadContext, fallbackTurnId = void 0, fallbackSourceMessageSeq = void 0) {
6056
6416
  if (state.kandanThreadId === void 0 || state.codexThreadId === void 0) {
6057
6417
  return;
6058
6418
  }
6059
- const turnId = stringValue(params.turnId) ?? stringValue(params.turn_id) ?? stringValue(objectValue(params.turn)?.id);
6060
6419
  const item = objectValue(params.item) ?? params;
6420
+ const itemTurn = objectValue(item.turn);
6421
+ const turnId = stringValue(params.turnId) ?? stringValue(params.turn_id) ?? stringValue(objectValue(params.turn)?.id) ?? stringValue(item.turnId) ?? stringValue(item.turn_id) ?? stringValue(itemTurn?.id) ?? fallbackTurnId;
6061
6422
  const completedItemKey = stringValue(params.itemId) ?? stringValue(params.item_id) ?? stringValue(objectValue(params.item)?.id);
6062
6423
  if (turnId !== void 0) {
6063
6424
  await waitForPendingTuiInputMirror(state, turnId);
@@ -6089,7 +6450,7 @@ async function forwardCompletedAssistantItem(args, state, params, payloadContext
6089
6450
  if (message === void 0 || completedAssistantItemForwarded(state, message.itemKey)) {
6090
6451
  return;
6091
6452
  }
6092
- const sourceMessageSeq = messageTurnId === void 0 ? void 0 : sourceMessageSeqForTurn(state, messageTurnId);
6453
+ const sourceMessageSeq = messageTurnId === void 0 ? fallbackSourceMessageSeq ?? startingSourceMessageSeq(state) : sourceMessageSeqForTurn(state, messageTurnId);
6093
6454
  if (messageTurnId !== void 0 && sourceMessageSeq === void 0) {
6094
6455
  args.log("codex.completed_assistant_item_without_source_message", {
6095
6456
  turn_id: messageTurnId,
@@ -6137,11 +6498,21 @@ async function forwardCompletedAssistantItem(args, state, params, payloadContext
6137
6498
  `Cannot reconcile completed assistant item ${message.itemKey} from item notification; ${streamed.candidateCount} active streamed assistant outputs exist`
6138
6499
  );
6139
6500
  }
6140
- rememberCompletedAssistantItemKey(state, message.itemKey);
6141
- logCompletedCodexOutput(args, messageTurnId ?? `item:${message.itemKey}`, {
6142
- ...message,
6143
- structured
6144
- });
6501
+ rememberCompletedAssistantItemKey(
6502
+ state,
6503
+ message.itemKey,
6504
+ messageTurnId,
6505
+ sourceMessageSeq
6506
+ );
6507
+ logCompletedCodexOutput(
6508
+ args,
6509
+ state,
6510
+ messageTurnId ?? `item:${message.itemKey}`,
6511
+ {
6512
+ ...message,
6513
+ structured
6514
+ }
6515
+ );
6145
6516
  }
6146
6517
  function compareCompletedOutputProjection(left, right) {
6147
6518
  if (left.kind === "snapshot" && right.kind === "snapshot" && left.match.kind !== right.match.kind && (left.match.kind === "none" || right.match.kind === "none")) {
@@ -6213,7 +6584,7 @@ async function forwardAssistantDeltaPayload(args, state, delta, payloadContext)
6213
6584
  }
6214
6585
  const existing = findStreamingAssistantOutput(state, delta.itemKey);
6215
6586
  const nextContent = `${existing?.content ?? ""}${delta.delta}`;
6216
- const sourceMessageSeq = delta.turnId === void 0 ? void 0 : sourceMessageSeqForTurn(state, delta.turnId);
6587
+ const sourceMessageSeq = delta.turnId === void 0 ? delta.sourceMessageSeq : sourceMessageSeqForTurn(state, delta.turnId);
6217
6588
  const rootSeq = state.rootSeq;
6218
6589
  if (delta.turnId !== void 0 && sourceMessageSeq === void 0) {
6219
6590
  args.log("codex.delta_without_source_message", {
@@ -6260,6 +6631,7 @@ async function forwardAssistantDeltaPayload(args, state, delta, payloadContext)
6260
6631
  rememberStreamingAssistantOutput(state, {
6261
6632
  itemKey: delta.itemKey,
6262
6633
  turnId: delta.turnId,
6634
+ sourceMessageSeq,
6263
6635
  seq,
6264
6636
  content: nextContent
6265
6637
  });
@@ -6275,20 +6647,24 @@ async function forwardAssistantDeltaPayload(args, state, delta, payloadContext)
6275
6647
  );
6276
6648
  rememberStreamingAssistantOutput(state, {
6277
6649
  ...existing,
6650
+ sourceMessageSeq: existing.sourceMessageSeq ?? sourceMessageSeq,
6278
6651
  content: nextContent
6279
6652
  });
6280
6653
  }
6281
6654
  args.log("kandan.codex_delta_forwarded", {
6282
6655
  item_key: delta.itemKey,
6283
6656
  turn_id: delta.turnId ?? null,
6657
+ linzumi_thread_id: state.kandanThreadId,
6658
+ codex_thread_id: state.codexThreadId,
6284
6659
  content_length: nextContent.length
6285
6660
  });
6286
6661
  }
6287
- function enqueueAssistantDelta(args, state, params, payloadContext) {
6288
- const delta = codexAssistantDeltaFromNotification(params);
6289
- if (delta === void 0) {
6662
+ function enqueueAssistantDelta(args, state, params, payloadContext, fallbackTurnId = void 0, fallbackSourceMessageSeq = void 0) {
6663
+ const parsedDelta = codexAssistantDeltaFromNotification(params);
6664
+ if (parsedDelta === void 0) {
6290
6665
  return;
6291
6666
  }
6667
+ const delta = parsedDelta.turnId === void 0 && fallbackTurnId !== void 0 ? { ...parsedDelta, turnId: fallbackTurnId } : { ...parsedDelta, sourceMessageSeq: fallbackSourceMessageSeq };
6292
6668
  enqueueStreamDelta(
6293
6669
  state.assistantDeltaQueue,
6294
6670
  delta,
@@ -6527,6 +6903,8 @@ async function forwardReasoningDeltaPayload(args, state, delta, payloadContext)
6527
6903
  args.log("kandan.codex_reasoning_delta_forwarded", {
6528
6904
  item_key: delta.itemKey,
6529
6905
  turn_id: turnId,
6906
+ linzumi_thread_id: state.kandanThreadId,
6907
+ codex_thread_id: state.codexThreadId,
6530
6908
  content_length: nextContent.length
6531
6909
  });
6532
6910
  }
@@ -6715,6 +7093,8 @@ async function forwardCommandOutputDeltaPayload(args, state, delta, payloadConte
6715
7093
  args.log("kandan.codex_command_output_forwarded", {
6716
7094
  item_key: delta.itemKey,
6717
7095
  turn_id: turnId,
7096
+ linzumi_thread_id: state.kandanThreadId,
7097
+ codex_thread_id: state.codexThreadId,
6718
7098
  process_id: delta.processId ?? null,
6719
7099
  stream: delta.stream,
6720
7100
  output_length: persistedOutput.length
@@ -6798,6 +7178,8 @@ async function forwardFileChangeDeltaPayload(args, state, delta, payloadContext)
6798
7178
  args.log("kandan.codex_file_change_forwarded", {
6799
7179
  item_key: delta.itemKey,
6800
7180
  turn_id: turnId,
7181
+ linzumi_thread_id: state.kandanThreadId,
7182
+ codex_thread_id: state.codexThreadId,
6801
7183
  patch_length: patchText.length
6802
7184
  });
6803
7185
  }
@@ -6936,6 +7318,8 @@ async function forwardTerminalInput(args, state, params, payloadContext) {
6936
7318
  args.log("kandan.codex_output_forwarded", {
6937
7319
  turn_id: turnId,
6938
7320
  item_key: terminal.itemKey,
7321
+ linzumi_thread_id: state.kandanThreadId,
7322
+ codex_thread_id: state.codexThreadId,
6939
7323
  structured_kind: "codex_terminal_input",
6940
7324
  command: null,
6941
7325
  file_paths: []
@@ -7023,6 +7407,8 @@ async function forwardWebSearchProgress(args, state, params, payloadContext) {
7023
7407
  args.log("kandan.codex_web_search_progress_forwarded", {
7024
7408
  turn_id: progress.turnId,
7025
7409
  item_key: progress.itemKey,
7410
+ linzumi_thread_id: state.kandanThreadId,
7411
+ codex_thread_id: state.codexThreadId,
7026
7412
  query_count: queries.length
7027
7413
  });
7028
7414
  }
@@ -7078,8 +7464,42 @@ async function completeInterruptedAssistantOutputs(args, state, turnId, payloadC
7078
7464
  "completed"
7079
7465
  );
7080
7466
  forgetStreamingAssistantOutput(state, output.itemKey);
7081
- rememberCompletedAssistantItemKey(state, output.itemKey);
7467
+ rememberCompletedAssistantItemKey(state, output.itemKey, turnId);
7468
+ }
7469
+ }
7470
+ async function completeStreamingAssistantOutputsForTurn(args, state, turnId) {
7471
+ const sourceMessageSeq = sourceMessageSeqForTurn(state, turnId);
7472
+ const outputs = boundedCacheValues(state.streamingAssistantOutputs).filter(
7473
+ (output) => output.turnId === turnId || sourceMessageSeq !== void 0 && output.sourceMessageSeq === sourceMessageSeq
7474
+ );
7475
+ for (const output of outputs) {
7476
+ const message = {
7477
+ itemKey: output.itemKey,
7478
+ body: output.content,
7479
+ structured: codexAssistantStructuredMessage(
7480
+ output.itemKey,
7481
+ output.content,
7482
+ "completed"
7483
+ )
7484
+ };
7485
+ await editStreamedCodexOutput(
7486
+ args,
7487
+ state,
7488
+ output.seq,
7489
+ output.itemKey,
7490
+ output.content,
7491
+ "completed"
7492
+ );
7493
+ forgetStreamingAssistantOutput(state, output.itemKey);
7494
+ rememberCompletedAssistantItemKey(
7495
+ state,
7496
+ output.itemKey,
7497
+ turnId,
7498
+ output.sourceMessageSeq
7499
+ );
7500
+ logCompletedCodexOutput(args, state, turnId, message);
7082
7501
  }
7502
+ return outputs.length;
7083
7503
  }
7084
7504
  function streamingClientMessageId(instanceId, delta) {
7085
7505
  const digest = createHash("sha256").update(`${instanceId}:${delta.turnId ?? "turn"}:${delta.itemKey}`).digest("hex").slice(0, 32);
@@ -7457,12 +7877,34 @@ function forgetStreamingAssistantOutput(state, itemKey) {
7457
7877
  function completedAssistantItemForwarded(state, itemKey) {
7458
7878
  return state.completedAssistantItemKeys.has(itemKey);
7459
7879
  }
7460
- function rememberCompletedAssistantItemKey(state, itemKey) {
7880
+ function completedAssistantItemForwardedForTurn(state, turnId) {
7881
+ return boundedCacheValues(state.completedAssistantItemTurns).some(
7882
+ (item) => item.turnId === turnId
7883
+ );
7884
+ }
7885
+ function completedAssistantItemForwardedForSourceSeq(state, sourceSeq) {
7886
+ return boundedCacheValues(state.completedAssistantItemSourceSeqs).filter(
7887
+ (item) => item.sourceSeq === sourceSeq
7888
+ ).length;
7889
+ }
7890
+ function rememberCompletedAssistantItemKey(state, itemKey, turnId = void 0, sourceSeq = void 0) {
7461
7891
  rememberBoundedStringSet(
7462
7892
  state.completedAssistantItemKeys,
7463
7893
  itemKey,
7464
7894
  maxForwardedTurnIds
7465
7895
  );
7896
+ if (turnId !== void 0) {
7897
+ rememberBoundedCacheValue(state.completedAssistantItemTurns, itemKey, {
7898
+ itemKey,
7899
+ turnId
7900
+ });
7901
+ }
7902
+ if (sourceSeq !== void 0) {
7903
+ rememberBoundedCacheValue(state.completedAssistantItemSourceSeqs, itemKey, {
7904
+ itemKey,
7905
+ sourceSeq
7906
+ });
7907
+ }
7466
7908
  }
7467
7909
  function rememberStreamingReasoningOutput(state, output) {
7468
7910
  rememberBoundedCacheValue(
@@ -7537,7 +7979,9 @@ function mergeWebSearchQueries(existing, incoming) {
7537
7979
  }
7538
7980
  function codexNotificationTurnId(params) {
7539
7981
  const turn = objectValue(params.turn);
7540
- return stringValue(turn?.id) ?? stringValue(params.turnId) ?? stringValue(params.turn_id);
7982
+ const item = objectValue(params.item);
7983
+ const itemTurn = objectValue(item?.turn);
7984
+ return stringValue(turn?.id) ?? stringValue(params.turnId) ?? stringValue(params.turn_id) ?? stringValue(item?.turnId) ?? stringValue(item?.turn_id) ?? stringValue(itemTurn?.id);
7541
7985
  }
7542
7986
  function codexNotificationThreadId(params) {
7543
7987
  const thread = objectValue(params.thread);
@@ -7577,11 +8021,58 @@ function inferThreadOnlyCompletedTurnId(args, state, threadId) {
7577
8021
  return void 0;
7578
8022
  }
7579
8023
  }
7580
- function codexNotificationActiveTurnFallback(method, state, params) {
7581
- if (method !== "response_item" || codexCompletedFileChangeFromNotification(params) === void 0) {
8024
+ function codexNotificationActiveTurnFallback(method, state, params, threadId) {
8025
+ if (threadId === void 0 || state.codexThreadId !== threadId) {
7582
8026
  return void 0;
7583
8027
  }
7584
- return activeTurnId(state.turn);
8028
+ switch (method) {
8029
+ case "response_item":
8030
+ return codexCompletedFileChangeFromNotification(params) === void 0 ? void 0 : activeTurnId(state.turn);
8031
+ case "item/agentMessage/delta":
8032
+ case "item/completed":
8033
+ case "warning":
8034
+ case "error":
8035
+ return activeTurnId(state.turn);
8036
+ default:
8037
+ return void 0;
8038
+ }
8039
+ }
8040
+ function rememberRecentCodexDiagnostic(state, method, params, threadId, turnId) {
8041
+ switch (method) {
8042
+ case "warning":
8043
+ case "error": {
8044
+ const summary = codexNotificationDiagnosticSummary(method, params) ?? `${method}: seen (no details)`;
8045
+ const cacheKey = [
8046
+ method,
8047
+ threadId ?? "no-thread",
8048
+ turnId ?? "no-turn"
8049
+ ].join(":");
8050
+ if (getBoundedCacheValue(state.recentCodexDiagnostics, cacheKey)) {
8051
+ break;
8052
+ }
8053
+ rememberBoundedCacheValue(state.recentCodexDiagnostics, cacheKey, {
8054
+ method,
8055
+ threadId,
8056
+ turnId,
8057
+ summary
8058
+ });
8059
+ break;
8060
+ }
8061
+ default:
8062
+ break;
8063
+ }
8064
+ }
8065
+ function recentCodexDiagnosticSummaryForTurn(state, method, threadId, turnId) {
8066
+ return getBoundedCacheValue(
8067
+ state.recentCodexDiagnostics,
8068
+ [method, threadId, turnId].join(":")
8069
+ )?.summary ?? getBoundedCacheValue(
8070
+ state.recentCodexDiagnostics,
8071
+ [method, "no-thread", turnId].join(":")
8072
+ )?.summary ?? getBoundedCacheValue(
8073
+ state.recentCodexDiagnostics,
8074
+ [method, threadId, "no-turn"].join(":")
8075
+ )?.summary;
7585
8076
  }
7586
8077
  function rememberLocalTuiTurnIfNeeded(args, state, threadId, turnId) {
7587
8078
  if (args.options.launchTui !== true || state.codexThreadId !== threadId || state.turn.status !== "idle") {
@@ -7645,6 +8136,35 @@ function forgetPendingCompletedAssistantItemForward(state, turnId, promise) {
7645
8136
  state.pendingCompletedAssistantItemForwards.delete(turnId);
7646
8137
  }
7647
8138
  }
8139
+ function rememberPendingCompletedAssistantItemSourceForward(state, sourceMessageSeq, promise) {
8140
+ const existing = state.pendingCompletedAssistantItemSourceForwards.get(sourceMessageSeq);
8141
+ const pending = existing ?? /* @__PURE__ */ new Set();
8142
+ pending.add(promise);
8143
+ state.pendingCompletedAssistantItemSourceForwards.set(
8144
+ sourceMessageSeq,
8145
+ pending
8146
+ );
8147
+ trimBoundedMap(
8148
+ state.pendingCompletedAssistantItemSourceForwards,
8149
+ maxForwardedTurnIds
8150
+ );
8151
+ }
8152
+ function forgetPendingCompletedAssistantItemSourceForward(state, sourceMessageSeq, promise) {
8153
+ const pending = state.pendingCompletedAssistantItemSourceForwards.get(sourceMessageSeq);
8154
+ if (pending === void 0) {
8155
+ return;
8156
+ }
8157
+ pending.delete(promise);
8158
+ if (pending.size === 0) {
8159
+ state.pendingCompletedAssistantItemSourceForwards.delete(sourceMessageSeq);
8160
+ }
8161
+ }
8162
+ async function waitForPendingCompletedAssistantItemSourceForwards(state, sourceMessageSeq) {
8163
+ const pending = state.pendingCompletedAssistantItemSourceForwards.get(sourceMessageSeq);
8164
+ if (pending !== void 0) {
8165
+ await Promise.allSettled([...pending]);
8166
+ }
8167
+ }
7648
8168
  async function waitForPendingCompletedAssistantItemForwards(state, turnId) {
7649
8169
  const pending = state.pendingCompletedAssistantItemForwards.get(turnId);
7650
8170
  if (pending !== void 0) {
@@ -7695,6 +8215,16 @@ function rememberTurnReplyTarget(state, turnId, replyToSeq) {
7695
8215
  function sourceMessageSeqForTurn(state, turnId) {
7696
8216
  return getBoundedCacheValue(state.turnReplyTargets, turnId)?.replyToSeq;
7697
8217
  }
8218
+ function startingSourceMessageSeq(state) {
8219
+ switch (state.turn.status) {
8220
+ case "starting":
8221
+ return state.turn.queuedSeq;
8222
+ case "active":
8223
+ case "completing":
8224
+ case "idle":
8225
+ return void 0;
8226
+ }
8227
+ }
7698
8228
  function fileChangePaths(structured) {
7699
8229
  const changes = arrayValue(structured.changes) ?? [];
7700
8230
  return changes.filter(isJsonObject).map((change) => stringValue(change.path) ?? "").filter((path2) => path2.trim() !== "");
@@ -8177,6 +8707,7 @@ var init_channelSession = __esm({
8177
8707
  "use strict";
8178
8708
  init_channelSessionSupport();
8179
8709
  init_commanderAttachments();
8710
+ init_codexNotificationDiagnostics();
8180
8711
  init_codexRuntimeOptions();
8181
8712
  init_codexOutput();
8182
8713
  init_codexSessionLog();
@@ -8898,7 +9429,7 @@ function createRunnerLogger(logFile, consoleReporter) {
8898
9429
  `,
8899
9430
  "utf8"
8900
9431
  );
8901
- consoleReporter?.(event, redacted);
9432
+ consoleReporter?.(event, runnerConsolePayload(redacted, payload));
8902
9433
  });
8903
9434
  Object.defineProperty(logger, "close", {
8904
9435
  value: () => closeStream(stream)
@@ -8935,7 +9466,7 @@ function redactForCliLog(value) {
8935
9466
  return redactObject(value);
8936
9467
  }
8937
9468
  function redactValue(value, key) {
8938
- if (sensitiveKey(key)) {
9469
+ if (sensitiveKey2(key)) {
8939
9470
  return sensitiveMarker;
8940
9471
  }
8941
9472
  if (typeof value === "string") {
@@ -8953,12 +9484,19 @@ function redactObject(value) {
8953
9484
  const headerName = typeof value.name === "string" ? value.name.toLowerCase() : void 0;
8954
9485
  const shouldRedactHeaderValue = headerName === "authorization" || headerName === "cookie" || headerName === "set-cookie";
8955
9486
  return Object.fromEntries(
8956
- Object.entries(value).map(([key, entry]) => [
9487
+ Object.entries(value).filter(([key]) => key !== "runner_console").map(([key, entry]) => [
8957
9488
  key,
8958
9489
  shouldRedactHeaderValue && key === "value" ? sensitiveMarker : redactValue(entry, key)
8959
9490
  ])
8960
9491
  );
8961
9492
  }
9493
+ function runnerConsolePayload(redacted, original) {
9494
+ const consoleFields = objectValue3(original.runner_console);
9495
+ return consoleFields === void 0 ? redacted : { ...redacted, ...redactForCliLog(consoleFields) };
9496
+ }
9497
+ function objectValue3(value) {
9498
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
9499
+ }
8962
9500
  function redactArgs(args) {
8963
9501
  let redactNext = false;
8964
9502
  return args.map((arg) => {
@@ -8985,7 +9523,7 @@ function splitArgAssignment(value) {
8985
9523
  const index = value.indexOf("=");
8986
9524
  return index === -1 ? [value, void 0] : [value.slice(0, index), value.slice(index + 1)];
8987
9525
  }
8988
- function sensitiveKey(key) {
9526
+ function sensitiveKey2(key) {
8989
9527
  if (key === void 0) {
8990
9528
  return false;
8991
9529
  }
@@ -9201,7 +9739,9 @@ var init_mcpConfig = __esm({
9201
9739
  });
9202
9740
 
9203
9741
  // src/codexAppServer.ts
9204
- import { spawn } from "node:child_process";
9742
+ import {
9743
+ spawn
9744
+ } from "node:child_process";
9205
9745
  import { createServer } from "node:net";
9206
9746
  async function chooseLoopbackPort() {
9207
9747
  return new Promise((resolve11, reject) => {
@@ -9226,9 +9766,15 @@ async function chooseLoopbackPort() {
9226
9766
  });
9227
9767
  }
9228
9768
  async function startCodexAppServer(codexBin, cwd, options = {}) {
9769
+ return startCodexAppServerAttempt(codexBin, cwd, options, 1);
9770
+ }
9771
+ async function startCodexAppServerAttempt(codexBin, cwd, options, attempt) {
9229
9772
  const port = await chooseLoopbackPort();
9230
9773
  const url = `ws://127.0.0.1:${port}`;
9231
9774
  const args = codexAppServerArgs(url, options);
9775
+ let stderrText = "";
9776
+ const configuredStdio = codexAppServerStdio(process.stdout.isTTY === true);
9777
+ const stdio = [configuredStdio[0], configuredStdio[1], "pipe"];
9232
9778
  writeCliAuditEvent("process.spawn", {
9233
9779
  command: codexBin,
9234
9780
  args,
@@ -9238,9 +9784,20 @@ async function startCodexAppServer(codexBin, cwd, options = {}) {
9238
9784
  const child = spawn(codexBin, args, {
9239
9785
  cwd,
9240
9786
  env: codexAppServerEnv(options.env),
9241
- stdio: ["ignore", "inherit", "inherit"],
9787
+ stdio,
9242
9788
  detached: true
9243
9789
  });
9790
+ if (stdio[1] === "pipe") {
9791
+ drainCodexAppServerOutput(child, "stdout");
9792
+ }
9793
+ if (stdio[2] === "pipe") {
9794
+ drainCodexAppServerOutput(child, "stderr", (chunk) => {
9795
+ stderrText = boundedText(`${stderrText}${chunk}`);
9796
+ if (configuredStdio[2] === "inherit") {
9797
+ process.stderr.write(chunk);
9798
+ }
9799
+ });
9800
+ }
9244
9801
  const stop = () => stopCodexAppServerProcess(child);
9245
9802
  writeCliAuditEvent("process.spawned", {
9246
9803
  command: codexBin,
@@ -9270,10 +9827,49 @@ async function startCodexAppServer(codexBin, cwd, options = {}) {
9270
9827
  await waitForReadyz(url, child);
9271
9828
  } catch (error) {
9272
9829
  stop();
9830
+ if (shouldRetryCodexAppServerBindFailure(error, stderrText, attempt)) {
9831
+ writeCliAuditEvent("process.spawn.retry", {
9832
+ command: codexBin,
9833
+ cwd,
9834
+ attempt,
9835
+ reason: "loopback_bind_in_use",
9836
+ purpose: "codex.app_server"
9837
+ });
9838
+ return startCodexAppServerAttempt(codexBin, cwd, options, attempt + 1);
9839
+ }
9273
9840
  throw error;
9274
9841
  }
9275
9842
  return { url, process: child, stop };
9276
9843
  }
9844
+ function boundedText(value) {
9845
+ const maxBytes = 12e3;
9846
+ return value.length > maxBytes ? value.slice(value.length - maxBytes) : value;
9847
+ }
9848
+ function shouldRetryCodexAppServerBindFailure(error, stderrText, attempt) {
9849
+ const maxAttempts = 3;
9850
+ const message = error instanceof Error ? error.message : String(error);
9851
+ return attempt < maxAttempts && message.includes("codex app-server exited before readyz") && stderrText.includes("EADDRINUSE") && stderrText.includes("listen");
9852
+ }
9853
+ function codexAppServerStdio(stdoutIsTty) {
9854
+ return stdoutIsTty ? ["ignore", "pipe", "pipe"] : ["ignore", "inherit", "inherit"];
9855
+ }
9856
+ function drainCodexAppServerOutput(child, streamName, onText = void 0) {
9857
+ const stream = child[streamName];
9858
+ if (stream === null) {
9859
+ return;
9860
+ }
9861
+ stream.on("data", (chunk) => {
9862
+ const text2 = chunk.toString();
9863
+ onText?.(text2);
9864
+ writeCliAuditEvent("process.output", {
9865
+ pid: child.pid,
9866
+ purpose: "codex.app_server",
9867
+ stream: streamName,
9868
+ chars: text2.length,
9869
+ output: text2
9870
+ });
9871
+ });
9872
+ }
9277
9873
  function codexAppServerEnv(overrides) {
9278
9874
  const env = { ...process.env };
9279
9875
  for (const key of blockedCodexAppServerEnvKeys) {
@@ -9614,9 +10210,9 @@ function codexConfigWithTrustedProject(config, projectPath) {
9614
10210
  trust_level = "trusted"
9615
10211
  `;
9616
10212
  }
9617
- const table = config.slice(range.start, range.end);
10213
+ const table2 = config.slice(range.start, range.end);
9618
10214
  const trustLine = /^(\s*trust_level\s*=\s*).*(\r?)$/m;
9619
- const nextTable = trustLine.test(table) ? table.replace(trustLine, '$1"trusted"$2') : `${trimTrailingNewlines(table)}
10215
+ const nextTable = trustLine.test(table2) ? table2.replace(trustLine, '$1"trusted"$2') : `${trimTrailingNewlines(table2)}
9620
10216
  trust_level = "trusted"
9621
10217
  `;
9622
10218
  return `${config.slice(0, range.start)}${nextTable}${config.slice(range.end)}`;
@@ -9648,6 +10244,129 @@ var init_codexProjectTrust = __esm({
9648
10244
  }
9649
10245
  });
9650
10246
 
10247
+ // src/codexNotificationConsoleStats.ts
10248
+ function codexNotificationConsoleLogPayload(method, params) {
10249
+ return {
10250
+ method,
10251
+ metadata: extractCodexIds(params),
10252
+ ...codexNotificationConsoleStats(method, params)
10253
+ };
10254
+ }
10255
+ function codexNotificationConsoleStats(method, params) {
10256
+ switch (method) {
10257
+ case "thread/tokenUsage/updated":
10258
+ return {
10259
+ token_usage_summary: tokenUsageSummary(params) ?? "seen (no details)"
10260
+ };
10261
+ case "account/rateLimits/updated":
10262
+ return {
10263
+ rate_limit_summary: rateLimitSummary(params) ?? "seen (no details)"
10264
+ };
10265
+ case "warning":
10266
+ case "error":
10267
+ return codexNotificationDiagnosticDetails(method, params);
10268
+ default:
10269
+ return {};
10270
+ }
10271
+ }
10272
+ function tokenUsageSummary(params) {
10273
+ const usage = objectValue4(params.tokenUsage) ?? objectValue4(params.token_usage) ?? objectValue4(params.usage) ?? params;
10274
+ const total = tokenUsageBreakdownSummary(objectValue4(usage.total)) ?? tokenUsageBreakdownSummary(objectValue4(usage.totalUsage)) ?? tokenUsageBreakdownSummary(objectValue4(usage.total_usage)) ?? tokenUsageBreakdownSummary(objectValue4(params.totalTokenUsage)) ?? tokenUsageBreakdownSummary(objectValue4(params.total_token_usage)) ?? tokenUsageBreakdownSummary(usage);
10275
+ const last = tokenUsageBreakdownSummary(objectValue4(usage.last)) ?? tokenUsageBreakdownSummary(objectValue4(usage.lastUsage)) ?? tokenUsageBreakdownSummary(objectValue4(usage.last_usage)) ?? tokenUsageBreakdownSummary(objectValue4(params.lastTokenUsage)) ?? tokenUsageBreakdownSummary(objectValue4(params.last_token_usage));
10276
+ const summary = [
10277
+ total === void 0 ? void 0 : `total ${total}`,
10278
+ last === void 0 ? void 0 : `last ${last}`
10279
+ ].filter((value) => value !== void 0);
10280
+ return summary.length === 0 ? void 0 : summary.join(" | ");
10281
+ }
10282
+ function tokenUsageBreakdownSummary(breakdown) {
10283
+ if (breakdown === void 0) {
10284
+ return void 0;
10285
+ }
10286
+ const inputDetails = objectValue4(breakdown.inputTokensDetails) ?? objectValue4(breakdown.input_tokens_details);
10287
+ const outputDetails = objectValue4(breakdown.outputTokensDetails) ?? objectValue4(breakdown.output_tokens_details);
10288
+ const input = integerValue2(breakdown.inputTokens) ?? integerValue2(breakdown.input_tokens) ?? integerValue2(breakdown.promptTokens) ?? integerValue2(breakdown.prompt_tokens);
10289
+ const output = integerValue2(breakdown.outputTokens) ?? integerValue2(breakdown.output_tokens) ?? integerValue2(breakdown.completionTokens) ?? integerValue2(breakdown.completion_tokens);
10290
+ const total = integerValue2(breakdown.totalTokens) ?? integerValue2(breakdown.total_tokens);
10291
+ const cached = integerValue2(breakdown.cachedInputTokens) ?? integerValue2(breakdown.cached_input_tokens) ?? integerValue2(breakdown.cacheReadInputTokens) ?? integerValue2(breakdown.cache_read_input_tokens) ?? integerValue2(inputDetails?.cachedTokens) ?? integerValue2(inputDetails?.cached_tokens);
10292
+ const reasoning = integerValue2(breakdown.reasoningOutputTokens) ?? integerValue2(breakdown.reasoning_output_tokens) ?? integerValue2(breakdown.reasoningTokens) ?? integerValue2(breakdown.reasoning_tokens) ?? integerValue2(outputDetails?.reasoningTokens) ?? integerValue2(outputDetails?.reasoning_tokens);
10293
+ const values = [
10294
+ input === void 0 ? void 0 : `${input} in`,
10295
+ output === void 0 ? void 0 : `${output} out`,
10296
+ total === void 0 ? void 0 : `${total} total`,
10297
+ cached === void 0 ? void 0 : `${cached} cached`,
10298
+ reasoning === void 0 ? void 0 : `${reasoning} reasoning`
10299
+ ].filter((value) => value !== void 0);
10300
+ return values.length === 0 ? void 0 : values.join(" / ");
10301
+ }
10302
+ function rateLimitSummary(params) {
10303
+ const rateLimits = objectValue4(params.rateLimits) ?? objectValue4(params.rate_limits) ?? objectValue4(params.limits) ?? params;
10304
+ const direct = rateLimitBucketSummary(rateLimits, void 0);
10305
+ const children = Object.entries(rateLimits).flatMap(([name, value]) => {
10306
+ const bucket = objectValue4(value);
10307
+ return bucket === void 0 ? [] : [rateLimitBucketSummary(bucket, name)];
10308
+ });
10309
+ const summaries = [direct, ...children].filter(
10310
+ (value) => value !== void 0
10311
+ );
10312
+ return summaries.length === 0 ? void 0 : summaries.join(" | ");
10313
+ }
10314
+ function rateLimitBucketSummary(bucket, fallbackName) {
10315
+ const name = stringValue3(bucket.limitName) ?? stringValue3(bucket.limit_name) ?? stringValue3(bucket.name) ?? stringValue3(bucket.type) ?? fallbackName;
10316
+ const plan = stringValue3(bucket.planType) ?? stringValue3(bucket.plan_type) ?? stringValue3(bucket.plan);
10317
+ const remaining = integerValue2(bucket.remaining) ?? integerValue2(bucket.remainingRequests) ?? integerValue2(bucket.remaining_requests) ?? integerValue2(bucket.remainingTokens) ?? integerValue2(bucket.remaining_tokens);
10318
+ const limit = integerValue2(bucket.limit) ?? integerValue2(bucket.requestLimit) ?? integerValue2(bucket.request_limit) ?? integerValue2(bucket.limitRequests) ?? integerValue2(bucket.limit_requests) ?? integerValue2(bucket.tokenLimit) ?? integerValue2(bucket.token_limit) ?? integerValue2(bucket.limitTokens) ?? integerValue2(bucket.limit_tokens);
10319
+ const reset = stringValue3(bucket.resetAt) ?? stringValue3(bucket.reset_at) ?? stringValue3(bucket.resetsAt) ?? stringValue3(bucket.resets_at) ?? unixTimestampValue(bucket.resetsAt) ?? unixTimestampValue(bucket.resets_at) ?? secondsValue(bucket.resetAfterSeconds) ?? secondsValue(bucket.reset_after_seconds) ?? secondsValue(bucket.retryAfterSeconds) ?? secondsValue(bucket.retry_after_seconds);
10320
+ const usedPercent = integerValue2(bucket.usedPercent) ?? integerValue2(bucket.used_percent);
10321
+ const windowDurationMins = integerValue2(bucket.windowDurationMins) ?? integerValue2(bucket.window_duration_mins);
10322
+ const label = [name, plan].filter((value) => value !== void 0).join(" ");
10323
+ const prefix = label === "" ? void 0 : label;
10324
+ const remainingSummary = remaining === void 0 ? void 0 : limit === void 0 ? `${remaining} remaining` : `${remaining}/${limit} remaining`;
10325
+ const fields = [
10326
+ remainingSummary,
10327
+ usedPercent === void 0 ? void 0 : `${usedPercent}% used`,
10328
+ windowDurationMins === void 0 ? void 0 : `${windowDurationMins}m window`,
10329
+ reset === void 0 ? void 0 : `reset ${reset}`
10330
+ ].filter((value) => value !== void 0);
10331
+ switch (fields.length) {
10332
+ case 0:
10333
+ return prefix === void 0 ? void 0 : `${prefix} seen (no details)`;
10334
+ default:
10335
+ return prefix === void 0 ? fields.join(" ") : `${prefix} ${fields.join(" ")}`;
10336
+ }
10337
+ }
10338
+ function secondsValue(value) {
10339
+ const seconds = integerValue2(value);
10340
+ return seconds === void 0 ? void 0 : `${seconds}s`;
10341
+ }
10342
+ function unixTimestampValue(value) {
10343
+ const seconds = integerValue2(value);
10344
+ return seconds === void 0 ? void 0 : new Date(seconds * 1e3).toISOString();
10345
+ }
10346
+ function objectValue4(value) {
10347
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
10348
+ }
10349
+ function stringValue3(value) {
10350
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
10351
+ }
10352
+ function integerValue2(value) {
10353
+ if (typeof value === "number" && Number.isSafeInteger(value)) {
10354
+ return value;
10355
+ }
10356
+ if (typeof value !== "string") {
10357
+ return void 0;
10358
+ }
10359
+ const parsed = Number.parseInt(value, 10);
10360
+ return Number.isSafeInteger(parsed) && parsed.toString() === value.trim() ? parsed : void 0;
10361
+ }
10362
+ var init_codexNotificationConsoleStats = __esm({
10363
+ "src/codexNotificationConsoleStats.ts"() {
10364
+ "use strict";
10365
+ init_codexNotificationDiagnostics();
10366
+ init_protocol();
10367
+ }
10368
+ });
10369
+
9651
10370
  // src/localCapabilities.ts
9652
10371
  import { realpathSync as realpathSync2 } from "node:fs";
9653
10372
  import { homedir as homedir6 } from "node:os";
@@ -9690,13 +10409,17 @@ function assertConfiguredAllowedCwds(paths) {
9690
10409
  }
9691
10410
  function expandUserPath(pathValue) {
9692
10411
  if (pathValue === "~") {
9693
- return homedir6();
10412
+ return currentHomeDirectory();
9694
10413
  }
9695
10414
  if (pathValue.startsWith("~/")) {
9696
- return resolve3(homedir6(), pathValue.slice(2));
10415
+ return resolve3(currentHomeDirectory(), pathValue.slice(2));
9697
10416
  }
9698
10417
  return pathValue;
9699
10418
  }
10419
+ function currentHomeDirectory() {
10420
+ const configuredHome = process.env.HOME;
10421
+ return configuredHome === void 0 || configuredHome.trim() === "" ? homedir6() : configuredHome;
10422
+ }
9700
10423
  function resolveAllowedCwd(requestedCwd, allowedRoots) {
9701
10424
  if (requestedCwd === void 0 || requestedCwd.trim() === "") {
9702
10425
  return { ok: false, reason: "missing_cwd" };
@@ -13116,7 +13839,8 @@ var init_dependencyStatus = __esm({
13116
13839
  });
13117
13840
 
13118
13841
  // src/phoenix.ts
13119
- function phoenixWebsocketUrl(baseUrl, token) {
13842
+ import { Buffer as Buffer3 } from "node:buffer";
13843
+ function phoenixWebsocketUrl(baseUrl) {
13120
13844
  const parsed = new URL(baseUrl);
13121
13845
  switch (parsed.protocol) {
13122
13846
  case "http:":
@@ -13127,11 +13851,18 @@ function phoenixWebsocketUrl(baseUrl, token) {
13127
13851
  break;
13128
13852
  }
13129
13853
  parsed.pathname = parsed.pathname.replace(/\/$/, "") + "/socket/websocket";
13130
- parsed.searchParams.set("token", token);
13131
13854
  parsed.searchParams.set("vsn", "2.0.0");
13132
13855
  return parsed.toString();
13133
13856
  }
13134
- async function connectPhoenixClient(baseUrl, token, socketFactory = (url) => new WebSocket(url), options = {}) {
13857
+ function phoenixWebsocketProtocols(token) {
13858
+ return [
13859
+ `${phoenixAuthTokenProtocolPrefix}${Buffer3.from(token, "utf8").toString(
13860
+ "base64url"
13861
+ )}`,
13862
+ "phoenix"
13863
+ ];
13864
+ }
13865
+ async function connectPhoenixClient(baseUrl, token, socketFactory = (url, protocols) => new WebSocket(url, protocols), options = {}) {
13135
13866
  const pending = /* @__PURE__ */ new Map();
13136
13867
  const joins = /* @__PURE__ */ new Map();
13137
13868
  const controlCallbacks = /* @__PURE__ */ new Set();
@@ -13270,7 +14001,10 @@ async function connectPhoenixClient(baseUrl, token, socketFactory = (url) => new
13270
14001
  if (state.closed) {
13271
14002
  return;
13272
14003
  }
13273
- const websocket = socketFactory(phoenixWebsocketUrl(baseUrl, token));
14004
+ const websocket = socketFactory(
14005
+ phoenixWebsocketUrl(baseUrl),
14006
+ phoenixWebsocketProtocols(token)
14007
+ );
13274
14008
  state.websocket = websocket;
13275
14009
  websocket.addEventListener("message", handleMessage);
13276
14010
  websocket.addEventListener(
@@ -13408,12 +14142,13 @@ function waitForOpen2(websocket) {
13408
14142
  );
13409
14143
  });
13410
14144
  }
13411
- var defaultPushTimeoutMs;
14145
+ var defaultPushTimeoutMs, phoenixAuthTokenProtocolPrefix;
13412
14146
  var init_phoenix = __esm({
13413
14147
  "src/phoenix.ts"() {
13414
14148
  "use strict";
13415
14149
  init_protocol();
13416
14150
  defaultPushTimeoutMs = 3e4;
14151
+ phoenixAuthTokenProtocolPrefix = "base64url.bearer.phx.";
13417
14152
  }
13418
14153
  });
13419
14154
 
@@ -13422,7 +14157,7 @@ var linzumiCliVersion, linzumiCliVersionText;
13422
14157
  var init_version = __esm({
13423
14158
  "src/version.ts"() {
13424
14159
  "use strict";
13425
- linzumiCliVersion = "0.0.72-beta";
14160
+ linzumiCliVersion = "0.0.74-beta";
13426
14161
  linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
13427
14162
  }
13428
14163
  });
@@ -13694,13 +14429,192 @@ var init_runnerLock = __esm({
13694
14429
  });
13695
14430
 
13696
14431
  // src/runnerConsoleReporter.ts
14432
+ import blessed from "blessed";
13697
14433
  function reportRunnerConsoleEvent(event, payload) {
14434
+ if (shouldRenderDashboard()) {
14435
+ const tui = initializeDashboardTui(dashboardState);
14436
+ updateRunnerConsoleDashboard(dashboardState, event, payload, Date.now());
14437
+ if (tui !== void 0) {
14438
+ tui.render(Date.now());
14439
+ return;
14440
+ }
14441
+ initializeDashboardKeyboard(dashboardState);
14442
+ initializeDashboardTicker(dashboardState);
14443
+ const screen = renderRunnerConsoleDashboard(dashboardState, Date.now());
14444
+ if (screen !== void 0) {
14445
+ redrawScreen(screen);
14446
+ return;
14447
+ }
14448
+ }
13698
14449
  const line = formatRunnerConsoleEvent(event, payload);
13699
14450
  if (line !== void 0) {
13700
14451
  process.stdout.write(`${line}
13701
14452
  `);
13702
14453
  }
13703
14454
  }
14455
+ function shouldRenderDashboard() {
14456
+ switch (process.env.LINZUMI_RUNNER_CONSOLE) {
14457
+ case "dashboard":
14458
+ return true;
14459
+ case "lines":
14460
+ return false;
14461
+ default:
14462
+ return process.stdout.isTTY === true;
14463
+ }
14464
+ }
14465
+ function updateRunnerConsoleDashboard(state, event, payload, nowMs) {
14466
+ state.lastUpdateAtMs = nowMs;
14467
+ const previousJobKey = latestDashboardJob(state)?.key;
14468
+ switch (event) {
14469
+ case "kandan.message_queued":
14470
+ case "codex.turn_starting": {
14471
+ const job = dashboardJobForPayload(state, payload, nowMs);
14472
+ job.linzumiThreadId = stringValue4(payload.linzumi_thread_id) ?? job.linzumiThreadId;
14473
+ job.linzumiThreadTitle = dashboardLinzumiThreadTitle(payload) ?? job.linzumiThreadTitle;
14474
+ job.codexThreadId = stringValue4(payload.codex_thread_id) ?? job.codexThreadId;
14475
+ job.lastIncomingPreview = stringValue4(payload.body_preview) ?? job.lastIncomingPreview;
14476
+ job.lastIncomingAtMs = numberValue(payload.message_received_at_ms) ?? job.lastIncomingAtMs;
14477
+ job.queueDepth = numberValue(payload.queue_depth) ?? job.queueDepth;
14478
+ job.eventType = dashboardEventLabel(event);
14479
+ job.updatedAtMs = nowMs;
14480
+ break;
14481
+ }
14482
+ case "codex.notification": {
14483
+ const tokenUsage = stringValue4(payload.token_usage_summary);
14484
+ const rateLimit = stringValue4(payload.rate_limit_summary);
14485
+ if (tokenUsage !== void 0) {
14486
+ state.tokenUsage = tokenUsage;
14487
+ }
14488
+ if (rateLimit !== void 0) {
14489
+ state.rateLimit = mergeRateLimitSummary(state.rateLimit, rateLimit);
14490
+ }
14491
+ const method = stringValue4(payload.method);
14492
+ const metadata = objectValue5(payload.metadata);
14493
+ const metadataHasJobSignal = stringValue4(metadata?.threadId) !== void 0 || stringValue4(metadata?.turnId) !== void 0 || stringValue4(metadata?.itemId) !== void 0;
14494
+ if (!metadataHasJobSignal) {
14495
+ break;
14496
+ }
14497
+ const codexThreadId = stringValue4(metadata?.threadId);
14498
+ const job = codexThreadId === void 0 ? latestDashboardJob(state) : dashboardJobForKey(state, `codex:${codexThreadId}`, nowMs);
14499
+ if (job !== void 0) {
14500
+ job.codexThreadId = codexThreadId ?? job.codexThreadId;
14501
+ job.tokenUsage = tokenUsage ?? job.tokenUsage;
14502
+ job.latestCodexEventId = codexEventId(payload);
14503
+ job.eventType = method ?? event;
14504
+ job.turnId = stringValue4(metadata?.turnId) ?? job.turnId;
14505
+ job.updatedAtMs = nowMs;
14506
+ }
14507
+ break;
14508
+ }
14509
+ case "kandan.codex_output_forwarded":
14510
+ case "kandan.codex_delta_forwarded":
14511
+ case "kandan.codex_reasoning_delta_forwarded":
14512
+ case "kandan.codex_command_output_forwarded":
14513
+ case "kandan.codex_file_change_forwarded":
14514
+ case "kandan.codex_web_search_progress_forwarded": {
14515
+ const job = dashboardJobForPayload(state, payload, nowMs);
14516
+ job.latestCodexEventId = stringValue4(payload.item_key) ?? job.latestCodexEventId;
14517
+ job.eventType = codexOutputDashboardLabel(event, payload);
14518
+ job.turnId = stringValue4(payload.turn_id) ?? job.turnId;
14519
+ if (stringValue4(payload.structured_kind) === "codex_assistant_message") {
14520
+ job.lastAssistantPreview = stringValue4(payload.body_preview) ?? job.lastAssistantPreview;
14521
+ job.lastAssistantAtMs = nowMs;
14522
+ }
14523
+ job.updatedAtMs = nowMs;
14524
+ break;
14525
+ }
14526
+ default:
14527
+ break;
14528
+ }
14529
+ rememberRawLine(state, event, payload, previousJobKey);
14530
+ }
14531
+ function renderRunnerConsoleDashboard(state, nowMs) {
14532
+ if (state.jobs.size === 0 && state.tokenUsage === void 0 && state.rateLimit === void 0) {
14533
+ return void 0;
14534
+ }
14535
+ const jobs = dashboardJobs(state);
14536
+ switch (state.mode.type) {
14537
+ case "table":
14538
+ return renderDashboardTable(state, jobs, nowMs);
14539
+ case "raw_all":
14540
+ return renderRawDashboard(
14541
+ state,
14542
+ "Raw Stream",
14543
+ void 0,
14544
+ "esc: table",
14545
+ state.rawLines,
14546
+ nowMs
14547
+ );
14548
+ case "raw_job": {
14549
+ const jobKey = state.mode.jobKey;
14550
+ const job = state.jobs.get(jobKey);
14551
+ const title = `Raw Stream: ${shortId(job?.linzumiThreadId)} / ${agentId(job?.codexThreadId)}`;
14552
+ return renderRawDashboard(
14553
+ state,
14554
+ title,
14555
+ job?.tokenUsage,
14556
+ "esc: table",
14557
+ state.rawLines.filter((line) => line.jobKey === jobKey),
14558
+ nowMs
14559
+ );
14560
+ }
14561
+ }
14562
+ }
14563
+ function renderDashboardTable(state, jobs, nowMs) {
14564
+ const model = dashboardTableModel(state, jobs, nowMs);
14565
+ return [
14566
+ "Linzumi Commander",
14567
+ "",
14568
+ `Jobs: ${jobs.length} Last update: ${timeAgo(state.lastUpdateAtMs, nowMs)}`,
14569
+ `Overall Token Usage: ${state.tokenUsage ?? "?"}`,
14570
+ `Account rate limits: ${state.rateLimit ?? "?"}`,
14571
+ "Controls: up/down select | enter raw job | r raw stream | esc table",
14572
+ "",
14573
+ table(model.rows),
14574
+ ""
14575
+ ].join("\n");
14576
+ }
14577
+ function dashboardTableModel(state, jobs, nowMs) {
14578
+ const selectedJobKey = selectedDashboardJobKey(state, jobs);
14579
+ const rows = jobs.length === 0 ? [dashboardTableHeader(), emptyJobRow()] : [
14580
+ dashboardTableHeader(),
14581
+ ...jobs.map((job) => jobRow(job, selectedJobKey, nowMs))
14582
+ ];
14583
+ return {
14584
+ jobs,
14585
+ selectedJobKey,
14586
+ rows
14587
+ };
14588
+ }
14589
+ function dashboardTableHeader() {
14590
+ return [
14591
+ "",
14592
+ "Linzumi thread",
14593
+ "Agent",
14594
+ "Last user message",
14595
+ "Ago",
14596
+ "Last assistant reply",
14597
+ "Ago",
14598
+ "Latest event",
14599
+ "Event type"
14600
+ ];
14601
+ }
14602
+ function mergeRateLimitSummary(previous, next) {
14603
+ if (previous === void 0) {
14604
+ return next;
14605
+ }
14606
+ const buckets = /* @__PURE__ */ new Map();
14607
+ for (const summary of previous.split(" | ")) {
14608
+ buckets.set(rateLimitSummaryKey(summary), summary);
14609
+ }
14610
+ for (const summary of next.split(" | ")) {
14611
+ buckets.set(rateLimitSummaryKey(summary), summary);
14612
+ }
14613
+ return Array.from(buckets.values()).join(" | ");
14614
+ }
14615
+ function rateLimitSummaryKey(summary) {
14616
+ return summary.split(" ", 1)[0] ?? summary;
14617
+ }
13704
14618
  function formatRunnerConsoleEvent(event, payload) {
13705
14619
  switch (event) {
13706
14620
  case "runner.instance_started":
@@ -13719,13 +14633,20 @@ function formatRunnerConsoleEvent(event, payload) {
13719
14633
  case "kandan.chat_event_failed":
13720
14634
  return `Incoming message handling failed: seq=${text(payload.seq)} reason=${text(payload.message)}`;
13721
14635
  case "kandan.reconnected":
13722
- return `Kandan reconnected: codex_session=${text(payload.codex_thread_id)} cursor=${text(payload.min_seq)}`;
14636
+ return `Linzumi reconnected: codex_session=${text(payload.codex_thread_id)} cursor=${text(payload.min_seq)}`;
13723
14637
  case "codex.turn_starting":
13724
14638
  return `Incoming message from ${sender(payload)}: forwarding to Codex session ${text(payload.codex_thread_id)} seq=${text(payload.queued_seq)}`;
13725
14639
  case "codex.turn_started":
13726
14640
  return `Codex turn started: id=${text(payload.turn_id)}`;
13727
- case "codex.notification":
13728
- return `Codex event [id=${codexEventId(payload)}]: ${text(payload.method)}`;
14641
+ case "codex.notification": {
14642
+ const summary = [
14643
+ stringValue4(payload.token_usage_summary),
14644
+ stringValue4(payload.rate_limit_summary),
14645
+ stringValue4(payload.diagnostic_summary)
14646
+ ].filter((value) => value !== void 0).join(" | ");
14647
+ const suffix = summary === "" ? "" : ` ${summary}`;
14648
+ return `Codex event [id=${codexEventId(payload)}]: ${text(payload.method)}${suffix}`;
14649
+ }
13729
14650
  case "codex.turn_completed":
13730
14651
  return `Codex turn completed: id=${text(payload.turn_id)} outputs=${text(payload.output_count)}`;
13731
14652
  case "kandan.codex_output_forwarded":
@@ -13758,11 +14679,11 @@ function connectedRunnerMessage(payload) {
13758
14679
  ].filter((line) => line !== void 0).join("\n");
13759
14680
  }
13760
14681
  function optionalLine(label, value) {
13761
- const normalized = stringValue2(value) ?? numberValue(value)?.toString();
14682
+ const normalized = stringValue4(value) ?? numberValue(value)?.toString();
13762
14683
  return normalized === void 0 ? void 0 : `${label}: ${normalized}`;
13763
14684
  }
13764
14685
  function optionalField(label, value) {
13765
- const normalized = stringValue2(value) ?? numberValue(value)?.toString();
14686
+ const normalized = stringValue4(value) ?? numberValue(value)?.toString();
13766
14687
  return normalized === void 0 ? void 0 : `${label}=${normalized}`;
13767
14688
  }
13768
14689
  function ignoredMessage(payload) {
@@ -13786,17 +14707,17 @@ function replacementLines(value) {
13786
14707
  return [];
13787
14708
  }
13788
14709
  const record = entry;
13789
- const runnerId = stringValue2(record.runnerId);
14710
+ const runnerId = stringValue4(record.runnerId);
13790
14711
  if (runnerId === void 0) {
13791
14712
  return [];
13792
14713
  }
13793
- const version = stringValue2(record.version);
14714
+ const version = stringValue4(record.version);
13794
14715
  const suffix = version === void 0 ? "" : ` (CLI ${version})`;
13795
14716
  return [`Replaced older runner from this machine: ${runnerId}${suffix}`];
13796
14717
  });
13797
14718
  }
13798
14719
  function sender(payload) {
13799
- const slug = stringValue2(payload.actor_slug);
14720
+ const slug = stringValue4(payload.actor_slug);
13800
14721
  const userId = numberValue(payload.actor_user_id);
13801
14722
  if (slug !== void 0 && userId !== void 0) {
13802
14723
  return `${slug}#${userId}`;
@@ -13804,7 +14725,7 @@ function sender(payload) {
13804
14725
  return slug ?? (userId === void 0 ? "unknown" : `user#${userId}`);
13805
14726
  }
13806
14727
  function codexOutputLabel(payload) {
13807
- const kind = stringValue2(payload.structured_kind) ?? "output";
14728
+ const kind = stringValue4(payload.structured_kind) ?? "output";
13808
14729
  switch (kind) {
13809
14730
  case "codex_assistant_message":
13810
14731
  return "assistant_message";
@@ -13830,22 +14751,693 @@ function codexEventId(payload) {
13830
14751
  const metadata = payload.metadata;
13831
14752
  if (typeof metadata === "object" && metadata !== null && !Array.isArray(metadata)) {
13832
14753
  const record = metadata;
13833
- return stringValue2(record.turnId) ?? stringValue2(record.itemId) ?? stringValue2(record.threadId) ?? "?";
14754
+ return stringValue4(record.turnId) ?? stringValue4(record.itemId) ?? stringValue4(record.threadId) ?? "?";
13834
14755
  }
13835
14756
  return "?";
13836
14757
  }
14758
+ function rememberRawLine(state, event, payload, previousJobKey) {
14759
+ const line = formatRunnerConsoleEvent(event, payload);
14760
+ if (line === void 0) {
14761
+ return;
14762
+ }
14763
+ const jobKey = dashboardRawLineJobKey(state, payload, previousJobKey);
14764
+ state.rawLines.push({ jobKey, line });
14765
+ if (state.rawLines.length > maxRawLines) {
14766
+ state.rawLines.splice(0, state.rawLines.length - maxRawLines);
14767
+ }
14768
+ }
14769
+ function dashboardRawLineJobKey(state, payload, previousJobKey) {
14770
+ const linzumiThreadId = stringValue4(payload.linzumi_thread_id);
14771
+ const codexThreadId = stringValue4(payload.codex_thread_id);
14772
+ if (linzumiThreadId !== void 0) {
14773
+ return `linzumi:${linzumiThreadId}`;
14774
+ }
14775
+ if (codexThreadId !== void 0) {
14776
+ return Array.from(state.jobs.values()).find(
14777
+ (job) => job.codexThreadId === codexThreadId
14778
+ )?.key;
14779
+ }
14780
+ const metadata = objectValue5(payload.metadata);
14781
+ const metadataCodexThreadId = stringValue4(metadata?.threadId);
14782
+ if (metadataCodexThreadId !== void 0) {
14783
+ return Array.from(state.jobs.values()).find(
14784
+ (job) => job.codexThreadId === metadataCodexThreadId
14785
+ )?.key;
14786
+ }
14787
+ return previousJobKey;
14788
+ }
14789
+ function selectedDashboardJobKey(state, jobs) {
14790
+ if (jobs.length === 0) {
14791
+ state.selectedJobKey = void 0;
14792
+ return void 0;
14793
+ }
14794
+ if (state.selectedJobKey === void 0 || jobs.every((job) => job.key !== state.selectedJobKey)) {
14795
+ state.selectedJobKey = jobs[0]?.key;
14796
+ }
14797
+ return state.selectedJobKey;
14798
+ }
14799
+ function renderRawDashboard(state, title, tokenUsage, controls, lines, nowMs) {
14800
+ const visibleLines = lines.slice(-40).map((entry) => entry.line);
14801
+ return [
14802
+ "Linzumi Commander",
14803
+ "",
14804
+ `${title} Last update: ${timeAgo(state.lastUpdateAtMs, nowMs)}`,
14805
+ ...tokenUsage === void 0 ? [] : [`Token usage: ${tokenUsage}`],
14806
+ `Controls: ${controls}`,
14807
+ "",
14808
+ ...visibleLines.length === 0 ? ["No raw events yet."] : visibleLines,
14809
+ ""
14810
+ ].join("\n");
14811
+ }
14812
+ function rawDashboardModel(state, mode) {
14813
+ switch (mode.type) {
14814
+ case "raw_all":
14815
+ return {
14816
+ title: "Raw Stream",
14817
+ tokenUsage: void 0,
14818
+ lines: state.rawLines.map((entry) => entry.line)
14819
+ };
14820
+ case "raw_job": {
14821
+ const jobKey = mode.jobKey;
14822
+ const job = state.jobs.get(jobKey);
14823
+ return {
14824
+ title: `Raw Stream: ${shortId(job?.linzumiThreadId)} / ${agentId(job?.codexThreadId)}`,
14825
+ tokenUsage: job?.tokenUsage,
14826
+ lines: state.rawLines.filter((line) => line.jobKey === jobKey).map((entry) => entry.line)
14827
+ };
14828
+ }
14829
+ }
14830
+ }
14831
+ function dashboardJobs(state) {
14832
+ return Array.from(state.jobs.values()).sort(compareDashboardJobsByCreated);
14833
+ }
14834
+ function dashboardJobsByUpdatedAt(state) {
14835
+ return Array.from(state.jobs.values()).sort((left, right) => {
14836
+ const updated = right.updatedAtMs - left.updatedAtMs;
14837
+ if (updated !== 0) {
14838
+ return updated;
14839
+ }
14840
+ return compareDashboardJobsByCreated(left, right);
14841
+ });
14842
+ }
14843
+ function compareDashboardJobsByCreated(left, right) {
14844
+ const created = right.createdAtMs - left.createdAtMs;
14845
+ if (created !== 0) {
14846
+ return created;
14847
+ }
14848
+ const ordinal = right.createdOrdinal - left.createdOrdinal;
14849
+ if (ordinal !== 0) {
14850
+ return ordinal;
14851
+ }
14852
+ return left.key.localeCompare(right.key);
14853
+ }
14854
+ function nextDashboardJobOrdinal(state) {
14855
+ const ordinal = state.nextJobOrdinal;
14856
+ state.nextJobOrdinal = state.nextJobOrdinal + 1;
14857
+ return ordinal;
14858
+ }
14859
+ function selectDashboardJobAtVisibleIndex(state, index) {
14860
+ const jobs = dashboardJobs(state);
14861
+ const job = jobs[index];
14862
+ if (job !== void 0) {
14863
+ state.selectedJobKey = job.key;
14864
+ }
14865
+ }
14866
+ function moveSelection(state, direction) {
14867
+ const jobs = dashboardJobs(state);
14868
+ if (jobs.length === 0) {
14869
+ state.selectedJobKey = void 0;
14870
+ return;
14871
+ }
14872
+ const selectedKey = selectedDashboardJobKey(state, jobs);
14873
+ const currentIndex = Math.max(
14874
+ 0,
14875
+ jobs.findIndex((job) => job.key === selectedKey)
14876
+ );
14877
+ const nextIndex = Math.min(
14878
+ jobs.length - 1,
14879
+ Math.max(0, currentIndex + direction)
14880
+ );
14881
+ state.selectedJobKey = jobs[nextIndex]?.key;
14882
+ }
14883
+ function initializeDashboardKeyboard(state) {
14884
+ if (keyboardState.initialized || process.stdin.isTTY !== true || typeof process.stdin.setRawMode !== "function") {
14885
+ return;
14886
+ }
14887
+ keyboardState.initialized = true;
14888
+ process.stdin.setRawMode(true);
14889
+ process.stdin.resume();
14890
+ process.stdin.on("data", (chunk) => {
14891
+ handleDashboardKey(state, chunk.toString("utf8"));
14892
+ });
14893
+ }
14894
+ function initializeDashboardTicker(state) {
14895
+ if (tickerState.stop !== void 0) {
14896
+ return;
14897
+ }
14898
+ tickerState.stop = startRunnerConsoleDashboardTicker(state);
14899
+ }
14900
+ function initializeDashboardTui(state) {
14901
+ if (tuiState.dashboard !== void 0) {
14902
+ return tuiState.dashboard;
14903
+ }
14904
+ if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
14905
+ return void 0;
14906
+ }
14907
+ const dashboard = createRunnerConsoleDashboardTui(state);
14908
+ tuiState.dashboard = dashboard;
14909
+ tuiState.stop = startRunnerConsoleDashboardRenderTicker(() => {
14910
+ dashboard.render(Date.now());
14911
+ });
14912
+ return dashboard;
14913
+ }
14914
+ function startRunnerConsoleDashboardTicker(state, redraw = redrawScreen, intervalMs = 1e3) {
14915
+ const interval = setInterval(() => {
14916
+ const screen = renderRunnerConsoleDashboard(state, Date.now());
14917
+ if (screen !== void 0) {
14918
+ redraw(screen);
14919
+ }
14920
+ }, intervalMs);
14921
+ interval.unref?.();
14922
+ return () => {
14923
+ clearInterval(interval);
14924
+ };
14925
+ }
14926
+ function startRunnerConsoleDashboardRenderTicker(render, intervalMs = 1e3) {
14927
+ const interval = setInterval(render, intervalMs);
14928
+ interval.unref?.();
14929
+ return () => {
14930
+ clearInterval(interval);
14931
+ };
14932
+ }
14933
+ function handleDashboardKey(state, key, exitProcess = () => process.kill(process.pid, "SIGINT")) {
14934
+ if (key === ctrlCKey) {
14935
+ exitProcess();
14936
+ return;
14937
+ }
14938
+ updateRunnerConsoleDashboardMode(state, key);
14939
+ const screen = renderRunnerConsoleDashboard(state, Date.now());
14940
+ if (screen !== void 0) {
14941
+ redrawScreen(screen);
14942
+ }
14943
+ }
14944
+ function createRunnerConsoleDashboardTui(state, exitProcess = () => process.kill(process.pid, "SIGINT")) {
14945
+ let rendering = false;
14946
+ let rawScrollAnchor;
14947
+ const screen = blessed.screen({
14948
+ title: "Linzumi Commander",
14949
+ smartCSR: true,
14950
+ fullUnicode: true,
14951
+ mouse: true
14952
+ });
14953
+ const header = blessed.box({
14954
+ top: 0,
14955
+ left: 0,
14956
+ width: "100%",
14957
+ height: 6,
14958
+ tags: false
14959
+ });
14960
+ const tableElement = blessed.listtable({
14961
+ top: 6,
14962
+ left: 0,
14963
+ width: "100%",
14964
+ bottom: 1,
14965
+ keys: true,
14966
+ vi: true,
14967
+ mouse: true,
14968
+ scrollable: true,
14969
+ alwaysScroll: true,
14970
+ border: "line",
14971
+ pad: 1,
14972
+ data: [dashboardTableHeader(), emptyJobRow()],
14973
+ scrollbar: {
14974
+ ch: " ",
14975
+ style: { inverse: true }
14976
+ },
14977
+ style: {
14978
+ header: { bold: true },
14979
+ cell: {
14980
+ selected: {
14981
+ inverse: true
14982
+ }
14983
+ },
14984
+ border: {
14985
+ fg: "grey"
14986
+ }
14987
+ }
14988
+ });
14989
+ const rawTitle = blessed.box({
14990
+ top: 0,
14991
+ left: 12,
14992
+ right: 0,
14993
+ height: 2,
14994
+ tags: false,
14995
+ hidden: true
14996
+ });
14997
+ const backButton = blessed.button({
14998
+ top: 0,
14999
+ left: 0,
15000
+ width: 10,
15001
+ height: 1,
15002
+ content: "< Back",
15003
+ mouse: true,
15004
+ keys: true,
15005
+ shrink: true,
15006
+ hidden: true,
15007
+ style: {
15008
+ focus: { inverse: true },
15009
+ hover: { inverse: true }
15010
+ }
15011
+ });
15012
+ const rawBox = blessed.box({
15013
+ top: 3,
15014
+ left: 0,
15015
+ width: "100%",
15016
+ bottom: 0,
15017
+ keys: true,
15018
+ vi: true,
15019
+ mouse: true,
15020
+ scrollable: true,
15021
+ alwaysScroll: true,
15022
+ border: "line",
15023
+ tags: false,
15024
+ hidden: true,
15025
+ scrollbar: {
15026
+ ch: " ",
15027
+ style: { inverse: true }
15028
+ },
15029
+ style: {
15030
+ border: {
15031
+ fg: "grey"
15032
+ }
15033
+ }
15034
+ });
15035
+ screen.append(header);
15036
+ screen.append(tableElement);
15037
+ screen.append(rawTitle);
15038
+ screen.append(backButton);
15039
+ screen.append(rawBox);
15040
+ const showTable = () => {
15041
+ state.mode = { type: "table" };
15042
+ render(Date.now());
15043
+ };
15044
+ const showAllRaw = () => {
15045
+ state.mode = { type: "raw_all" };
15046
+ render(Date.now());
15047
+ };
15048
+ const showSelectedRaw = () => {
15049
+ const selectedKey = selectedDashboardJobKey(state, dashboardJobs(state));
15050
+ if (selectedKey !== void 0) {
15051
+ state.mode = { type: "raw_job", jobKey: selectedKey };
15052
+ render(Date.now());
15053
+ }
15054
+ };
15055
+ screen.key(["C-c"], () => {
15056
+ destroy();
15057
+ exitProcess();
15058
+ });
15059
+ screen.key(["escape"], showTable);
15060
+ screen.key(["r"], showAllRaw);
15061
+ tableElement.key(["enter"], showSelectedRaw);
15062
+ tableElement.on("select item", (_item, index) => {
15063
+ if (rendering || state.mode.type !== "table") {
15064
+ return;
15065
+ }
15066
+ selectDashboardJobAtVisibleIndex(state, index - 1);
15067
+ });
15068
+ tableElement.on("select", (_item, index) => {
15069
+ if (rendering || state.mode.type !== "table") {
15070
+ return;
15071
+ }
15072
+ selectDashboardJobAtVisibleIndex(state, index - 1);
15073
+ showSelectedRaw();
15074
+ });
15075
+ backButton.on("press", showTable);
15076
+ backButton.on("click", showTable);
15077
+ function render(nowMs) {
15078
+ rendering = true;
15079
+ try {
15080
+ switch (state.mode.type) {
15081
+ case "table": {
15082
+ const model = dashboardTableModel(state, dashboardJobs(state), nowMs);
15083
+ header.setContent(tuiHeaderContent(state, model.jobs.length, nowMs));
15084
+ header.show();
15085
+ tableElement.setData(model.rows);
15086
+ tableElement.show();
15087
+ rawTitle.hide();
15088
+ backButton.hide();
15089
+ rawBox.hide();
15090
+ rawScrollAnchor = void 0;
15091
+ syncTuiTableSelection(tableElement, model);
15092
+ tableElement.focus();
15093
+ break;
15094
+ }
15095
+ case "raw_all":
15096
+ case "raw_job": {
15097
+ const model = rawDashboardModel(state, state.mode);
15098
+ const nextRawScrollAnchor = `${state.mode.type}:${state.mode.type === "raw_job" ? state.mode.jobKey : "*"}:${model.lines.length}`;
15099
+ header.hide();
15100
+ tableElement.hide();
15101
+ rawTitle.setContent(
15102
+ [
15103
+ `${model.title} Last update: ${timeAgo(state.lastUpdateAtMs, nowMs)} esc/back: table`,
15104
+ model.tokenUsage === void 0 ? void 0 : `Token usage: ${model.tokenUsage}`
15105
+ ].filter((value) => value !== void 0).join("\n")
15106
+ );
15107
+ rawBox.setContent(
15108
+ model.lines.length === 0 ? "No raw events yet." : model.lines.join("\n")
15109
+ );
15110
+ rawTitle.show();
15111
+ backButton.show();
15112
+ rawBox.show();
15113
+ if (rawScrollAnchor !== nextRawScrollAnchor) {
15114
+ rawBox.setScrollPerc(100);
15115
+ rawScrollAnchor = nextRawScrollAnchor;
15116
+ }
15117
+ rawBox.focus();
15118
+ break;
15119
+ }
15120
+ }
15121
+ screen.render();
15122
+ } finally {
15123
+ rendering = false;
15124
+ }
15125
+ }
15126
+ function destroy() {
15127
+ tuiState.stop?.();
15128
+ tuiState.stop = void 0;
15129
+ tuiState.dashboard = void 0;
15130
+ screen.destroy();
15131
+ }
15132
+ return { render, destroy };
15133
+ }
15134
+ function syncTuiTableSelection(tableElement, model) {
15135
+ const selectedIndex = model.jobs.findIndex(
15136
+ (job) => job.key === model.selectedJobKey
15137
+ );
15138
+ const tableIndex = selectedIndex < 0 ? 1 : selectedIndex + 1;
15139
+ tableElement.select(tableIndex);
15140
+ }
15141
+ function tuiHeaderContent(state, jobCount, nowMs) {
15142
+ return [
15143
+ "Linzumi Commander",
15144
+ `Jobs: ${jobCount} Last update: ${timeAgo(state.lastUpdateAtMs, nowMs)}`,
15145
+ `Overall Token Usage: ${state.tokenUsage ?? "?"}`,
15146
+ `Account rate limits: ${state.rateLimit ?? "?"}`,
15147
+ "Controls: click row/enter raw job | r raw stream | esc/back table | mouse wheel scroll"
15148
+ ].join("\n");
15149
+ }
15150
+ function updateRunnerConsoleDashboardMode(state, key) {
15151
+ switch (key) {
15152
+ case "r":
15153
+ state.mode = { type: "raw_all" };
15154
+ break;
15155
+ case escapeKey:
15156
+ state.mode = { type: "table" };
15157
+ break;
15158
+ case enterKey: {
15159
+ const selectedKey = selectedDashboardJobKey(state, dashboardJobs(state));
15160
+ if (selectedKey !== void 0) {
15161
+ state.mode = { type: "raw_job", jobKey: selectedKey };
15162
+ }
15163
+ break;
15164
+ }
15165
+ case upKey:
15166
+ moveSelection(state, -1);
15167
+ break;
15168
+ case downKey:
15169
+ moveSelection(state, 1);
15170
+ break;
15171
+ default:
15172
+ break;
15173
+ }
15174
+ }
15175
+ function dashboardJobForPayload(state, payload, nowMs) {
15176
+ const linzumiThreadId = stringValue4(payload.linzumi_thread_id);
15177
+ const codexThreadId = stringValue4(payload.codex_thread_id);
15178
+ const existingByCodex = codexThreadId !== void 0 ? Array.from(state.jobs.values()).find(
15179
+ (job) => job.codexThreadId === codexThreadId
15180
+ ) : void 0;
15181
+ if (existingByCodex !== void 0) {
15182
+ return promoteDashboardJobKey(state, existingByCodex, linzumiThreadId);
15183
+ }
15184
+ const key = linzumiThreadId === void 0 ? codexThreadId === void 0 ? "job:unknown" : `codex:${codexThreadId}` : `linzumi:${linzumiThreadId}`;
15185
+ const existing = state.jobs.get(key);
15186
+ if (existing !== void 0) {
15187
+ return existing;
15188
+ }
15189
+ const created = {
15190
+ key,
15191
+ createdAtMs: numberValue(payload.message_received_at_ms) ?? nowMs,
15192
+ createdOrdinal: nextDashboardJobOrdinal(state),
15193
+ linzumiThreadId,
15194
+ linzumiThreadTitle: dashboardLinzumiThreadTitle(payload),
15195
+ codexThreadId,
15196
+ tokenUsage: void 0,
15197
+ lastIncomingPreview: void 0,
15198
+ lastIncomingAtMs: void 0,
15199
+ lastAssistantPreview: void 0,
15200
+ lastAssistantAtMs: void 0,
15201
+ latestCodexEventId: void 0,
15202
+ eventType: void 0,
15203
+ queueDepth: void 0,
15204
+ turnId: void 0,
15205
+ updatedAtMs: nowMs
15206
+ };
15207
+ state.jobs.set(key, created);
15208
+ return created;
15209
+ }
15210
+ function promoteDashboardJobKey(state, job, linzumiThreadId) {
15211
+ if (linzumiThreadId === void 0) {
15212
+ return job;
15213
+ }
15214
+ const key = `linzumi:${linzumiThreadId}`;
15215
+ if (job.key === key) {
15216
+ job.linzumiThreadId = linzumiThreadId;
15217
+ return job;
15218
+ }
15219
+ const promoted = { ...job, key, linzumiThreadId };
15220
+ state.jobs.delete(job.key);
15221
+ state.jobs.set(key, promoted);
15222
+ if (state.selectedJobKey === job.key) {
15223
+ state.selectedJobKey = key;
15224
+ }
15225
+ if (state.mode.type === "raw_job" && state.mode.jobKey === job.key) {
15226
+ state.mode = { type: "raw_job", jobKey: key };
15227
+ }
15228
+ state.rawLines.splice(
15229
+ 0,
15230
+ state.rawLines.length,
15231
+ ...state.rawLines.map(
15232
+ (line) => line.jobKey === job.key ? { ...line, jobKey: key } : line
15233
+ )
15234
+ );
15235
+ return promoted;
15236
+ }
15237
+ function dashboardJobForKey(state, key, nowMs) {
15238
+ if (key.startsWith("codex:")) {
15239
+ const codexThreadId = key.slice("codex:".length);
15240
+ const existingByCodex = Array.from(state.jobs.values()).find(
15241
+ (job) => job.codexThreadId === codexThreadId
15242
+ );
15243
+ if (existingByCodex !== void 0) {
15244
+ return existingByCodex;
15245
+ }
15246
+ }
15247
+ const existing = state.jobs.get(key);
15248
+ if (existing !== void 0) {
15249
+ return existing;
15250
+ }
15251
+ const created = {
15252
+ key,
15253
+ createdAtMs: nowMs,
15254
+ createdOrdinal: nextDashboardJobOrdinal(state),
15255
+ linzumiThreadId: void 0,
15256
+ linzumiThreadTitle: void 0,
15257
+ codexThreadId: key.startsWith("codex:") ? key.slice("codex:".length) : void 0,
15258
+ tokenUsage: void 0,
15259
+ lastIncomingPreview: void 0,
15260
+ lastIncomingAtMs: void 0,
15261
+ lastAssistantPreview: void 0,
15262
+ lastAssistantAtMs: void 0,
15263
+ latestCodexEventId: void 0,
15264
+ eventType: void 0,
15265
+ queueDepth: void 0,
15266
+ turnId: void 0,
15267
+ updatedAtMs: nowMs
15268
+ };
15269
+ state.jobs.set(key, created);
15270
+ return created;
15271
+ }
15272
+ function latestDashboardJob(state) {
15273
+ return dashboardJobsByUpdatedAt(state)[0];
15274
+ }
15275
+ function jobRow(job, selectedJobKey, nowMs) {
15276
+ return [
15277
+ job.key === selectedJobKey ? ">" : " ",
15278
+ linzumiThreadLabel(job),
15279
+ agentId(job.codexThreadId),
15280
+ truncate(job.lastIncomingPreview ?? "?", 50),
15281
+ timeAgo(job.lastIncomingAtMs, nowMs),
15282
+ truncate(job.lastAssistantPreview ?? "?", 50),
15283
+ timeAgo(job.lastAssistantAtMs, nowMs),
15284
+ lastFour(job.latestCodexEventId),
15285
+ truncate(job.eventType ?? "?", 34)
15286
+ ];
15287
+ }
15288
+ function emptyJobRow() {
15289
+ return [" ", "?", "?", "?", "?", "?", "?", "?", "?"];
15290
+ }
15291
+ function codexOutputDashboardLabel(event, payload) {
15292
+ switch (event) {
15293
+ case "kandan.codex_output_forwarded":
15294
+ return codexOutputLabel(payload);
15295
+ case "kandan.codex_delta_forwarded":
15296
+ return "assistant_delta";
15297
+ case "kandan.codex_reasoning_delta_forwarded":
15298
+ return "reasoning_delta";
15299
+ case "kandan.codex_command_output_forwarded":
15300
+ return `command_output ${text(payload.stream)}`;
15301
+ case "kandan.codex_file_change_forwarded":
15302
+ return "file_change";
15303
+ case "kandan.codex_web_search_progress_forwarded":
15304
+ return "search_progress";
15305
+ default:
15306
+ return event;
15307
+ }
15308
+ }
15309
+ function dashboardEventLabel(event) {
15310
+ switch (event) {
15311
+ case "kandan.message_queued":
15312
+ return "message_queued";
15313
+ case "codex.turn_starting":
15314
+ return "turn_starting";
15315
+ default:
15316
+ return event;
15317
+ }
15318
+ }
15319
+ function table(rows) {
15320
+ if (rows.length === 0) {
15321
+ return "";
15322
+ }
15323
+ const widths = dashboardTableColumns.map((column) => column.width);
15324
+ const divider = widths.map((width) => "-".repeat(width)).join("-+-");
15325
+ return rows.map((row, index) => {
15326
+ const line = row.map(
15327
+ (cell, cellIndex) => fixedWidthCell(cell, widths[cellIndex] ?? displayWidth(cell))
15328
+ ).join(" | ");
15329
+ return index === 0 ? `${line}
15330
+ ${divider}` : line;
15331
+ }).join("\n");
15332
+ }
15333
+ function displayWidth(value) {
15334
+ return value.length;
15335
+ }
15336
+ function shortId(value) {
15337
+ return value === void 0 ? "?" : truncate(value, 18);
15338
+ }
15339
+ function linzumiThreadLabel(job) {
15340
+ if (job.linzumiThreadTitle === void 0) {
15341
+ return shortId(job.linzumiThreadId);
15342
+ }
15343
+ return firstN(job.linzumiThreadTitle, 30);
15344
+ }
15345
+ function dashboardLinzumiThreadTitle(payload) {
15346
+ return stringValue4(payload.linzumi_thread_title) ?? stringValue4(payload.thread_title) ?? stringValue4(payload.threadTitle) ?? stringValue4(payload.title);
15347
+ }
15348
+ function agentId(codexThreadId) {
15349
+ if (codexThreadId === void 0) {
15350
+ return "?";
15351
+ }
15352
+ return `codex/...${lastN(codexThreadId, 5)}`;
15353
+ }
15354
+ function fixedWidthCell(value, width) {
15355
+ return truncate(value, width).padEnd(width, " ");
15356
+ }
15357
+ function truncate(value, maxLength) {
15358
+ if (value.length <= maxLength) {
15359
+ return value;
15360
+ }
15361
+ return `${value.slice(0, Math.max(0, maxLength - 3))}...`;
15362
+ }
15363
+ function firstN(value, maxLength) {
15364
+ return value.length <= maxLength ? value : value.slice(0, maxLength);
15365
+ }
15366
+ function lastFour(value) {
15367
+ if (value === void 0) {
15368
+ return "?";
15369
+ }
15370
+ return lastN(value, 4);
15371
+ }
15372
+ function lastN(value, count) {
15373
+ return value.length <= count ? value : value.slice(-count);
15374
+ }
15375
+ function timeAgo(valueMs, nowMs) {
15376
+ if (valueMs === void 0) {
15377
+ return "?";
15378
+ }
15379
+ const seconds = Math.max(0, Math.floor((nowMs - valueMs) / 1e3));
15380
+ if (seconds < 60) {
15381
+ return `${seconds}s`;
15382
+ }
15383
+ const minutes = Math.floor(seconds / 60);
15384
+ if (minutes < 60) {
15385
+ return `${minutes}m`;
15386
+ }
15387
+ return `${Math.floor(minutes / 60)}h`;
15388
+ }
15389
+ function objectValue5(value) {
15390
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
15391
+ }
13837
15392
  function text(value) {
13838
- return stringValue2(value) ?? numberValue(value)?.toString() ?? "?";
15393
+ return stringValue4(value) ?? numberValue(value)?.toString() ?? "?";
13839
15394
  }
13840
- function stringValue2(value) {
15395
+ function stringValue4(value) {
13841
15396
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
13842
15397
  }
13843
15398
  function numberValue(value) {
13844
15399
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
13845
15400
  }
15401
+ var dashboardState, maxRawLines, escapeKey, ctrlCKey, enterKey, upKey, downKey, dashboardTableColumns, redrawScreen, keyboardState, tickerState, tuiState;
13846
15402
  var init_runnerConsoleReporter = __esm({
13847
15403
  "src/runnerConsoleReporter.ts"() {
13848
15404
  "use strict";
15405
+ dashboardState = {
15406
+ jobs: /* @__PURE__ */ new Map(),
15407
+ rawLines: [],
15408
+ nextJobOrdinal: 0,
15409
+ mode: { type: "table" },
15410
+ selectedJobKey: void 0,
15411
+ tokenUsage: void 0,
15412
+ rateLimit: void 0,
15413
+ lastUpdateAtMs: void 0
15414
+ };
15415
+ maxRawLines = 500;
15416
+ escapeKey = "\x1B";
15417
+ ctrlCKey = "";
15418
+ enterKey = "\r";
15419
+ upKey = "\x1B[A";
15420
+ downKey = "\x1B[B";
15421
+ dashboardTableColumns = [
15422
+ { width: 1 },
15423
+ { width: 30 },
15424
+ { width: 16 },
15425
+ { width: 38 },
15426
+ { width: 6 },
15427
+ { width: 42 },
15428
+ { width: 6 },
15429
+ { width: 12 },
15430
+ { width: 24 }
15431
+ ];
15432
+ redrawScreen = (screen) => {
15433
+ process.stdout.write(`\x1B[2J\x1B[H${screen}`);
15434
+ };
15435
+ keyboardState = { initialized: false };
15436
+ tickerState = { stop: void 0 };
15437
+ tuiState = {
15438
+ dashboard: void 0,
15439
+ stop: void 0
15440
+ };
13849
15441
  }
13850
15442
  });
13851
15443
 
@@ -14557,8 +16149,8 @@ import {
14557
16149
  statSync
14558
16150
  } from "node:fs";
14559
16151
  import { createServer as createServer3 } from "node:http";
14560
- import { homedir as homedir10, hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
14561
- import { dirname as dirname9, join as join14, resolve as resolve7 } from "node:path";
16152
+ import { hostname as hostname2, tmpdir as tmpdir3 } from "node:os";
16153
+ import { dirname as dirname9, isAbsolute as isAbsolute3, join as join14, resolve as resolve7 } from "node:path";
14562
16154
  async function runLocalCodexRunner(options) {
14563
16155
  const log = makeRunnerLogger(options);
14564
16156
  const cleanup = {
@@ -15675,11 +17267,10 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
15675
17267
  sessionCodex.onNotification((notification) => {
15676
17268
  seq.value += 1;
15677
17269
  const params = notification.params ?? {};
15678
- const metadata = extractCodexIds(params);
15679
- log("codex.notification", {
15680
- method: notification.method,
15681
- metadata
15682
- });
17270
+ log(
17271
+ "codex.notification",
17272
+ codexNotificationConsoleLogPayload(notification.method, params)
17273
+ );
15683
17274
  session.handleCodexNotification(notification.method, params);
15684
17275
  });
15685
17276
  }
@@ -15722,6 +17313,11 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
15722
17313
  }
15723
17314
  const spawnThreadRunner = options.spawnThreadRunner ?? spawnLocalThreadRunnerProcess;
15724
17315
  const readyTimeoutMs = threadRunnerReadyTimeoutMs(options);
17316
+ log("runner.thread_process_starting", {
17317
+ kandanThreadId,
17318
+ cwd,
17319
+ readyTimeoutMs
17320
+ });
15725
17321
  const startingEntry = {
15726
17322
  kind: "starting",
15727
17323
  promise: withThreadRunnerReadyTimeout(
@@ -15749,6 +17345,11 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
15749
17345
  if (threadRunnerProcesses.get(kandanThreadId) === startingEntry) {
15750
17346
  threadRunnerProcesses.delete(kandanThreadId);
15751
17347
  }
17348
+ log("runner.thread_process_start_failed", {
17349
+ kandanThreadId,
17350
+ cwd,
17351
+ message: error instanceof Error ? error.message : String(error)
17352
+ });
15752
17353
  throw error;
15753
17354
  }
15754
17355
  for (const port of handle.commanderManagedPorts ?? []) {
@@ -15894,14 +17495,17 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
15894
17495
  codex.onNotification((notification) => {
15895
17496
  seq.value += 1;
15896
17497
  const params = notification.params ?? {};
15897
- const metadata = extractCodexIds(params);
17498
+ const logPayload = codexNotificationConsoleLogPayload(
17499
+ notification.method,
17500
+ params
17501
+ );
15898
17502
  if (channelSession === void 0) {
15899
17503
  void kandan.push(topic, "codex_notification", {
15900
17504
  instanceId,
15901
17505
  seq: seq.value,
15902
17506
  method: notification.method,
15903
17507
  params,
15904
- metadata,
17508
+ metadata: logPayload.metadata,
15905
17509
  receivedAt: (/* @__PURE__ */ new Date()).toISOString()
15906
17510
  }).catch((error) => {
15907
17511
  log("kandan.codex_notification_push_failed", {
@@ -15909,10 +17513,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
15909
17513
  });
15910
17514
  });
15911
17515
  }
15912
- log("codex.notification", {
15913
- method: notification.method,
15914
- metadata
15915
- });
17516
+ log("codex.notification", logPayload);
15916
17517
  channelSession?.handleCodexNotification(notification.method, params);
15917
17518
  for (const session of dynamicChannelSessions.values()) {
15918
17519
  session.handleCodexNotification(notification.method, params);
@@ -16517,6 +18118,11 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
16517
18118
  }
16518
18119
  const agentProvider = agentProviderResult.provider;
16519
18120
  if (!cwd.ok) {
18121
+ log("kandan.start_instance_cwd_rejected", {
18122
+ thread_id: controlThreadId(control) ?? null,
18123
+ reason: cwd.reason,
18124
+ allowedCwdCount: allowedCwds.value.length
18125
+ });
16520
18126
  try {
16521
18127
  await publishStartInstanceCwdFailureMessageState(
16522
18128
  kandan,
@@ -18736,13 +20342,8 @@ async function closeThreadRunnerEntry(entry) {
18736
20342
  }
18737
20343
  }
18738
20344
  async function spawnLocalThreadRunnerProcess(options) {
18739
- const scriptPath = process.argv[1];
20345
+ const scriptPath = threadRunnerScriptPath(process.argv[1], process.cwd());
18740
20346
  const kandanThreadId = options.threadProcess?.kandanThreadId ?? "";
18741
- if (scriptPath === void 0 || scriptPath.trim() === "") {
18742
- throw new Error(
18743
- "cannot fork thread runner without current CLI script path"
18744
- );
18745
- }
18746
20347
  const args = ["run", ...threadRunnerCliArgs(options)];
18747
20348
  const nodeArgs = threadRunnerNodeArgs(scriptPath, args);
18748
20349
  const redactedNodeArgs = threadRunnerNodeArgs(
@@ -18870,6 +20471,14 @@ async function spawnLocalThreadRunnerProcess(options) {
18870
20471
  function threadRunnerNodeArgs(scriptPath, args) {
18871
20472
  return [...threadRunnerLoaderExecArgv(process.execArgv), scriptPath, ...args];
18872
20473
  }
20474
+ function threadRunnerScriptPath(scriptPath, cwd) {
20475
+ if (scriptPath === void 0 || scriptPath.trim() === "") {
20476
+ throw new Error(
20477
+ "cannot fork thread runner without current CLI script path"
20478
+ );
20479
+ }
20480
+ return isAbsolute3(scriptPath) ? scriptPath : resolve7(cwd, scriptPath);
20481
+ }
18873
20482
  function threadRunnerLoaderExecArgv(execArgv, index = 0) {
18874
20483
  const current = execArgv[index];
18875
20484
  if (current === void 0) {
@@ -19248,7 +20857,7 @@ function isGitProjectDirectory(cwd) {
19248
20857
  }
19249
20858
  function browseRunnerDirectory(control, options) {
19250
20859
  const requestId = stringValue(control.requestId) ?? null;
19251
- const requestedPath = stringValue(control.path) ?? homedir10();
20860
+ const requestedPath = stringValue(control.path) ?? currentHomeDirectory();
19252
20861
  try {
19253
20862
  const currentPath = realpathSync5(resolve7(expandUserPath(requestedPath)));
19254
20863
  const stats = statSync(currentPath);
@@ -19279,7 +20888,7 @@ function browseRunnerDirectory(control, options) {
19279
20888
  ok: true,
19280
20889
  currentPath,
19281
20890
  parentPath: parent === currentPath ? null : parent,
19282
- homePath: homedir10(),
20891
+ homePath: currentHomeDirectory(),
19283
20892
  runnerCwd: resolve7(options.cwd),
19284
20893
  entries,
19285
20894
  isGit: isGitProjectDirectory(currentPath)
@@ -19332,7 +20941,7 @@ function createRunnerProject(control, options, allowedCwds) {
19332
20941
  error: "invalid_project_template"
19333
20942
  };
19334
20943
  }
19335
- const projectsRoot = join14(homedir10(), "linzumi");
20944
+ const projectsRoot = join14(currentHomeDirectory(), "linzumi");
19336
20945
  const resolvedProjectDirName = template === "hello_linzumi_demo" ? availableProjectDirectoryName(projectsRoot, projectDirName) : projectDirName;
19337
20946
  const projectPath = join14(projectsRoot, resolvedProjectDirName);
19338
20947
  let createdProjectPath = false;
@@ -19359,16 +20968,18 @@ function createRunnerProject(control, options, allowedCwds) {
19359
20968
  }
19360
20969
  const git = spawnSync4("git", ["init"], {
19361
20970
  cwd: projectPath,
19362
- encoding: "utf8"
20971
+ encoding: "utf8",
20972
+ env: process.env
19363
20973
  });
19364
20974
  if (git.status !== 0) {
19365
20975
  const cleanupError = removeCreatedProjectDirectory(projectPath);
20976
+ const gitFailure = git.error instanceof Error ? git.error.message : git.stderr || git.stdout || git.status;
19366
20977
  return {
19367
20978
  instanceId: options.runnerId,
19368
20979
  controlType: control.type,
19369
20980
  requestId,
19370
20981
  ok: false,
19371
- error: cleanupError === void 0 ? `git_init_failed:${git.stderr || git.stdout || git.status}` : `git_init_cleanup_failed:${cleanupError}`
20982
+ error: cleanupError === void 0 ? `git_init_failed:${gitFailure}` : `git_init_cleanup_failed:${cleanupError}`
19372
20983
  };
19373
20984
  }
19374
20985
  const projectRealPath = realpathSync5(projectPath);
@@ -19474,6 +21085,7 @@ var init_runner = __esm({
19474
21085
  init_codexAppServer();
19475
21086
  init_codexProjectTrust();
19476
21087
  init_codexRuntimeOptions();
21088
+ init_codexNotificationConsoleStats();
19477
21089
  init_json();
19478
21090
  init_linzumiContext();
19479
21091
  init_localCapabilities();
@@ -19547,9 +21159,12 @@ function trustedWebSocketFactory(trust, WebSocketImpl = WsWebSocket) {
19547
21159
  if (trust === void 0) {
19548
21160
  return void 0;
19549
21161
  }
19550
- return (url) => new WebSocketImpl(url, {
19551
- ca: trust.ca
19552
- });
21162
+ return (url, protocols) => {
21163
+ const options = {
21164
+ ca: trust.ca
21165
+ };
21166
+ return protocols === void 0 ? new WebSocketImpl(url, options) : new WebSocketImpl(url, protocols, options);
21167
+ };
19553
21168
  }
19554
21169
  var init_kandanTls = __esm({
19555
21170
  "src/kandanTls.ts"() {
@@ -29823,23 +31438,23 @@ var require_sbcs = __commonJS({
29823
31438
  this.ngramList = theNgramList;
29824
31439
  this.byteMap = theByteMap;
29825
31440
  }
29826
- search(table, value) {
31441
+ search(table2, value) {
29827
31442
  let index = 0;
29828
- if (table[index + 32] <= value)
31443
+ if (table2[index + 32] <= value)
29829
31444
  index += 32;
29830
- if (table[index + 16] <= value)
31445
+ if (table2[index + 16] <= value)
29831
31446
  index += 16;
29832
- if (table[index + 8] <= value)
31447
+ if (table2[index + 8] <= value)
29833
31448
  index += 8;
29834
- if (table[index + 4] <= value)
31449
+ if (table2[index + 4] <= value)
29835
31450
  index += 4;
29836
- if (table[index + 2] <= value)
31451
+ if (table2[index + 2] <= value)
29837
31452
  index += 2;
29838
- if (table[index + 1] <= value)
31453
+ if (table2[index + 1] <= value)
29839
31454
  index += 1;
29840
- if (table[index] > value)
31455
+ if (table2[index] > value)
29841
31456
  index -= 1;
29842
- if (index < 0 || table[index] != value)
31457
+ if (index < 0 || table2[index] != value)
29843
31458
  return -1;
29844
31459
  return index;
29845
31460
  }
@@ -34469,7 +36084,7 @@ var require_safer = __commonJS({
34469
36084
  "../../node_modules/safer-buffer/safer.js"(exports, module) {
34470
36085
  "use strict";
34471
36086
  var buffer = __require("buffer");
34472
- var Buffer3 = buffer.Buffer;
36087
+ var Buffer4 = buffer.Buffer;
34473
36088
  var safer = {};
34474
36089
  var key;
34475
36090
  for (key in buffer) {
@@ -34478,12 +36093,12 @@ var require_safer = __commonJS({
34478
36093
  safer[key] = buffer[key];
34479
36094
  }
34480
36095
  var Safer = safer.Buffer = {};
34481
- for (key in Buffer3) {
34482
- if (!Buffer3.hasOwnProperty(key)) continue;
36096
+ for (key in Buffer4) {
36097
+ if (!Buffer4.hasOwnProperty(key)) continue;
34483
36098
  if (key === "allocUnsafe" || key === "allocUnsafeSlow") continue;
34484
- Safer[key] = Buffer3[key];
36099
+ Safer[key] = Buffer4[key];
34485
36100
  }
34486
- safer.Buffer.prototype = Buffer3.prototype;
36101
+ safer.Buffer.prototype = Buffer4.prototype;
34487
36102
  if (!Safer.from || Safer.from === Uint8Array.from) {
34488
36103
  Safer.from = function(value, encodingOrOffset, length) {
34489
36104
  if (typeof value === "number") {
@@ -34492,7 +36107,7 @@ var require_safer = __commonJS({
34492
36107
  if (value && typeof value.length === "undefined") {
34493
36108
  throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type " + typeof value);
34494
36109
  }
34495
- return Buffer3(value, encodingOrOffset, length);
36110
+ return Buffer4(value, encodingOrOffset, length);
34496
36111
  };
34497
36112
  }
34498
36113
  if (!Safer.alloc) {
@@ -34503,7 +36118,7 @@ var require_safer = __commonJS({
34503
36118
  if (size < 0 || size >= 2 * (1 << 30)) {
34504
36119
  throw new RangeError('The value "' + size + '" is invalid for option "size"');
34505
36120
  }
34506
- var buf = Buffer3(size);
36121
+ var buf = Buffer4(size);
34507
36122
  if (!fill || fill.length === 0) {
34508
36123
  buf.fill(0);
34509
36124
  } else if (typeof encoding === "string") {
@@ -34598,7 +36213,7 @@ var require_merge_exports = __commonJS({
34598
36213
  var require_internal = __commonJS({
34599
36214
  "../../node_modules/@inquirer/external-editor/node_modules/iconv-lite/encodings/internal.js"(exports, module) {
34600
36215
  "use strict";
34601
- var Buffer3 = require_safer().Buffer;
36216
+ var Buffer4 = require_safer().Buffer;
34602
36217
  module.exports = {
34603
36218
  // Encodings
34604
36219
  utf8: { type: "_internal", bomAware: true },
@@ -34622,7 +36237,7 @@ var require_internal = __commonJS({
34622
36237
  } else if (this.enc === "cesu8") {
34623
36238
  this.enc = "utf8";
34624
36239
  this.encoder = InternalEncoderCesu8;
34625
- if (Buffer3.from("eda0bdedb2a9", "hex").toString() !== "\u{1F4A9}") {
36240
+ if (Buffer4.from("eda0bdedb2a9", "hex").toString() !== "\u{1F4A9}") {
34626
36241
  this.decoder = InternalDecoderCesu8;
34627
36242
  this.defaultCharUnicode = iconv2.defaultCharUnicode;
34628
36243
  }
@@ -34635,8 +36250,8 @@ var require_internal = __commonJS({
34635
36250
  this.decoder = new StringDecoder(codec.enc);
34636
36251
  }
34637
36252
  InternalDecoder.prototype.write = function(buf) {
34638
- if (!Buffer3.isBuffer(buf)) {
34639
- buf = Buffer3.from(buf);
36253
+ if (!Buffer4.isBuffer(buf)) {
36254
+ buf = Buffer4.from(buf);
34640
36255
  }
34641
36256
  return this.decoder.write(buf);
34642
36257
  };
@@ -34647,7 +36262,7 @@ var require_internal = __commonJS({
34647
36262
  this.enc = codec.enc;
34648
36263
  }
34649
36264
  InternalEncoder.prototype.write = function(str) {
34650
- return Buffer3.from(str, this.enc);
36265
+ return Buffer4.from(str, this.enc);
34651
36266
  };
34652
36267
  InternalEncoder.prototype.end = function() {
34653
36268
  };
@@ -34659,15 +36274,15 @@ var require_internal = __commonJS({
34659
36274
  var completeQuads = str.length - str.length % 4;
34660
36275
  this.prevStr = str.slice(completeQuads);
34661
36276
  str = str.slice(0, completeQuads);
34662
- return Buffer3.from(str, "base64");
36277
+ return Buffer4.from(str, "base64");
34663
36278
  };
34664
36279
  InternalEncoderBase64.prototype.end = function() {
34665
- return Buffer3.from(this.prevStr, "base64");
36280
+ return Buffer4.from(this.prevStr, "base64");
34666
36281
  };
34667
36282
  function InternalEncoderCesu8(options, codec) {
34668
36283
  }
34669
36284
  InternalEncoderCesu8.prototype.write = function(str) {
34670
- var buf = Buffer3.alloc(str.length * 3);
36285
+ var buf = Buffer4.alloc(str.length * 3);
34671
36286
  var bufIdx = 0;
34672
36287
  for (var i = 0; i < str.length; i++) {
34673
36288
  var charCode = str.charCodeAt(i);
@@ -34763,13 +36378,13 @@ var require_internal = __commonJS({
34763
36378
  str = str.slice(0, str.length - 1);
34764
36379
  }
34765
36380
  }
34766
- return Buffer3.from(str, this.enc);
36381
+ return Buffer4.from(str, this.enc);
34767
36382
  };
34768
36383
  InternalEncoderUtf8.prototype.end = function() {
34769
36384
  if (this.highSurrogate) {
34770
36385
  var str = this.highSurrogate;
34771
36386
  this.highSurrogate = "";
34772
- return Buffer3.from(str, this.enc);
36387
+ return Buffer4.from(str, this.enc);
34773
36388
  }
34774
36389
  };
34775
36390
  }
@@ -34779,7 +36394,7 @@ var require_internal = __commonJS({
34779
36394
  var require_utf32 = __commonJS({
34780
36395
  "../../node_modules/@inquirer/external-editor/node_modules/iconv-lite/encodings/utf32.js"(exports) {
34781
36396
  "use strict";
34782
- var Buffer3 = require_safer().Buffer;
36397
+ var Buffer4 = require_safer().Buffer;
34783
36398
  exports._utf32 = Utf32Codec;
34784
36399
  function Utf32Codec(codecOptions, iconv2) {
34785
36400
  this.iconv = iconv2;
@@ -34797,8 +36412,8 @@ var require_utf32 = __commonJS({
34797
36412
  this.highSurrogate = 0;
34798
36413
  }
34799
36414
  Utf32Encoder.prototype.write = function(str) {
34800
- var src = Buffer3.from(str, "ucs2");
34801
- var dst = Buffer3.alloc(src.length * 2);
36415
+ var src = Buffer4.from(str, "ucs2");
36416
+ var dst = Buffer4.alloc(src.length * 2);
34802
36417
  var write32 = this.isLE ? dst.writeUInt32LE : dst.writeUInt32BE;
34803
36418
  var offset = 0;
34804
36419
  for (var i = 0; i < src.length; i += 2) {
@@ -34834,7 +36449,7 @@ var require_utf32 = __commonJS({
34834
36449
  if (!this.highSurrogate) {
34835
36450
  return;
34836
36451
  }
34837
- var buf = Buffer3.alloc(4);
36452
+ var buf = Buffer4.alloc(4);
34838
36453
  if (this.isLE) {
34839
36454
  buf.writeUInt32LE(this.highSurrogate, 0);
34840
36455
  } else {
@@ -34854,7 +36469,7 @@ var require_utf32 = __commonJS({
34854
36469
  }
34855
36470
  var i = 0;
34856
36471
  var codepoint = 0;
34857
- var dst = Buffer3.alloc(src.length + 4);
36472
+ var dst = Buffer4.alloc(src.length + 4);
34858
36473
  var offset = 0;
34859
36474
  var isLE = this.isLE;
34860
36475
  var overflow = this.overflow;
@@ -35010,7 +36625,7 @@ var require_utf32 = __commonJS({
35010
36625
  var require_utf16 = __commonJS({
35011
36626
  "../../node_modules/@inquirer/external-editor/node_modules/iconv-lite/encodings/utf16.js"(exports) {
35012
36627
  "use strict";
35013
- var Buffer3 = require_safer().Buffer;
36628
+ var Buffer4 = require_safer().Buffer;
35014
36629
  exports.utf16be = Utf16BECodec;
35015
36630
  function Utf16BECodec() {
35016
36631
  }
@@ -35020,7 +36635,7 @@ var require_utf16 = __commonJS({
35020
36635
  function Utf16BEEncoder() {
35021
36636
  }
35022
36637
  Utf16BEEncoder.prototype.write = function(str) {
35023
- var buf = Buffer3.from(str, "ucs2");
36638
+ var buf = Buffer4.from(str, "ucs2");
35024
36639
  for (var i = 0; i < buf.length; i += 2) {
35025
36640
  var tmp = buf[i];
35026
36641
  buf[i] = buf[i + 1];
@@ -35037,7 +36652,7 @@ var require_utf16 = __commonJS({
35037
36652
  if (buf.length == 0) {
35038
36653
  return "";
35039
36654
  }
35040
- var buf2 = Buffer3.alloc(buf.length + 1);
36655
+ var buf2 = Buffer4.alloc(buf.length + 1);
35041
36656
  var i = 0;
35042
36657
  var j = 0;
35043
36658
  if (this.overflowByte !== -1) {
@@ -35153,7 +36768,7 @@ var require_utf16 = __commonJS({
35153
36768
  var require_utf7 = __commonJS({
35154
36769
  "../../node_modules/@inquirer/external-editor/node_modules/iconv-lite/encodings/utf7.js"(exports) {
35155
36770
  "use strict";
35156
- var Buffer3 = require_safer().Buffer;
36771
+ var Buffer4 = require_safer().Buffer;
35157
36772
  exports.utf7 = Utf7Codec;
35158
36773
  exports.unicode11utf7 = "utf7";
35159
36774
  function Utf7Codec(codecOptions, iconv2) {
@@ -35167,7 +36782,7 @@ var require_utf7 = __commonJS({
35167
36782
  this.iconv = codec.iconv;
35168
36783
  }
35169
36784
  Utf7Encoder.prototype.write = function(str) {
35170
- return Buffer3.from(str.replace(nonDirectChars, function(chunk) {
36785
+ return Buffer4.from(str.replace(nonDirectChars, function(chunk) {
35171
36786
  return "+" + (chunk === "+" ? "" : this.iconv.encode(chunk, "utf16-be").toString("base64").replace(/=+$/, "")) + "-";
35172
36787
  }.bind(this)));
35173
36788
  };
@@ -35205,7 +36820,7 @@ var require_utf7 = __commonJS({
35205
36820
  res += "+";
35206
36821
  } else {
35207
36822
  var b64str = base64Accum + this.iconv.decode(buf.slice(lastI, i2), "ascii");
35208
- res += this.iconv.decode(Buffer3.from(b64str, "base64"), "utf16-be");
36823
+ res += this.iconv.decode(Buffer4.from(b64str, "base64"), "utf16-be");
35209
36824
  }
35210
36825
  if (buf[i2] != minusChar) {
35211
36826
  i2--;
@@ -35223,7 +36838,7 @@ var require_utf7 = __commonJS({
35223
36838
  var canBeDecoded = b64str.length - b64str.length % 8;
35224
36839
  base64Accum = b64str.slice(canBeDecoded);
35225
36840
  b64str = b64str.slice(0, canBeDecoded);
35226
- res += this.iconv.decode(Buffer3.from(b64str, "base64"), "utf16-be");
36841
+ res += this.iconv.decode(Buffer4.from(b64str, "base64"), "utf16-be");
35227
36842
  }
35228
36843
  this.inBase64 = inBase64;
35229
36844
  this.base64Accum = base64Accum;
@@ -35232,7 +36847,7 @@ var require_utf7 = __commonJS({
35232
36847
  Utf7Decoder.prototype.end = function() {
35233
36848
  var res = "";
35234
36849
  if (this.inBase64 && this.base64Accum.length > 0) {
35235
- res = this.iconv.decode(Buffer3.from(this.base64Accum, "base64"), "utf16-be");
36850
+ res = this.iconv.decode(Buffer4.from(this.base64Accum, "base64"), "utf16-be");
35236
36851
  }
35237
36852
  this.inBase64 = false;
35238
36853
  this.base64Accum = "";
@@ -35248,14 +36863,14 @@ var require_utf7 = __commonJS({
35248
36863
  function Utf7IMAPEncoder(options, codec) {
35249
36864
  this.iconv = codec.iconv;
35250
36865
  this.inBase64 = false;
35251
- this.base64Accum = Buffer3.alloc(6);
36866
+ this.base64Accum = Buffer4.alloc(6);
35252
36867
  this.base64AccumIdx = 0;
35253
36868
  }
35254
36869
  Utf7IMAPEncoder.prototype.write = function(str) {
35255
36870
  var inBase64 = this.inBase64;
35256
36871
  var base64Accum = this.base64Accum;
35257
36872
  var base64AccumIdx = this.base64AccumIdx;
35258
- var buf = Buffer3.alloc(str.length * 5 + 10);
36873
+ var buf = Buffer4.alloc(str.length * 5 + 10);
35259
36874
  var bufIdx = 0;
35260
36875
  for (var i2 = 0; i2 < str.length; i2++) {
35261
36876
  var uChar = str.charCodeAt(i2);
@@ -35294,7 +36909,7 @@ var require_utf7 = __commonJS({
35294
36909
  return buf.slice(0, bufIdx);
35295
36910
  };
35296
36911
  Utf7IMAPEncoder.prototype.end = function() {
35297
- var buf = Buffer3.alloc(10);
36912
+ var buf = Buffer4.alloc(10);
35298
36913
  var bufIdx = 0;
35299
36914
  if (this.inBase64) {
35300
36915
  if (this.base64AccumIdx > 0) {
@@ -35331,7 +36946,7 @@ var require_utf7 = __commonJS({
35331
36946
  res += "&";
35332
36947
  } else {
35333
36948
  var b64str = base64Accum + this.iconv.decode(buf.slice(lastI, i2), "ascii").replace(/,/g, "/");
35334
- res += this.iconv.decode(Buffer3.from(b64str, "base64"), "utf16-be");
36949
+ res += this.iconv.decode(Buffer4.from(b64str, "base64"), "utf16-be");
35335
36950
  }
35336
36951
  if (buf[i2] != minusChar) {
35337
36952
  i2--;
@@ -35349,7 +36964,7 @@ var require_utf7 = __commonJS({
35349
36964
  var canBeDecoded = b64str.length - b64str.length % 8;
35350
36965
  base64Accum = b64str.slice(canBeDecoded);
35351
36966
  b64str = b64str.slice(0, canBeDecoded);
35352
- res += this.iconv.decode(Buffer3.from(b64str, "base64"), "utf16-be");
36967
+ res += this.iconv.decode(Buffer4.from(b64str, "base64"), "utf16-be");
35353
36968
  }
35354
36969
  this.inBase64 = inBase64;
35355
36970
  this.base64Accum = base64Accum;
@@ -35358,7 +36973,7 @@ var require_utf7 = __commonJS({
35358
36973
  Utf7IMAPDecoder.prototype.end = function() {
35359
36974
  var res = "";
35360
36975
  if (this.inBase64 && this.base64Accum.length > 0) {
35361
- res = this.iconv.decode(Buffer3.from(this.base64Accum, "base64"), "utf16-be");
36976
+ res = this.iconv.decode(Buffer4.from(this.base64Accum, "base64"), "utf16-be");
35362
36977
  }
35363
36978
  this.inBase64 = false;
35364
36979
  this.base64Accum = "";
@@ -35371,7 +36986,7 @@ var require_utf7 = __commonJS({
35371
36986
  var require_sbcs_codec = __commonJS({
35372
36987
  "../../node_modules/@inquirer/external-editor/node_modules/iconv-lite/encodings/sbcs-codec.js"(exports) {
35373
36988
  "use strict";
35374
- var Buffer3 = require_safer().Buffer;
36989
+ var Buffer4 = require_safer().Buffer;
35375
36990
  exports._sbcs = SBCSCodec;
35376
36991
  function SBCSCodec(codecOptions, iconv2) {
35377
36992
  if (!codecOptions) {
@@ -35387,8 +37002,8 @@ var require_sbcs_codec = __commonJS({
35387
37002
  }
35388
37003
  codecOptions.chars = asciiString + codecOptions.chars;
35389
37004
  }
35390
- this.decodeBuf = Buffer3.from(codecOptions.chars, "ucs2");
35391
- var encodeBuf = Buffer3.alloc(65536, iconv2.defaultCharSingleByte.charCodeAt(0));
37005
+ this.decodeBuf = Buffer4.from(codecOptions.chars, "ucs2");
37006
+ var encodeBuf = Buffer4.alloc(65536, iconv2.defaultCharSingleByte.charCodeAt(0));
35392
37007
  for (var i = 0; i < codecOptions.chars.length; i++) {
35393
37008
  encodeBuf[codecOptions.chars.charCodeAt(i)] = i;
35394
37009
  }
@@ -35400,7 +37015,7 @@ var require_sbcs_codec = __commonJS({
35400
37015
  this.encodeBuf = codec.encodeBuf;
35401
37016
  }
35402
37017
  SBCSEncoder.prototype.write = function(str) {
35403
- var buf = Buffer3.alloc(str.length);
37018
+ var buf = Buffer4.alloc(str.length);
35404
37019
  for (var i = 0; i < str.length; i++) {
35405
37020
  buf[i] = this.encodeBuf[str.charCodeAt(i)];
35406
37021
  }
@@ -35413,7 +37028,7 @@ var require_sbcs_codec = __commonJS({
35413
37028
  }
35414
37029
  SBCSDecoder.prototype.write = function(buf) {
35415
37030
  var decodeBuf = this.decodeBuf;
35416
- var newBuf = Buffer3.alloc(buf.length * 2);
37031
+ var newBuf = Buffer4.alloc(buf.length * 2);
35417
37032
  var idx1 = 0;
35418
37033
  var idx2 = 0;
35419
37034
  for (var i = 0; i < buf.length; i++) {
@@ -36041,7 +37656,7 @@ var require_sbcs_data_generated = __commonJS({
36041
37656
  var require_dbcs_codec = __commonJS({
36042
37657
  "../../node_modules/@inquirer/external-editor/node_modules/iconv-lite/encodings/dbcs-codec.js"(exports) {
36043
37658
  "use strict";
36044
- var Buffer3 = require_safer().Buffer;
37659
+ var Buffer4 = require_safer().Buffer;
36045
37660
  exports._dbcs = DBCSCodec;
36046
37661
  var UNASSIGNED = -1;
36047
37662
  var GB18030_CODE = -2;
@@ -36277,7 +37892,7 @@ var require_dbcs_codec = __commonJS({
36277
37892
  this.gb18030 = codec.gb18030;
36278
37893
  }
36279
37894
  DBCSEncoder.prototype.write = function(str) {
36280
- var newBuf = Buffer3.alloc(str.length * (this.gb18030 ? 4 : 3));
37895
+ var newBuf = Buffer4.alloc(str.length * (this.gb18030 ? 4 : 3));
36281
37896
  var leadSurrogate = this.leadSurrogate;
36282
37897
  var seqObj = this.seqObj;
36283
37898
  var nextChar = -1;
@@ -36381,7 +37996,7 @@ var require_dbcs_codec = __commonJS({
36381
37996
  if (this.leadSurrogate === -1 && this.seqObj === void 0) {
36382
37997
  return;
36383
37998
  }
36384
- var newBuf = Buffer3.alloc(10);
37999
+ var newBuf = Buffer4.alloc(10);
36385
38000
  var j = 0;
36386
38001
  if (this.seqObj) {
36387
38002
  var dbcsCode = this.seqObj[DEF_CHAR];
@@ -36412,7 +38027,7 @@ var require_dbcs_codec = __commonJS({
36412
38027
  this.gb18030 = codec.gb18030;
36413
38028
  }
36414
38029
  DBCSDecoder.prototype.write = function(buf) {
36415
- var newBuf = Buffer3.alloc(buf.length * 2);
38030
+ var newBuf = Buffer4.alloc(buf.length * 2);
36416
38031
  var nodeIdx = this.nodeIdx;
36417
38032
  var prevBytes = this.prevBytes;
36418
38033
  var prevOffset = this.prevBytes.length;
@@ -36478,15 +38093,15 @@ var require_dbcs_codec = __commonJS({
36478
38093
  this.nodeIdx = 0;
36479
38094
  return ret;
36480
38095
  };
36481
- function findIdx(table, val) {
36482
- if (table[0] > val) {
38096
+ function findIdx(table2, val) {
38097
+ if (table2[0] > val) {
36483
38098
  return -1;
36484
38099
  }
36485
38100
  var l = 0;
36486
- var r = table.length;
38101
+ var r = table2.length;
36487
38102
  while (l < r - 1) {
36488
38103
  var mid = l + (r - l + 1 >> 1);
36489
- if (table[mid] <= val) {
38104
+ if (table2[mid] <= val) {
36490
38105
  l = mid;
36491
38106
  } else {
36492
38107
  r = mid;
@@ -38021,7 +39636,7 @@ var require_encodings = __commonJS({
38021
39636
  var require_streams = __commonJS({
38022
39637
  "../../node_modules/@inquirer/external-editor/node_modules/iconv-lite/lib/streams.js"(exports, module) {
38023
39638
  "use strict";
38024
- var Buffer3 = require_safer().Buffer;
39639
+ var Buffer4 = require_safer().Buffer;
38025
39640
  module.exports = function(streamModule) {
38026
39641
  var Transform = streamModule.Transform;
38027
39642
  function IconvLiteEncoderStream(conv, options) {
@@ -38061,7 +39676,7 @@ var require_streams = __commonJS({
38061
39676
  chunks.push(chunk);
38062
39677
  });
38063
39678
  this.on("end", function() {
38064
- cb(null, Buffer3.concat(chunks));
39679
+ cb(null, Buffer4.concat(chunks));
38065
39680
  });
38066
39681
  return this;
38067
39682
  };
@@ -38075,7 +39690,7 @@ var require_streams = __commonJS({
38075
39690
  constructor: { value: IconvLiteDecoderStream }
38076
39691
  });
38077
39692
  IconvLiteDecoderStream.prototype._transform = function(chunk, encoding, done) {
38078
- if (!Buffer3.isBuffer(chunk) && !(chunk instanceof Uint8Array)) {
39693
+ if (!Buffer4.isBuffer(chunk) && !(chunk instanceof Uint8Array)) {
38079
39694
  return done(new Error("Iconv decoding stream needs buffers as its input."));
38080
39695
  }
38081
39696
  try {
@@ -38118,7 +39733,7 @@ var require_streams = __commonJS({
38118
39733
  var require_lib3 = __commonJS({
38119
39734
  "../../node_modules/@inquirer/external-editor/node_modules/iconv-lite/lib/index.js"(exports, module) {
38120
39735
  "use strict";
38121
- var Buffer3 = require_safer().Buffer;
39736
+ var Buffer4 = require_safer().Buffer;
38122
39737
  var bomHandling = require_bom_handling();
38123
39738
  var mergeModules = require_merge_exports();
38124
39739
  var iconv2 = module.exports;
@@ -38130,7 +39745,7 @@ var require_lib3 = __commonJS({
38130
39745
  var encoder = iconv2.getEncoder(encoding, options);
38131
39746
  var res = encoder.write(str);
38132
39747
  var trail = encoder.end();
38133
- return trail && trail.length > 0 ? Buffer3.concat([res, trail]) : res;
39748
+ return trail && trail.length > 0 ? Buffer4.concat([res, trail]) : res;
38134
39749
  };
38135
39750
  iconv2.decode = function decode(buf, encoding, options) {
38136
39751
  if (typeof buf === "string") {
@@ -38138,7 +39753,7 @@ var require_lib3 = __commonJS({
38138
39753
  console.error("Iconv-lite warning: decode()-ing strings is deprecated. Refer to https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding");
38139
39754
  iconv2.skipDecodeWarning = true;
38140
39755
  }
38141
- buf = Buffer3.from("" + (buf || ""), "binary");
39756
+ buf = Buffer4.from("" + (buf || ""), "binary");
38142
39757
  }
38143
39758
  var decoder = iconv2.getDecoder(encoding, options);
38144
39759
  var res = decoder.write(buf);
@@ -38537,8 +40152,8 @@ var init_esm6 = __esm({
38537
40152
  if (status === "loading") {
38538
40153
  helpTip = theme.style.help("Received");
38539
40154
  } else if (status === "idle") {
38540
- const enterKey = theme.style.key("enter");
38541
- helpTip = theme.style.help(`Press ${enterKey} to launch your preferred editor.`);
40155
+ const enterKey2 = theme.style.key("enter");
40156
+ helpTip = theme.style.help(`Press ${enterKey2} to launch your preferred editor.`);
38542
40157
  }
38543
40158
  let error = "";
38544
40159
  if (errorMsg) {
@@ -39568,15 +41183,15 @@ function signupErrorCodeFromResponseText(text2) {
39568
41183
  function renderEmailCodeStart(value) {
39569
41184
  const body = objectRecord(value, "signup email-code start response");
39570
41185
  return {
39571
- pendingId: stringValue4(body, "pending_id"),
39572
- email: stringValue4(body, "email")
41186
+ pendingId: stringValue6(body, "pending_id"),
41187
+ email: stringValue6(body, "email")
39573
41188
  };
39574
41189
  }
39575
41190
  function renderEmailCodeVerify(value) {
39576
41191
  const body = objectRecord(value, "signup email-code verify response");
39577
41192
  return {
39578
- accessToken: stringValue4(body, "access_token"),
39579
- refreshToken: stringValue4(body, "refresh_token"),
41193
+ accessToken: stringValue6(body, "access_token"),
41194
+ refreshToken: stringValue6(body, "refresh_token"),
39580
41195
  expiresInSeconds: positiveIntegerValue(body, "expires_in"),
39581
41196
  user: renderUserRef(requiredValue(body, "user")),
39582
41197
  isNewUser: booleanValue(body, "is_new_user"),
@@ -39616,7 +41231,7 @@ function renderMissionControlComplete(value) {
39616
41231
  localRunnerToken: renderLocalRunnerToken(
39617
41232
  requiredValue(body, "local_runner_token")
39618
41233
  ),
39619
- launchUrl: stringValue4(body, "launch_url"),
41234
+ launchUrl: stringValue6(body, "launch_url"),
39620
41235
  ...missionControlUrl === void 0 ? {} : { missionControlUrl },
39621
41236
  config: {
39622
41237
  allowedCwds: arrayValue2(config, "allowed_cwds").map(
@@ -39636,21 +41251,21 @@ function renderLocalRunnerToken(value) {
39636
41251
  const body = objectRecord(value, "signup local runner token");
39637
41252
  const expiresInSeconds = optionalPositiveIntegerValue(body, "expires_in");
39638
41253
  return {
39639
- accessToken: stringValue4(body, "access_token"),
41254
+ accessToken: stringValue6(body, "access_token"),
39640
41255
  ...expiresInSeconds === void 0 ? {} : { expiresInSeconds },
39641
- workspaceSlug: stringValue4(body, "workspace"),
39642
- channelSlug: stringValue4(body, "channel")
41256
+ workspaceSlug: stringValue6(body, "workspace"),
41257
+ channelSlug: stringValue6(body, "channel")
39643
41258
  };
39644
41259
  }
39645
41260
  function renderTaskStartResult(value) {
39646
41261
  const body = objectRecord(value, "signup task start");
39647
- const status = stringValue4(body, "status");
41262
+ const status = stringValue6(body, "status");
39648
41263
  if (status !== "queued" && status !== "started" && status !== "failed") {
39649
41264
  throw new Error("signup task start status was incomplete");
39650
41265
  }
39651
41266
  return {
39652
- threadId: stringValue4(body, "thread_id"),
39653
- title: stringValue4(body, "title"),
41267
+ threadId: stringValue6(body, "thread_id"),
41268
+ title: stringValue6(body, "title"),
39654
41269
  status,
39655
41270
  ...optionalStringValue(body, "runner_id") === void 0 ? {} : { runnerId: optionalStringValue(body, "runner_id") },
39656
41271
  ...optionalStringValue(body, "error") === void 0 ? {} : { error: optionalStringValue(body, "error") }
@@ -39660,7 +41275,7 @@ function renderUserRef(value) {
39660
41275
  const body = objectRecord(value, "signup user");
39661
41276
  return {
39662
41277
  id: positiveIntegerValue(body, "id"),
39663
- username: stringValue4(body, "username"),
41278
+ username: stringValue6(body, "username"),
39664
41279
  ...optionalStringValue(body, "display_name") === void 0 ? {} : { displayName: optionalStringValue(body, "display_name") },
39665
41280
  ...optionalStringValue(body, "primary_email") === void 0 ? {} : { primaryEmail: optionalStringValue(body, "primary_email") }
39666
41281
  };
@@ -39669,23 +41284,23 @@ function renderWorkspaceRef(value) {
39669
41284
  const body = objectRecord(value, "signup workspace");
39670
41285
  return {
39671
41286
  id: positiveIntegerValue(body, "id"),
39672
- slug: stringValue4(body, "slug"),
39673
- name: stringValue4(body, "name")
41287
+ slug: stringValue6(body, "slug"),
41288
+ name: stringValue6(body, "name")
39674
41289
  };
39675
41290
  }
39676
41291
  function renderChannelRef(value) {
39677
41292
  const body = objectRecord(value, "signup channel");
39678
41293
  return {
39679
41294
  id: positiveIntegerValue(body, "id"),
39680
- slug: stringValue4(body, "slug"),
39681
- name: stringValue4(body, "name")
41295
+ slug: stringValue6(body, "slug"),
41296
+ name: stringValue6(body, "name")
39682
41297
  };
39683
41298
  }
39684
41299
  function renderThreadRef(value) {
39685
41300
  const body = objectRecord(value, "signup thread");
39686
41301
  return {
39687
- threadId: stringValue4(body, "thread_id"),
39688
- title: stringValue4(body, "title"),
41302
+ threadId: stringValue6(body, "thread_id"),
41303
+ title: stringValue6(body, "title"),
39689
41304
  rootSeq: positiveIntegerValue(body, "root_seq"),
39690
41305
  threadSeq: positiveIntegerValue(body, "thread_seq")
39691
41306
  };
@@ -39703,7 +41318,7 @@ function requiredValue(record, key) {
39703
41318
  }
39704
41319
  return value;
39705
41320
  }
39706
- function stringValue4(record, key) {
41321
+ function stringValue6(record, key) {
39707
41322
  return stringFromUnknown(requiredValue(record, key), `signup ${key}`);
39708
41323
  }
39709
41324
  function optionalStringValue(record, key) {
@@ -39778,6 +41393,7 @@ __export(signupFlow_exports, {
39778
41393
  resolveSignupCodexCommandForTest: () => resolveSignupCodexCommandForTest,
39779
41394
  runSignupFlow: () => runSignupFlow,
39780
41395
  signupCodexTaskSuggestionProcessForTest: () => signupCodexTaskSuggestionProcessForTest,
41396
+ signupCommanderAllowedCwdsForTest: () => signupCommanderAllowedCwdsForTest,
39781
41397
  signupDiscoverProjectsForStartForTest: () => signupDiscoverProjectsForStartForTest,
39782
41398
  signupEmailPickerChoiceNamesForTest: () => signupEmailPickerChoiceNamesForTest,
39783
41399
  signupHelpText: () => signupHelpText,
@@ -39806,7 +41422,7 @@ import {
39806
41422
  writeFileSync as writeFileSync11
39807
41423
  } from "node:fs";
39808
41424
  import { access } from "node:fs/promises";
39809
- import { homedir as homedir13, tmpdir as tmpdir4 } from "node:os";
41425
+ import { homedir as homedir12, tmpdir as tmpdir4 } from "node:os";
39810
41426
  import { delimiter as delimiter3, dirname as dirname12, join as join17, resolve as resolve9 } from "node:path";
39811
41427
  import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
39812
41428
  import { emitKeypressEvents } from "node:readline";
@@ -41164,8 +42780,9 @@ async function signupCommanderRunnerOptions(args, runnerId) {
41164
42780
  codexUrl: void 0,
41165
42781
  launchTui: false,
41166
42782
  fast: false,
41167
- allowedCwds: Array.from(
41168
- /* @__PURE__ */ new Set([args.projectPath, ...args.selectedProjectPaths])
42783
+ allowedCwds: signupCommanderAllowedCwds(
42784
+ args.projectPath,
42785
+ args.selectedProjectPaths
41169
42786
  ),
41170
42787
  allowedForwardPorts: [],
41171
42788
  codeServerBin: editorRuntime.codeServerBin,
@@ -41182,6 +42799,12 @@ async function signupCommanderRunnerOptions(args, runnerId) {
41182
42799
  channelSession: void 0
41183
42800
  };
41184
42801
  }
42802
+ function signupCommanderAllowedCwds(projectPath, selectedProjectPaths) {
42803
+ return assertConfiguredAllowedCwds([projectPath, ...selectedProjectPaths]);
42804
+ }
42805
+ function signupCommanderAllowedCwdsForTest(projectPath, selectedProjectPaths) {
42806
+ return signupCommanderAllowedCwds(projectPath, selectedProjectPaths);
42807
+ }
41185
42808
  function signupConnectRestartCommand(args) {
41186
42809
  return [
41187
42810
  "npx",
@@ -42109,7 +43732,7 @@ async function runSignupPreflights(runtime) {
42109
43732
  function defaultPreflightRuntime() {
42110
43733
  return {
42111
43734
  cwd: process.cwd(),
42112
- homeDir: homedir13(),
43735
+ homeDir: homedir12(),
42113
43736
  probeTool,
42114
43737
  readGitEmail,
42115
43738
  discoverCodeRoots,
@@ -42318,7 +43941,7 @@ function codexTaskSuggestionProcess2(args) {
42318
43941
  function signupCodexTaskSuggestionProcessForTest(args) {
42319
43942
  return codexTaskSuggestionProcess2(args);
42320
43943
  }
42321
- async function resolveSignupCodexCommand(env = process.env, homeDir = homedir13(), executableExists = fileIsExecutable) {
43944
+ async function resolveSignupCodexCommand(env = process.env, homeDir = homedir12(), executableExists = fileIsExecutable) {
42322
43945
  const override = firstConfiguredValue([
42323
43946
  env.LINZUMI_SIGNUP_CODEX_BIN,
42324
43947
  env.LINZUMI_CODEX_BIN
@@ -42957,10 +44580,10 @@ function directoryExists(path2) {
42957
44580
  }
42958
44581
  function expandHomePath(path2) {
42959
44582
  if (path2 === "~") {
42960
- return homedir13();
44583
+ return homedir12();
42961
44584
  }
42962
44585
  if (path2.startsWith("~/")) {
42963
- return join17(homedir13(), path2.slice(2));
44586
+ return join17(homedir12(), path2.slice(2));
42964
44587
  }
42965
44588
  return resolve9(path2);
42966
44589
  }
@@ -43001,6 +44624,7 @@ var init_signupFlow = __esm({
43001
44624
  init_helloLinzumiProject();
43002
44625
  init_localEditorRuntime();
43003
44626
  init_runner();
44627
+ init_localCapabilities();
43004
44628
  init_kandanTls();
43005
44629
  init_userFacingErrors();
43006
44630
  maxSignupVerificationCodeAttempts = 3;
@@ -43049,7 +44673,7 @@ init_runner();
43049
44673
  init_claudeCodeSession();
43050
44674
  init_authCache();
43051
44675
  import { existsSync as existsSync13, readFileSync as readFileSync16, realpathSync as realpathSync6 } from "node:fs";
43052
- import { homedir as homedir14 } from "node:os";
44676
+ import { homedir as homedir13 } from "node:os";
43053
44677
  import { resolve as resolve10 } from "node:path";
43054
44678
  import { fileURLToPath as fileURLToPath4 } from "node:url";
43055
44679
 
@@ -43112,7 +44736,7 @@ init_json();
43112
44736
  init_defaultUrls();
43113
44737
  import { existsSync as existsSync10, mkdirSync as mkdirSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
43114
44738
  import { dirname as dirname10, join as join15 } from "node:path";
43115
- import { homedir as homedir11 } from "node:os";
44739
+ import { homedir as homedir10 } from "node:os";
43116
44740
  async function runAgentCliCommand(args, deps = {
43117
44741
  fetchImpl: fetch,
43118
44742
  stdout: process.stdout,
@@ -43820,7 +45444,7 @@ function agentTokenFile(flags) {
43820
45444
  return flags.get("agent-token-file") ?? defaultAgentTokenFilePath();
43821
45445
  }
43822
45446
  function defaultAgentTokenFilePath() {
43823
- return join15(homedir11(), ".linzumi", "agent-token.json");
45447
+ return join15(homedir10(), ".linzumi", "agent-token.json");
43824
45448
  }
43825
45449
  function normalizedApiUrl(apiUrl) {
43826
45450
  return apiUrl.endsWith("/") ? apiUrl : `${apiUrl}/`;
@@ -43928,13 +45552,13 @@ import {
43928
45552
  watch,
43929
45553
  writeFileSync as writeFileSync9
43930
45554
  } from "node:fs";
43931
- import { homedir as homedir12 } from "node:os";
45555
+ import { homedir as homedir11 } from "node:os";
43932
45556
  import { dirname as dirname11, join as join16, resolve as resolve8 } from "node:path";
43933
45557
  import { execFileSync, spawn as spawn8 } from "node:child_process";
43934
45558
  import { fileURLToPath as fileURLToPath3 } from "node:url";
43935
45559
  var connectedMarkers = ["Connected to Linzumi", "Runner connected:"];
43936
45560
  function commanderStatusDir() {
43937
- return join16(homedir12(), ".linzumi", "commanders");
45561
+ return join16(homedir11(), ".linzumi", "commanders");
43938
45562
  }
43939
45563
  function commanderStatusFile(runnerId, statusDir = commanderStatusDir()) {
43940
45564
  return join16(statusDir, `${safeRunnerId(runnerId)}.json`);
@@ -55982,12 +57606,14 @@ function createLinzumiMcpApiClient(options) {
55982
57606
  getMessage: (params) => request("GET", `${apiPrefix}/message`, params),
55983
57607
  getThread: (params) => request("GET", `${apiPrefix}/thread`, params),
55984
57608
  getChannel: (params) => request("GET", `${apiPrefix}/channel`, params),
57609
+ listVaultSecrets: (params) => request("GET", `${apiPrefix}/vault-secrets`, params),
55985
57610
  sendChannelMessage: (params) => request("POST", `${apiPrefix}/channel-message`, params),
55986
57611
  prepareMessageUploads: (params) => request("POST", `${apiPrefix}/message-uploads/prepare`, params),
55987
57612
  attachMessageFiles: (params) => request("POST", `${apiPrefix}/message-files/attach`, params),
55988
57613
  sendThreadReply: (params) => request("POST", `${apiPrefix}/thread-reply`, params),
55989
57614
  sendDm: (params) => request("POST", `${apiPrefix}/dm`, params),
55990
- dmOwner: (params) => request("POST", `${apiPrefix}/dm-owner`, params)
57615
+ dmOwner: (params) => request("POST", `${apiPrefix}/dm-owner`, params),
57616
+ getVaultValues: (params) => request("POST", `${apiPrefix}/vault-values`, params)
55991
57617
  };
55992
57618
  }
55993
57619
 
@@ -56044,12 +57670,16 @@ Tools:
56044
57670
  linzumi_get_message Read one message by scoped seq or Linzumi message URL.
56045
57671
  linzumi_get_thread Read one bounded thread by ID.
56046
57672
  linzumi_get_channel Read bounded recent messages and channel metadata.
57673
+ linzumi_list_vault_secrets
57674
+ List vault secret names, descriptions, and owner scope.
56047
57675
  linzumi_send_channel_message
56048
57676
  Send a plain-text message to the scoped channel.
56049
57677
  linzumi_send_thread_reply
56050
57678
  Send a plain-text reply to an existing scoped thread.
56051
57679
  linzumi_send_dm Send a plain-text DM to a visible user.
56052
57680
  linzumi_dm_owner Send a plain-text DM to the configured owner username.
57681
+ linzumi_get_vault_values
57682
+ Request approved named vault values for a thread.
56053
57683
 
56054
57684
  Auth:
56055
57685
  Uses --token, LINZUMI_MCP_ACCESS_TOKEN, or cached local runner OAuth from linzumi auth.
@@ -56065,16 +57695,16 @@ async function runMcpServer(args) {
56065
57695
  const kandanUrl = required(values, "api-url");
56066
57696
  const auth = await resolveMcpAuth({
56067
57697
  kandanUrl,
56068
- explicitToken: stringValue3(values, "token") ?? process.env.LINZUMI_MCP_ACCESS_TOKEN,
56069
- authFilePath: stringValue3(values, "auth-file"),
56070
- delegationAuthFilePath: stringValue3(values, "delegation-auth-file"),
56071
- workspaceSlug: stringValue3(values, "workspace"),
56072
- channelSlug: stringValue3(values, "channel")
56073
- });
56074
- const ownerUsername = stringValue3(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME;
56075
- const operatingMode = mcpOperatingMode(stringValue3(values, "mode"));
56076
- const cwd = stringValue3(values, "cwd") ?? process.cwd();
56077
- const defaultThreadId = stringValue3(values, "thread-id") ?? process.env.LINZUMI_THREAD_RUNNER_KANDAN_THREAD_ID;
57698
+ explicitToken: stringValue5(values, "token") ?? process.env.LINZUMI_MCP_ACCESS_TOKEN,
57699
+ authFilePath: stringValue5(values, "auth-file"),
57700
+ delegationAuthFilePath: stringValue5(values, "delegation-auth-file"),
57701
+ workspaceSlug: stringValue5(values, "workspace"),
57702
+ channelSlug: stringValue5(values, "channel")
57703
+ });
57704
+ const ownerUsername = stringValue5(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME;
57705
+ const operatingMode = mcpOperatingMode(stringValue5(values, "mode"));
57706
+ const cwd = stringValue5(values, "cwd") ?? process.cwd();
57707
+ const defaultThreadId = stringValue5(values, "thread-id") ?? process.env.LINZUMI_THREAD_RUNNER_KANDAN_THREAD_ID;
56078
57708
  const client = createLinzumiMcpApiClient({
56079
57709
  kandanUrl,
56080
57710
  accessToken: auth.accessToken,
@@ -56165,6 +57795,12 @@ async function runMcpServer(args) {
56165
57795
  return mcpJsonResult(result);
56166
57796
  }
56167
57797
  );
57798
+ server.tool(
57799
+ "linzumi_list_vault_secrets",
57800
+ "List vault secret names, descriptions, and whether each secret is personal or workspace-owned. Does not return secret values.",
57801
+ {},
57802
+ async (params) => mcpJsonResult(await client.listVaultSecrets(params))
57803
+ );
56168
57804
  server.tool(
56169
57805
  "linzumi_dm_owner",
56170
57806
  "Send a plain-text DM to the configured Commander owner. Requires dm.write and a visible owner username.",
@@ -56221,6 +57857,23 @@ async function runMcpServer(args) {
56221
57857
  },
56222
57858
  async (params) => mcpJsonResult(await client.sendDm(params))
56223
57859
  );
57860
+ server.tool(
57861
+ "linzumi_get_vault_values",
57862
+ "Request values for explicit vault env var names after a thread-scoped human approval. Requires vault.read; thread_id resolves the approval thread.",
57863
+ {
57864
+ workspace: external_exports2.string().optional().describe("Workspace slug. Omit to use the authenticated token scope."),
57865
+ channel: external_exports2.string().optional().describe(
57866
+ "Channel slug used to disambiguate the approval thread when needed."
57867
+ ),
57868
+ thread_id: external_exports2.string().uuid().describe("Existing Linzumi thread UUID for the inline approval."),
57869
+ env_var_names: external_exports2.array(external_exports2.string().regex(/^[A-Z][A-Z0-9_]{0,127}$/)).min(1).max(20).describe(
57870
+ "Explicit vault env var names to request. This tool cannot list all vault keys."
57871
+ ),
57872
+ description: external_exports2.string().min(1).max(500).describe("Human-readable reason shown in the approval request."),
57873
+ justification: external_exports2.string().min(10).max(1e3).describe("Agent justification shown to the approver.")
57874
+ },
57875
+ async (params) => mcpJsonResult(await client.getVaultValues(params))
57876
+ );
56224
57877
  await server.connect(new StdioServerTransport());
56225
57878
  }
56226
57879
  async function uploadFilesWithClient(args) {
@@ -56319,22 +57972,22 @@ function registerEmptyMcpResources(server) {
56319
57972
  async function runMcpConfig(args) {
56320
57973
  const values = strictFlagValues(args);
56321
57974
  const kandanUrl = required(values, "api-url");
56322
- const format = stringValue3(values, "format") ?? "codex";
56323
- const operatingMode = mcpOperatingMode(stringValue3(values, "mode"));
57975
+ const format = stringValue5(values, "format") ?? "codex";
57976
+ const operatingMode = mcpOperatingMode(stringValue5(values, "mode"));
56324
57977
  const token = values.get("include-token") === true ? (await resolveMcpAuth({
56325
57978
  kandanUrl,
56326
- explicitToken: stringValue3(values, "token") ?? process.env.LINZUMI_MCP_ACCESS_TOKEN,
56327
- authFilePath: stringValue3(values, "auth-file"),
56328
- delegationAuthFilePath: stringValue3(values, "delegation-auth-file"),
56329
- workspaceSlug: stringValue3(values, "workspace"),
56330
- channelSlug: stringValue3(values, "channel")
57979
+ explicitToken: stringValue5(values, "token") ?? process.env.LINZUMI_MCP_ACCESS_TOKEN,
57980
+ authFilePath: stringValue5(values, "auth-file"),
57981
+ delegationAuthFilePath: stringValue5(values, "delegation-auth-file"),
57982
+ workspaceSlug: stringValue5(values, "workspace"),
57983
+ channelSlug: stringValue5(values, "channel")
56331
57984
  })).accessToken : void 0;
56332
57985
  const config = linzumiMcpServerConfig({
56333
- command: stringValue3(values, "command"),
57986
+ command: stringValue5(values, "command"),
56334
57987
  kandanUrl,
56335
57988
  accessToken: token,
56336
- delegationAuthFilePath: stringValue3(values, "delegation-auth-file"),
56337
- ownerUsername: stringValue3(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME,
57989
+ delegationAuthFilePath: stringValue5(values, "delegation-auth-file"),
57990
+ ownerUsername: stringValue5(values, "owner-username") ?? process.env.LINZUMI_MCP_OWNER_USERNAME,
56338
57991
  operatingMode
56339
57992
  });
56340
57993
  switch (format) {
@@ -56353,19 +58006,19 @@ async function runMcpDoctor(args) {
56353
58006
  const kandanUrl = required(values, "api-url");
56354
58007
  const auth = await resolveMcpAuth({
56355
58008
  kandanUrl,
56356
- explicitToken: stringValue3(values, "token") ?? process.env.LINZUMI_MCP_ACCESS_TOKEN,
56357
- authFilePath: stringValue3(values, "auth-file"),
56358
- delegationAuthFilePath: stringValue3(values, "delegation-auth-file"),
56359
- workspaceSlug: stringValue3(values, "workspace"),
56360
- channelSlug: stringValue3(values, "channel")
58009
+ explicitToken: stringValue5(values, "token") ?? process.env.LINZUMI_MCP_ACCESS_TOKEN,
58010
+ authFilePath: stringValue5(values, "auth-file"),
58011
+ delegationAuthFilePath: stringValue5(values, "delegation-auth-file"),
58012
+ workspaceSlug: stringValue5(values, "workspace"),
58013
+ channelSlug: stringValue5(values, "channel")
56361
58014
  });
56362
58015
  switch (auth.mode) {
56363
58016
  case "local-runner": {
56364
58017
  const ok = await validateLocalRunnerToken({
56365
58018
  kandanUrl,
56366
58019
  accessToken: auth.accessToken,
56367
- workspaceSlug: stringValue3(values, "workspace"),
56368
- channelSlug: stringValue3(values, "channel")
58020
+ workspaceSlug: stringValue5(values, "workspace"),
58021
+ channelSlug: stringValue5(values, "channel")
56369
58022
  });
56370
58023
  if (!ok) {
56371
58024
  throw new Error("Linzumi MCP auth validation failed");
@@ -56377,7 +58030,7 @@ async function runMcpDoctor(args) {
56377
58030
  kandanUrl,
56378
58031
  accessToken: auth.accessToken,
56379
58032
  authMode: auth.mode,
56380
- operatingMode: mcpOperatingMode(stringValue3(values, "mode"))
58033
+ operatingMode: mcpOperatingMode(stringValue5(values, "mode"))
56381
58034
  });
56382
58035
  await client.validateAuth();
56383
58036
  break;
@@ -56448,7 +58101,7 @@ function strictFlagValues(args) {
56448
58101
  }
56449
58102
  return values;
56450
58103
  }
56451
- function stringValue3(values, key) {
58104
+ function stringValue5(values, key) {
56452
58105
  const value = values.get(key);
56453
58106
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
56454
58107
  }
@@ -56464,7 +58117,7 @@ function mcpOperatingMode(value) {
56464
58117
  }
56465
58118
  }
56466
58119
  function required(values, key) {
56467
- const value = stringValue3(values, key);
58120
+ const value = stringValue5(values, key);
56468
58121
  if (value === void 0) {
56469
58122
  throw new Error(`--${key} is required`);
56470
58123
  }
@@ -56683,11 +58336,11 @@ function runHelloCommand(args) {
56683
58336
  process.stdout.write(helloHelpText());
56684
58337
  return;
56685
58338
  }
56686
- const rootPath = stringValue5(values, "dir");
56687
- const parentDir = stringValue5(values, "parent-dir");
56688
- const name = stringValue5(values, "name");
58339
+ const rootPath = stringValue7(values, "dir");
58340
+ const parentDir = stringValue7(values, "parent-dir");
58341
+ const name = stringValue7(values, "name");
56689
58342
  const port = tcpPortValue(values, "port");
56690
- const host = stringValue5(values, "host");
58343
+ const host = stringValue7(values, "host");
56691
58344
  const project = createHelloLinzumiProject({
56692
58345
  ...rootPath === void 0 ? {} : { rootPath },
56693
58346
  ...parentDir === void 0 ? {} : { parentDir: resolveUserPath(parentDir) },
@@ -56827,8 +58480,8 @@ async function runCommanderDaemonCommand(args) {
56827
58480
  runnerId,
56828
58481
  cwd,
56829
58482
  args: stripDaemonSupervisorFlags(flagArgs),
56830
- logFile: stringValue5(values, "log-file"),
56831
- statusDir: stringValue5(values, "status-dir")
58483
+ logFile: stringValue7(values, "log-file"),
58484
+ statusDir: stringValue7(values, "status-dir")
56832
58485
  });
56833
58486
  process.stdout.write("commander_status: daemon_started\n");
56834
58487
  process.stdout.write(`runner_id: ${record.runnerId}
@@ -56849,7 +58502,7 @@ async function runCommanderDaemonCommand(args) {
56849
58502
  const runnerId = ensureLocalRunnerIdForLinzumiUrl(kandanUrl);
56850
58503
  const status = commanderDaemonStatus(
56851
58504
  runnerId,
56852
- stringValue5(values, "status-dir")
58505
+ stringValue7(values, "status-dir")
56853
58506
  );
56854
58507
  process.stdout.write(`${JSON.stringify(status)}
56855
58508
  `);
@@ -56863,7 +58516,7 @@ async function runCommanderDaemonCommand(args) {
56863
58516
  const result = await waitForCommanderDaemon({
56864
58517
  runnerId,
56865
58518
  timeoutMs,
56866
- statusDir: stringValue5(values, "status-dir")
58519
+ statusDir: stringValue7(values, "status-dir")
56867
58520
  });
56868
58521
  if (result.ok) {
56869
58522
  process.stdout.write("commander_status: connected\n");
@@ -56881,7 +58534,7 @@ async function runCommanderDaemonCommand(args) {
56881
58534
  const runnerId = ensureLocalRunnerIdForLinzumiUrl(kandanUrl);
56882
58535
  const status = stopCommanderDaemon(
56883
58536
  runnerId,
56884
- stringValue5(values, "status-dir")
58537
+ stringValue7(values, "status-dir")
56885
58538
  );
56886
58539
  process.stdout.write(`${JSON.stringify(status)}
56887
58540
  `);
@@ -56910,13 +58563,13 @@ async function runAuthCommand(args) {
56910
58563
  kandanUrl,
56911
58564
  workspaceSlug: target?.workspaceSlug,
56912
58565
  channelSlug: target?.channelSlug,
56913
- callbackHost: stringValue5(values, "oauth-callback-host")
58566
+ callbackHost: stringValue7(values, "oauth-callback-host")
56914
58567
  });
56915
58568
  const cached = writeCachedLocalRunnerToken({
56916
58569
  kandanUrl,
56917
58570
  accessToken: token.accessToken,
56918
58571
  expiresInSeconds: token.expiresInSeconds,
56919
- authFilePath: stringValue5(values, "auth-file")
58572
+ authFilePath: stringValue7(values, "auth-file")
56920
58573
  });
56921
58574
  process.stdout.write(
56922
58575
  `Saved Linzumi local runner auth for ${cached.kandanBaseUrl}
@@ -56943,11 +58596,11 @@ async function parseStartRunnerArgs(args, deps = {
56943
58596
  const requestedCwd = resolveUserPath(cwdArg ?? process.cwd());
56944
58597
  const cwd = assertConfiguredAllowedCwds([requestedCwd])[0] ?? requestedCwd;
56945
58598
  const explicitAllowedCwds = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(
56946
- parseAllowedCwdList(stringValue5(values, "allowed-cwd"))
58599
+ parseAllowedCwdList(stringValue7(values, "allowed-cwd"))
56947
58600
  ) : [];
56948
58601
  const allowedCwds = Array.from(/* @__PURE__ */ new Set([cwd, ...explicitAllowedCwds]));
56949
- const requestedCodexBin = stringValue5(values, "codex-bin") ?? "codex";
56950
- const customCodeServerBin = stringValue5(values, "code-server-bin");
58602
+ const requestedCodexBin = stringValue7(values, "codex-bin") ?? "codex";
58603
+ const customCodeServerBin = stringValue7(values, "code-server-bin");
56951
58604
  const initialDependencyStatus = await deps.buildDependencyStatus({
56952
58605
  cwd,
56953
58606
  codexBin: requestedCodexBin,
@@ -56955,9 +58608,9 @@ async function parseStartRunnerArgs(args, deps = {
56955
58608
  });
56956
58609
  assertStartDependencies(initialDependencyStatus);
56957
58610
  const codexBin = initialDependencyStatus.codex.command;
56958
- const explicitToken = stringValue5(values, "token");
56959
- const authFilePath = stringValue5(values, "auth-file");
56960
- const callbackHost = stringValue5(values, "oauth-callback-host");
58611
+ const explicitToken = stringValue7(values, "token");
58612
+ const authFilePath = stringValue7(values, "auth-file");
58613
+ const callbackHost = stringValue7(values, "oauth-callback-host");
56961
58614
  const reportRejectedCachedToken = () => {
56962
58615
  process.stderr.write(
56963
58616
  "Cached Linzumi local runner auth was rejected; starting OAuth.\n"
@@ -57010,13 +58663,13 @@ async function parseStartRunnerArgs(args, deps = {
57010
58663
  workspaceSlug: target.workspaceSlug,
57011
58664
  cwd,
57012
58665
  codexBin,
57013
- codexUrl: stringValue5(values, "codex-url"),
58666
+ codexUrl: stringValue7(values, "codex-url"),
57014
58667
  launchTui: values.get("launch-tui") === true,
57015
58668
  fast: values.get("fast") === true,
57016
- logFile: stringValue5(values, "log-file"),
58669
+ logFile: stringValue7(values, "log-file"),
57017
58670
  allowedCwds,
57018
58671
  allowedForwardPorts: parseAllowedPortList(
57019
- stringValue5(values, "forward-port")
58672
+ stringValue7(values, "forward-port")
57020
58673
  ),
57021
58674
  codeServerBin: editorRuntime.codeServerBin,
57022
58675
  editorRuntime: editorRuntime.runtime,
@@ -57027,12 +58680,12 @@ async function parseStartRunnerArgs(args, deps = {
57027
58680
  channelSession: {
57028
58681
  workspaceSlug: target.workspaceSlug,
57029
58682
  channelSlug: target.channelSlug,
57030
- kandanThreadId: stringValue5(values, "linzumi-thread-id"),
57031
- listenUser: stringValue5(values, "listen-user") ?? defaultListenUserFromToken(targetToken),
57032
- model: stringValue5(values, "model"),
57033
- reasoningEffort: stringValue5(values, "reasoning-effort"),
57034
- sandbox: stringValue5(values, "sandbox"),
57035
- approvalPolicy: stringValue5(values, "approval-policy"),
58683
+ kandanThreadId: stringValue7(values, "linzumi-thread-id"),
58684
+ listenUser: stringValue7(values, "listen-user") ?? defaultListenUserFromToken(targetToken),
58685
+ model: stringValue7(values, "model"),
58686
+ reasoningEffort: stringValue7(values, "reasoning-effort"),
58687
+ sandbox: stringValue7(values, "sandbox"),
58688
+ approvalPolicy: stringValue7(values, "approval-policy"),
57036
58689
  allowPortForwardingByDefault: true,
57037
58690
  streamFlushMs: positiveIntegerValue2(values, "stream-flush-ms")
57038
58691
  }
@@ -57057,32 +58710,32 @@ async function parseAgentRunnerArgs(args, deps = {
57057
58710
  "linzumi commander accepts either <folder> or --cwd, not both"
57058
58711
  );
57059
58712
  }
57060
- const tokenFilePath = stringValue5(values, "agent-token-file") ?? defaultAgentTokenFilePath();
58713
+ const tokenFilePath = stringValue7(values, "agent-token-file") ?? defaultAgentTokenFilePath();
57061
58714
  const tokenFile = readStoredAgentTokenFile(tokenFilePath, deps.readTextFile);
57062
58715
  const channelSlug = tokenFile.channelId;
57063
58716
  rejectWorkspaceCommanderThreadFlags(values, channelSlug);
57064
58717
  const channelSession = channelSlug === void 0 ? void 0 : {
57065
58718
  workspaceSlug: tokenFile.workspaceId,
57066
58719
  channelSlug,
57067
- kandanThreadId: stringValue5(values, "linzumi-thread-id"),
57068
- listenUser: stringValue5(values, "listen-user") ?? requiredStoredOwnerUsername(tokenFile.ownerUsername),
57069
- model: stringValue5(values, "model"),
57070
- reasoningEffort: stringValue5(values, "reasoning-effort"),
57071
- sandbox: stringValue5(values, "sandbox"),
57072
- approvalPolicy: stringValue5(values, "approval-policy"),
58720
+ kandanThreadId: stringValue7(values, "linzumi-thread-id"),
58721
+ listenUser: stringValue7(values, "listen-user") ?? requiredStoredOwnerUsername(tokenFile.ownerUsername),
58722
+ model: stringValue7(values, "model"),
58723
+ reasoningEffort: stringValue7(values, "reasoning-effort"),
58724
+ sandbox: stringValue7(values, "sandbox"),
58725
+ approvalPolicy: stringValue7(values, "approval-policy"),
57073
58726
  allowPortForwardingByDefault: true,
57074
58727
  streamFlushMs: positiveIntegerValue2(values, "stream-flush-ms")
57075
58728
  };
57076
58729
  const kandanUrl = kandanUrlValue(values) ?? agentApiUrlToKandanUrl(tokenFile.apiUrl);
57077
- const requestedCwdValue = cwdArg ?? stringValue5(values, "cwd");
58730
+ const requestedCwdValue = cwdArg ?? stringValue7(values, "cwd");
57078
58731
  const requestedCwd = resolveUserPath(requestedCwdValue ?? process.cwd());
57079
58732
  const configuredAllowedCwds2 = requestedCwdValue === void 0 && !values.has("allowed-cwd") ? readConfiguredAllowedCwdDetailsForLinzumiUrl(kandanUrl) : { allowedCwds: [], missingAllowedCwds: [] };
57080
58733
  const allowedCwds = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(
57081
- parseAllowedCwdList(stringValue5(values, "allowed-cwd"))
58734
+ parseAllowedCwdList(stringValue7(values, "allowed-cwd"))
57082
58735
  ) : requestedCwdValue === void 0 ? configuredAllowedCwds2.allowedCwds.length > 0 ? [...configuredAllowedCwds2.allowedCwds] : assertConfiguredAllowedCwds([requestedCwd]) : assertConfiguredAllowedCwds([requestedCwd]);
57083
58736
  const cwd = allowedCwds[0] ?? requestedCwd;
57084
- const requestedCodexBin = stringValue5(values, "codex-bin") ?? "codex";
57085
- const customCodeServerBin = stringValue5(values, "code-server-bin");
58737
+ const requestedCodexBin = stringValue7(values, "codex-bin") ?? "codex";
58738
+ const customCodeServerBin = stringValue7(values, "code-server-bin");
57086
58739
  const initialDependencyStatus = await deps.buildDependencyStatus({
57087
58740
  cwd,
57088
58741
  codexBin: requestedCodexBin,
@@ -57113,14 +58766,14 @@ async function parseAgentRunnerArgs(args, deps = {
57113
58766
  workspaceSlug: tokenFile.workspaceId,
57114
58767
  cwd,
57115
58768
  codexBin,
57116
- codexUrl: stringValue5(values, "codex-url"),
58769
+ codexUrl: stringValue7(values, "codex-url"),
57117
58770
  launchTui: values.get("launch-tui") === true,
57118
58771
  fast: values.get("fast") === true,
57119
- logFile: stringValue5(values, "log-file"),
58772
+ logFile: stringValue7(values, "log-file"),
57120
58773
  allowedCwds,
57121
58774
  missingAllowedCwds: configuredAllowedCwds2.missingAllowedCwds,
57122
58775
  allowedForwardPorts: parseAllowedPortList(
57123
- stringValue5(values, "forward-port")
58776
+ stringValue7(values, "forward-port")
57124
58777
  ),
57125
58778
  codeServerBin: editorRuntime.codeServerBin,
57126
58779
  editorRuntime: editorRuntime.runtime,
@@ -57233,16 +58886,16 @@ async function parseRunnerArgs(args, deps = {
57233
58886
  process.exit(0);
57234
58887
  }
57235
58888
  rejectConnectChannelFlags(values);
57236
- const workspaceSlug = stringValue5(values, "workspace");
58889
+ const workspaceSlug = stringValue7(values, "workspace");
57237
58890
  const kandanUrl = kandanUrlValue(values) ?? defaultLinzumiWebSocketUrl;
57238
- const cwd = stringValue5(values, "cwd") ?? process.cwd();
58891
+ const cwd = stringValue7(values, "cwd") ?? process.cwd();
57239
58892
  const cwdAllowedCwds = assertConfiguredAllowedCwds([cwd]);
57240
58893
  const localConfiguredAllowedCwds = values.has("allowed-cwd") ? { allowedCwds: [], missingAllowedCwds: [] } : readConfiguredAllowedCwdDetailsForLinzumiUrl(kandanUrl);
57241
58894
  const configuredAllowedCwds2 = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(
57242
- parseAllowedCwdList(stringValue5(values, "allowed-cwd"))
58895
+ parseAllowedCwdList(stringValue7(values, "allowed-cwd"))
57243
58896
  ) : [...localConfiguredAllowedCwds.allowedCwds];
57244
- const requestedCodexBin = stringValue5(values, "codex-bin") ?? "codex";
57245
- const customCodeServerBin = stringValue5(values, "code-server-bin");
58897
+ const requestedCodexBin = stringValue7(values, "codex-bin") ?? "codex";
58898
+ const customCodeServerBin = stringValue7(values, "code-server-bin");
57246
58899
  const initialDependencyStatus = await deps.buildDependencyStatus({
57247
58900
  cwd,
57248
58901
  codexBin: requestedCodexBin,
@@ -57250,13 +58903,13 @@ async function parseRunnerArgs(args, deps = {
57250
58903
  });
57251
58904
  assertStartDependencies(initialDependencyStatus);
57252
58905
  const codexBin = initialDependencyStatus.codex.command;
57253
- const explicitToken = stringValue5(values, "token");
58906
+ const explicitToken = stringValue7(values, "token");
57254
58907
  const token = await deps.resolveToken({
57255
58908
  kandanUrl,
57256
58909
  explicitToken,
57257
58910
  workspaceSlug,
57258
- authFilePath: stringValue5(values, "auth-file"),
57259
- callbackHost: stringValue5(values, "oauth-callback-host"),
58911
+ authFilePath: stringValue7(values, "auth-file"),
58912
+ callbackHost: stringValue7(values, "oauth-callback-host"),
57260
58913
  reportRejectedCachedToken: () => {
57261
58914
  process.stderr.write(
57262
58915
  "Cached Linzumi local runner auth was rejected; starting OAuth.\n"
@@ -57285,16 +58938,16 @@ async function parseRunnerArgs(args, deps = {
57285
58938
  ),
57286
58939
  cwd,
57287
58940
  codexBin,
57288
- codexUrl: stringValue5(values, "codex-url"),
58941
+ codexUrl: stringValue7(values, "codex-url"),
57289
58942
  launchTui: values.get("launch-tui") === true,
57290
58943
  fast: values.get("fast") === true,
57291
- logFile: stringValue5(values, "log-file"),
58944
+ logFile: stringValue7(values, "log-file"),
57292
58945
  allowedCwds: Array.from(
57293
58946
  /* @__PURE__ */ new Set([...cwdAllowedCwds, ...configuredAllowedCwds2])
57294
58947
  ),
57295
58948
  missingAllowedCwds: localConfiguredAllowedCwds.missingAllowedCwds,
57296
58949
  allowedForwardPorts: parseAllowedPortList(
57297
- stringValue5(values, "forward-port")
58950
+ stringValue7(values, "forward-port")
57298
58951
  ),
57299
58952
  codeServerBin: editorRuntime.codeServerBin,
57300
58953
  editorRuntime: editorRuntime.runtime,
@@ -57308,16 +58961,16 @@ async function parseRunnerArgs(args, deps = {
57308
58961
  }
57309
58962
  function runnerRuntimeDefaultsFromValues(values) {
57310
58963
  return {
57311
- model: stringValue5(values, "model"),
57312
- reasoningEffort: stringValue5(values, "reasoning-effort"),
57313
- approvalPolicy: stringValue5(values, "approval-policy"),
57314
- sandbox: stringValue5(values, "sandbox"),
58964
+ model: stringValue7(values, "model"),
58965
+ reasoningEffort: stringValue7(values, "reasoning-effort"),
58966
+ approvalPolicy: stringValue7(values, "approval-policy"),
58967
+ sandbox: stringValue7(values, "sandbox"),
57315
58968
  allowPortForwardingByDefault: true
57316
58969
  };
57317
58970
  }
57318
58971
  function kandanUrlValue(values) {
57319
- const apiUrl = stringValue5(values, "api-url");
57320
- const linzumiUrl = stringValue5(values, "linzumi-url");
58972
+ const apiUrl = stringValue7(values, "api-url");
58973
+ const linzumiUrl = stringValue7(values, "linzumi-url");
57321
58974
  if (apiUrl !== void 0 && linzumiUrl !== void 0 && apiUrl !== linzumiUrl) {
57322
58975
  throw new Error("use only one of --api-url or --linzumi-url");
57323
58976
  }
@@ -57426,10 +59079,10 @@ function rejectStartTargetingFlags(values) {
57426
59079
  }
57427
59080
  function resolveUserPath(pathValue) {
57428
59081
  if (pathValue === "~") {
57429
- return homedir14();
59082
+ return homedir13();
57430
59083
  }
57431
59084
  if (pathValue.startsWith("~/")) {
57432
- return resolve10(homedir14(), pathValue.slice(2));
59085
+ return resolve10(homedir13(), pathValue.slice(2));
57433
59086
  }
57434
59087
  return resolve10(pathValue);
57435
59088
  }
@@ -57456,8 +59109,8 @@ function rejectConnectChannelFlags(values) {
57456
59109
  );
57457
59110
  }
57458
59111
  function parseOptionalChannelTarget(values) {
57459
- const channel = stringValue5(values, "channel");
57460
- const workspace = stringValue5(values, "workspace");
59112
+ const channel = stringValue7(values, "channel");
59113
+ const workspace = stringValue7(values, "workspace");
57461
59114
  if (channel === void 0) {
57462
59115
  if (workspace === void 0) {
57463
59116
  return void 0;
@@ -57491,9 +59144,9 @@ function withLocalMachineId(options) {
57491
59144
  function parseThreadCodexWorkerArgs(args) {
57492
59145
  const values = strictFlagValues2(args);
57493
59146
  const kandanUrl = kandanUrlValue(values) ?? defaultLinzumiWebSocketUrl;
57494
- const cwd = stringValue5(values, "cwd") ?? process.cwd();
59147
+ const cwd = stringValue7(values, "cwd") ?? process.cwd();
57495
59148
  const configuredAllowedCwds2 = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(
57496
- parseAllowedCwdList(stringValue5(values, "allowed-cwd"))
59149
+ parseAllowedCwdList(stringValue7(values, "allowed-cwd"))
57497
59150
  ) : assertConfiguredAllowedCwds([cwd]);
57498
59151
  const kandanThreadId = requiredThreadRunnerKandanThreadId();
57499
59152
  const token = requiredThreadRunnerToken();
@@ -57503,17 +59156,17 @@ function parseThreadCodexWorkerArgs(args) {
57503
59156
  runnerId: `thread-codex-worker:${kandanThreadId}`,
57504
59157
  machineId: void 0,
57505
59158
  runnerLockConfigPath: void 0,
57506
- workspaceSlug: stringValue5(values, "workspace"),
59159
+ workspaceSlug: stringValue7(values, "workspace"),
57507
59160
  cwd,
57508
- codexBin: stringValue5(values, "codex-bin") ?? "codex",
59161
+ codexBin: stringValue7(values, "codex-bin") ?? "codex",
57509
59162
  codexUrl: void 0,
57510
59163
  launchTui: false,
57511
59164
  fast: values.get("fast") === true ? true : void 0,
57512
- logFile: stringValue5(values, "log-file"),
59165
+ logFile: stringValue7(values, "log-file"),
57513
59166
  allowedCwds: configuredAllowedCwds2.allowedCwds,
57514
59167
  missingAllowedCwds: configuredAllowedCwds2.missingAllowedCwds,
57515
59168
  allowedForwardPorts: parseAllowedPortList(
57516
- stringValue5(values, "forward-port")
59169
+ stringValue7(values, "forward-port")
57517
59170
  ),
57518
59171
  codeServerBin: void 0,
57519
59172
  editorRuntime: void 0,
@@ -57543,13 +59196,13 @@ function requiredThreadRunnerToken() {
57543
59196
  return token;
57544
59197
  }
57545
59198
  function required2(values, key) {
57546
- const value = stringValue5(values, key);
59199
+ const value = stringValue7(values, key);
57547
59200
  if (value === void 0) {
57548
59201
  throw new Error(`missing --${key}`);
57549
59202
  }
57550
59203
  return value;
57551
59204
  }
57552
- function stringValue5(values, key) {
59205
+ function stringValue7(values, key) {
57553
59206
  const value = values.get(key);
57554
59207
  if (typeof value === "string" && value.trim() !== "") {
57555
59208
  return value;
@@ -57557,7 +59210,7 @@ function stringValue5(values, key) {
57557
59210
  return void 0;
57558
59211
  }
57559
59212
  function positiveIntegerValue2(values, key) {
57560
- const value = stringValue5(values, key);
59213
+ const value = stringValue7(values, key);
57561
59214
  if (value === void 0) {
57562
59215
  return void 0;
57563
59216
  }
@@ -57568,7 +59221,7 @@ function positiveIntegerValue2(values, key) {
57568
59221
  throw new Error(`--${key} must be a positive integer`);
57569
59222
  }
57570
59223
  function tcpPortValue(values, key) {
57571
- const value = stringValue5(values, key);
59224
+ const value = stringValue7(values, key);
57572
59225
  if (value === void 0) {
57573
59226
  return void 0;
57574
59227
  }