@linzumi/cli 0.0.39-beta → 0.0.41-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +566 -260
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,15 +1,16 @@
1
1
  // src/index.ts
2
2
  import { randomUUID as randomUUID3 } from "node:crypto";
3
- import { existsSync as existsSync10, readFileSync as readFileSync9, realpathSync as realpathSync5 } from "node:fs";
3
+ import { existsSync as existsSync10, readFileSync as readFileSync9, realpathSync as realpathSync6 } from "node:fs";
4
4
  import { homedir as homedir9 } from "node:os";
5
- import { resolve as resolve8 } from "node:path";
5
+ import { resolve as resolve9 } from "node:path";
6
6
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7
7
 
8
8
  // src/runner.ts
9
9
  import { spawn as spawn6 } from "node:child_process";
10
10
  import { randomUUID as randomUUID2 } from "node:crypto";
11
+ import { realpathSync as realpathSync4 } from "node:fs";
11
12
  import { hostname as hostname2 } from "node:os";
12
- import { join as join6 } from "node:path";
13
+ import { join as join6, resolve as resolve5 } from "node:path";
13
14
 
14
15
  // src/channelSessionSupport.ts
15
16
  import { spawnSync } from "node:child_process";
@@ -289,6 +290,9 @@ function codexSandboxPolicy(sandbox, cwd) {
289
290
  // src/codexOutput.ts
290
291
  import { Buffer as Buffer2 } from "node:buffer";
291
292
  var maxVisibleWebSearchQueries = 6;
293
+ var maxPersistedCommandOutputChars = 14000;
294
+ var maxPersistedCommandLabelChars = 512;
295
+ var commandOutputTruncationReason = "phoenix_insert_body_limit";
292
296
  function codexOutputMessagesForTurn(response, turnId) {
293
297
  if ("error" in response) {
294
298
  return [];
@@ -354,21 +358,20 @@ function codexOutputMessagesForItem(item, index) {
354
358
  case "commandExecution": {
355
359
  const command = stringValue(item.command) ?? "command";
356
360
  const output = nonBlankStringValue(item.aggregatedOutput) ?? "";
357
- const body = [`$ ${command}`, output].filter((part) => part.trim() !== "").join(`
358
-
359
- `);
361
+ const projection = codexCommandOutputProjection(command, output);
360
362
  return [
361
363
  {
362
364
  itemKey,
363
- body,
365
+ body: projection.body,
364
366
  structured: baseStructured("codex_command_execution", {
365
- command,
367
+ command: projection.command,
366
368
  cwd: stringValue(item.cwd) ?? "",
367
369
  status: stringValue(item.status) ?? "",
368
370
  process_id: stringValue(item.processId) ?? "",
369
371
  duration_ms: integerValue(item.durationMs) ?? null,
370
372
  exit_code: integerValue(item.exitCode) ?? null,
371
- output
373
+ output: projection.output,
374
+ ...commandOutputTruncationMetadata(projection)
372
375
  })
373
376
  }
374
377
  ];
@@ -500,21 +503,29 @@ function codexCommandOutputDeltaFromNotification(params) {
500
503
  };
501
504
  }
502
505
  function codexCommandExecutionStructuredMessage(itemKey, fields, streamState) {
506
+ const projection = codexCommandOutputProjection(fields.command, fields.output);
507
+ return codexCommandExecutionStructuredMessageFromProjection(itemKey, fields, streamState, projection);
508
+ }
509
+ function codexCommandExecutionStructuredMessageFromProjection(itemKey, fields, streamState, projection) {
503
510
  return {
504
511
  kind: "codex_command_execution",
505
512
  item_id: itemKey,
506
513
  transcript_unit_id: `codex_command_execution:${itemKey}`,
507
514
  stream_state: streamState,
508
- command: fields.command,
515
+ command: projection.command,
509
516
  cwd: fields.cwd ?? "",
510
517
  status: fields.status ?? streamState,
511
518
  process_id: fields.processId ?? "",
512
519
  duration_ms: fields.durationMs ?? null,
513
520
  exit_code: fields.exitCode ?? null,
514
521
  stream: fields.stream ?? "",
515
- output: fields.output
522
+ output: projection.output,
523
+ ...commandOutputTruncationMetadata(projection)
516
524
  };
517
525
  }
526
+ function codexCommandOutputBody(command, output) {
527
+ return codexCommandOutputProjection(command, output).body;
528
+ }
518
529
  function codexFileChangeDeltaFromNotification(params) {
519
530
  const patchText = textDeltaFromParams(params);
520
531
  if (patchText === undefined || patchText === "") {
@@ -617,6 +628,65 @@ function assistantVisibleText(item) {
617
628
  function assistantDeltaText(params) {
618
629
  return textDeltaFromParams(params);
619
630
  }
631
+ function codexCommandOutputProjection(command, output) {
632
+ const normalizedCommand = truncatePersistedText(sanitizePersistedText(command), maxPersistedCommandLabelChars, "command label");
633
+ const normalizedOutput = truncateCommandOutput(sanitizePersistedText(output));
634
+ const body = commandOutputBodyForProjection(normalizedCommand.text, normalizedOutput.text);
635
+ return {
636
+ command: normalizedCommand.text,
637
+ output: normalizedOutput.text,
638
+ body,
639
+ truncated: normalizedCommand.truncated || normalizedOutput.truncated,
640
+ originalLength: output.length,
641
+ maxOutputChars: maxPersistedCommandOutputChars,
642
+ truncationReason: commandOutputTruncationReason
643
+ };
644
+ }
645
+ function commandOutputTruncationMetadata(projection) {
646
+ return projection.truncated ? {
647
+ output_truncated: true,
648
+ output_original_length: projection.originalLength,
649
+ output_max_chars: projection.maxOutputChars,
650
+ output_truncation_reason: projection.truncationReason
651
+ } : {};
652
+ }
653
+ function commandOutputBodyForProjection(command, output) {
654
+ return [`$ ${command}`, output].filter((part) => part.trim() !== "").join(`
655
+
656
+ `);
657
+ }
658
+ function truncateCommandOutput(output) {
659
+ if (output.length <= maxPersistedCommandOutputChars) {
660
+ return { text: output, truncated: false };
661
+ }
662
+ const marker = `
663
+
664
+ [Linzumi truncated command output: output was too large to persist fully. Owners should search runner logs for codex.command_output_truncated failure_class=phoenix_insert_body_limit.]
665
+
666
+ `;
667
+ const availableChars = Math.max(0, maxPersistedCommandOutputChars - marker.length);
668
+ const headChars = Math.min(2000, Math.floor(availableChars / 3));
669
+ const tailChars = availableChars - headChars;
670
+ const text = `${output.slice(0, headChars)}${marker}${output.slice(-tailChars)}`;
671
+ return {
672
+ text: text.slice(0, maxPersistedCommandOutputChars),
673
+ truncated: true
674
+ };
675
+ }
676
+ function truncatePersistedText(text, maxChars, label) {
677
+ if (text.length <= maxChars) {
678
+ return { text, truncated: false };
679
+ }
680
+ const marker = `[Linzumi truncated ${label}]`;
681
+ const availableChars = Math.max(0, maxChars - marker.length - 1);
682
+ return {
683
+ text: `${text.slice(0, availableChars)} ${marker}`.slice(0, maxChars),
684
+ truncated: true
685
+ };
686
+ }
687
+ function sanitizePersistedText(text) {
688
+ return text.replaceAll("\x00", "�");
689
+ }
620
690
  function textDeltaFromParams(params) {
621
691
  const delta = params.delta;
622
692
  if (typeof delta === "string") {
@@ -640,57 +710,67 @@ function commandMessageForFunctionCall(item, output, index) {
640
710
  const command = name === "exec_command" ? nonBlankStringValue(args?.cmd) ?? name : `${name}${toolArgumentsSummary(item.arguments)}`;
641
711
  const cwd = nonBlankStringValue(args?.cwd) ?? "";
642
712
  const outputText = toolOutputText(output);
643
- const body = [`$ ${command}`, outputText].filter((part) => part.trim() !== "").join(`
644
-
645
- `);
646
- return [{
647
- itemKey,
648
- body,
649
- structured: codexCommandExecutionStructuredMessage(itemKey, {
650
- command,
651
- cwd,
652
- output: outputText,
653
- status: output === undefined ? "started" : "completed"
654
- }, "completed")
655
- }];
713
+ const projection = codexCommandOutputProjection(command, outputText);
714
+ return [
715
+ {
716
+ itemKey,
717
+ body: projection.body,
718
+ structured: codexCommandExecutionStructuredMessage(itemKey, {
719
+ command,
720
+ cwd,
721
+ output: outputText,
722
+ status: output === undefined ? "started" : "completed"
723
+ }, "completed")
724
+ }
725
+ ];
656
726
  }
657
727
  function messageForCustomToolCall(item, output, index) {
658
728
  const name = stringValue(item.name) ?? "custom_tool_call";
659
729
  const itemKey = stringValue(item.call_id) ?? stringValue(item.id) ?? `item-${index}`;
660
730
  const input = nonBlankStringValue(item.input) ?? nonBlankStringValue(item.arguments) ?? "";
661
731
  if (name === "apply_patch") {
662
- return [{
663
- itemKey,
664
- body: input,
665
- structured: {
666
- kind: "codex_file_change",
667
- item_id: itemKey,
668
- transcript_unit_id: `codex_file_change:${itemKey}`,
669
- stream_state: "completed",
670
- status: output === undefined ? "started" : "completed",
671
- patch_text: input,
672
- changes: fileChangesFromPatch(input)
732
+ return [
733
+ {
734
+ itemKey,
735
+ body: input,
736
+ structured: {
737
+ kind: "codex_file_change",
738
+ item_id: itemKey,
739
+ transcript_unit_id: `codex_file_change:${itemKey}`,
740
+ stream_state: "completed",
741
+ status: output === undefined ? "started" : "completed",
742
+ patch_text: input,
743
+ changes: fileChangesFromPatch(input)
744
+ }
673
745
  }
674
- }];
746
+ ];
675
747
  }
676
748
  const outputText = toolOutputText(output);
677
749
  const command = `${name}${toolArgumentsSummary(item.input)}`;
678
- return [{
679
- itemKey,
680
- body: [`$ ${command}`, outputText].filter((part) => part.trim() !== "").join(`
681
-
682
- `),
683
- structured: codexCommandExecutionStructuredMessage(itemKey, { command, output: outputText, status: output === undefined ? "started" : "completed" }, "completed")
684
- }];
750
+ const projection = codexCommandOutputProjection(command, outputText);
751
+ return [
752
+ {
753
+ itemKey,
754
+ body: projection.body,
755
+ structured: codexCommandExecutionStructuredMessage(itemKey, {
756
+ command,
757
+ output: outputText,
758
+ status: output === undefined ? "started" : "completed"
759
+ }, "completed")
760
+ }
761
+ ];
685
762
  }
686
763
  function commandMessageForUnpairedToolOutput(item, index) {
687
764
  const itemKey = stringValue(item.call_id) ?? stringValue(item.id) ?? `item-${index}`;
688
765
  const output = toolOutputText(item);
689
- return [{
690
- itemKey,
691
- body: output,
692
- structured: codexCommandExecutionStructuredMessage(itemKey, { command: "tool output", output, status: "completed" }, "completed")
693
- }];
766
+ const projection = codexCommandOutputProjection("tool output", output);
767
+ return [
768
+ {
769
+ itemKey,
770
+ body: projection.output,
771
+ structured: codexCommandExecutionStructuredMessageFromProjection(itemKey, { status: "completed" }, "completed", projection)
772
+ }
773
+ ];
694
774
  }
695
775
  function toolArgumentsSummary(value) {
696
776
  const text = nonBlankStringValue(value);
@@ -709,13 +789,34 @@ function fileChangesFromPatch(patchText) {
709
789
  const addPrefix = "*** Add File: ";
710
790
  const deletePrefix = "*** Delete File: ";
711
791
  if (line.startsWith(updatePrefix)) {
712
- return [{ path: line.slice(updatePrefix.length).trim(), diff: "", kind: "update", move_path: "" }];
792
+ return [
793
+ {
794
+ path: line.slice(updatePrefix.length).trim(),
795
+ diff: "",
796
+ kind: "update",
797
+ move_path: ""
798
+ }
799
+ ];
713
800
  }
714
801
  if (line.startsWith(addPrefix)) {
715
- return [{ path: line.slice(addPrefix.length).trim(), diff: "", kind: "add", move_path: "" }];
802
+ return [
803
+ {
804
+ path: line.slice(addPrefix.length).trim(),
805
+ diff: "",
806
+ kind: "add",
807
+ move_path: ""
808
+ }
809
+ ];
716
810
  }
717
811
  if (line.startsWith(deletePrefix)) {
718
- return [{ path: line.slice(deletePrefix.length).trim(), diff: "", kind: "delete", move_path: "" }];
812
+ return [
813
+ {
814
+ path: line.slice(deletePrefix.length).trim(),
815
+ diff: "",
816
+ kind: "delete",
817
+ move_path: ""
818
+ }
819
+ ];
719
820
  }
720
821
  return [];
721
822
  });
@@ -2344,8 +2445,8 @@ async function attachChannelSession(args) {
2344
2445
  const session = args.options.channelSession;
2345
2446
  const chatTopic = `chat:${session.workspaceSlug}:${session.channelSlug}`;
2346
2447
  const state = initialChannelSessionState(0, session.rootSeq, session.kandanThreadId, session.codexThreadId, args.options);
2347
- const joined = await args.kandan.join(chatTopic, { last_seq: 0 }, {
2348
- rejoinPayload: () => ({ last_seq: state.minSeq })
2448
+ const joined = await args.kandan.join(chatTopic, chatJoinPayload(args.options.runnerId, state, 0), {
2449
+ rejoinPayload: () => chatJoinPayload(args.options.runnerId, state, state.minSeq)
2349
2450
  });
2350
2451
  const cursor = integerValue(joined.cursor) ?? 0;
2351
2452
  const runnerIdentity = identityFromAccessToken(args.options.token);
@@ -2491,6 +2592,19 @@ async function attachChannelSession(args) {
2491
2592
  }
2492
2593
  };
2493
2594
  }
2595
+ function chatJoinPayload(runnerId, state, lastSeq) {
2596
+ const base = { last_seq: lastSeq };
2597
+ switch (state.kandanThreadId) {
2598
+ case undefined:
2599
+ return base;
2600
+ default:
2601
+ return {
2602
+ ...base,
2603
+ runner_id: runnerId,
2604
+ thread_id: state.kandanThreadId
2605
+ };
2606
+ }
2607
+ }
2494
2608
  async function bindCurrentCodexThread(args, state) {
2495
2609
  if (state.codexThreadId === undefined) {
2496
2610
  return;
@@ -2544,7 +2658,10 @@ function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThread
2544
2658
  webSearchProgressForwardChain: Promise.resolve(),
2545
2659
  typingHeartbeat: undefined,
2546
2660
  typingHeartbeatInFlight: false,
2547
- runtimeSettings: runtimeSettingsFromOptions(options)
2661
+ runtimeSettings: runtimeSettingsFromOptions(options),
2662
+ pendingRuntimeSettingsResume: false,
2663
+ runtimeSettingsGeneration: 0,
2664
+ runtimeSettingsResumeChain: Promise.resolve()
2548
2665
  };
2549
2666
  }
2550
2667
  async function handleLostPortForwardCandidate(args, state, payloadContext, candidate) {
@@ -2682,7 +2799,7 @@ async function handleChannelSessionControl(args, state, payloadContext, control)
2682
2799
  interruptedQueuedMessages: interrupted.ok ? interrupted.selectedCount : 0
2683
2800
  };
2684
2801
  }
2685
- function updateSessionSettings(args, state, control) {
2802
+ async function updateSessionSettings(args, state, control) {
2686
2803
  if (state.codexThreadId !== control.threadId) {
2687
2804
  return;
2688
2805
  }
@@ -2694,6 +2811,8 @@ function updateSessionSettings(args, state, control) {
2694
2811
  };
2695
2812
  }
2696
2813
  state.runtimeSettings = mergeRuntimeSettings(state.runtimeSettings, control);
2814
+ state.pendingRuntimeSettingsResume = true;
2815
+ state.runtimeSettingsGeneration += 1;
2697
2816
  publishRuntimeSettings(args, state).catch((error) => {
2698
2817
  args.log("kandan.session_settings_publish_failed", {
2699
2818
  message: error instanceof Error ? error.message : String(error)
@@ -2706,6 +2825,7 @@ function updateSessionSettings(args, state, control) {
2706
2825
  sandbox: state.runtimeSettings.sandbox ?? null,
2707
2826
  fast: state.runtimeSettings.fast ?? null
2708
2827
  });
2828
+ await tryResumeCodexThreadForPendingRuntimeSettings(args, state);
2709
2829
  return {
2710
2830
  instanceId: args.instanceId,
2711
2831
  ok: true,
@@ -2716,6 +2836,71 @@ function updateSessionSettings(args, state, control) {
2716
2836
  fast: state.runtimeSettings.fast ?? null
2717
2837
  };
2718
2838
  }
2839
+ async function resumeCodexThreadForPendingRuntimeSettings(args, state) {
2840
+ const requestedGeneration = state.runtimeSettingsGeneration;
2841
+ const previousResume = state.runtimeSettingsResumeChain.catch(() => {
2842
+ return;
2843
+ });
2844
+ const resume = previousResume.then(() => performCodexThreadResumeForPendingRuntimeSettings(args, state, requestedGeneration));
2845
+ state.runtimeSettingsResumeChain = resume.then(() => {
2846
+ return;
2847
+ }, () => {
2848
+ return;
2849
+ });
2850
+ return await resume;
2851
+ }
2852
+ async function performCodexThreadResumeForPendingRuntimeSettings(args, state, requestedGeneration) {
2853
+ if (state.pendingRuntimeSettingsResume !== true || state.closed || state.turn.status !== "idle" || localTuiTurnIsActive(state)) {
2854
+ return true;
2855
+ }
2856
+ const codexThreadId = state.codexThreadId;
2857
+ if (codexThreadId === undefined) {
2858
+ return true;
2859
+ }
2860
+ if (state.runtimeSettingsGeneration !== requestedGeneration) {
2861
+ return false;
2862
+ }
2863
+ const resumeGeneration = requestedGeneration;
2864
+ const resumeSettings = state.runtimeSettings;
2865
+ const runtimeOptions = runtimeOptionsForSettings(args.options, resumeSettings);
2866
+ const resumeParams = {
2867
+ threadId: codexThreadId,
2868
+ ...codexThreadRuntimeOverrides(runtimeOptions)
2869
+ };
2870
+ args.log("codex.session_settings_resume_requested", {
2871
+ codex_thread_id: codexThreadId,
2872
+ model: resumeSettings.model ?? null,
2873
+ reasoning_effort: resumeSettings.reasoningEffort ?? null,
2874
+ approval_policy: resumeSettings.approvalPolicy ?? null,
2875
+ sandbox: resumeSettings.sandbox ?? null,
2876
+ fast: resumeSettings.fast ?? null,
2877
+ generation: resumeGeneration
2878
+ });
2879
+ const resumed = await args.codex.request("thread/resume", resumeParams);
2880
+ if ("error" in resumed) {
2881
+ throw new Error(`failed to resume Codex thread with updated settings: ${resumed.error.message}`);
2882
+ }
2883
+ if (state.pendingRuntimeSettingsResume === true && state.runtimeSettingsGeneration === resumeGeneration) {
2884
+ state.pendingRuntimeSettingsResume = false;
2885
+ }
2886
+ args.log("codex.session_settings_resume_completed", {
2887
+ codex_thread_id: codexThreadId,
2888
+ generation: resumeGeneration,
2889
+ current_generation: state.runtimeSettingsGeneration,
2890
+ pending: state.pendingRuntimeSettingsResume
2891
+ });
2892
+ return state.pendingRuntimeSettingsResume !== true;
2893
+ }
2894
+ async function tryResumeCodexThreadForPendingRuntimeSettings(args, state) {
2895
+ try {
2896
+ return await resumeCodexThreadForPendingRuntimeSettings(args, state);
2897
+ } catch (error) {
2898
+ args.log("codex.session_settings_resume_failed", {
2899
+ message: error instanceof Error ? error.message : String(error)
2900
+ });
2901
+ return false;
2902
+ }
2903
+ }
2719
2904
  async function resolvePendingCodexApprovalRequest(args, state, control) {
2720
2905
  if (state.codexThreadId !== control.threadId) {
2721
2906
  return;
@@ -3277,6 +3462,10 @@ async function drainKandanMessageQueue(args, state, payloadContext) {
3277
3462
  if (state.closed || state.turn.status !== "idle" || localTuiTurnIsActive(state)) {
3278
3463
  return;
3279
3464
  }
3465
+ const resumed = await tryResumeCodexThreadForPendingRuntimeSettings(args, state);
3466
+ if (!resumed) {
3467
+ return;
3468
+ }
3280
3469
  const next = dequeuePendingKandanMessage(state.queue);
3281
3470
  if (next === undefined) {
3282
3471
  return;
@@ -3602,11 +3791,17 @@ async function forwardCompletedCodexTurn(args, state, turnId, payloadContext) {
3602
3791
  if (completingActiveTurn && state.turn.status === "completing" && state.turn.turnId === turnId) {
3603
3792
  state.turn = { status: "idle" };
3604
3793
  await stopCodexTyping(args, state);
3605
- await drainKandanMessageQueue(args, state, payloadContext);
3794
+ const resumed = await tryResumeCodexThreadForPendingRuntimeSettings(args, state);
3795
+ if (resumed) {
3796
+ await drainKandanMessageQueue(args, state, payloadContext);
3797
+ }
3606
3798
  }
3607
3799
  if (completingLocalTuiTurn && !completingActiveTurn) {
3608
3800
  await stopCodexTyping(args, state);
3609
- await drainKandanMessageQueue(args, state, payloadContext);
3801
+ const resumed = await tryResumeCodexThreadForPendingRuntimeSettings(args, state);
3802
+ if (resumed) {
3803
+ await drainKandanMessageQueue(args, state, payloadContext);
3804
+ }
3610
3805
  }
3611
3806
  }
3612
3807
  }
@@ -3890,13 +4085,25 @@ async function forwardCommandOutputDeltaPayload(args, state, delta, payloadConte
3890
4085
  }
3891
4086
  const existing = findStreamingCommandOutput(state, delta.itemKey);
3892
4087
  const output = `${existing?.output ?? ""}${delta.delta}`;
3893
- const body = commandOutputBody("command output", output);
3894
4088
  const structured = codexCommandExecutionStructuredMessage(delta.itemKey, {
3895
4089
  command: "command output",
3896
4090
  output,
3897
4091
  processId: delta.processId,
3898
4092
  stream: delta.stream
3899
4093
  }, "streaming");
4094
+ const persistedOutput = typeof structured.output === "string" ? structured.output : output;
4095
+ const body = codexCommandOutputBody("command output", output);
4096
+ if (structured.output_truncated === true) {
4097
+ args.log("codex.command_output_truncated", {
4098
+ item_key: delta.itemKey,
4099
+ turn_id: turnId,
4100
+ process_id: delta.processId ?? null,
4101
+ stream: delta.stream,
4102
+ failure_class: stringValue(structured.output_truncation_reason) ?? null,
4103
+ original_length: integerValue(structured.output_original_length) ?? null,
4104
+ persisted_length: persistedOutput.length
4105
+ });
4106
+ }
3900
4107
  if (existing === undefined) {
3901
4108
  const session = args.options.channelSession;
3902
4109
  const reply = await pushOk(args.kandan, args.topic, "session:post_thread_message", {
@@ -3920,7 +4127,7 @@ async function forwardCommandOutputDeltaPayload(args, state, delta, payloadConte
3920
4127
  itemKey: delta.itemKey,
3921
4128
  turnId,
3922
4129
  seq,
3923
- output,
4130
+ output: persistedOutput,
3924
4131
  processId: delta.processId,
3925
4132
  stream: delta.stream
3926
4133
  });
@@ -3929,7 +4136,7 @@ async function forwardCommandOutputDeltaPayload(args, state, delta, payloadConte
3929
4136
  await editCodexStructuredOutput(args, state, existing.seq, body, structured);
3930
4137
  rememberStreamingCommandOutput(state, {
3931
4138
  ...existing,
3932
- output,
4139
+ output: persistedOutput,
3933
4140
  processId: delta.processId ?? existing.processId,
3934
4141
  stream: delta.stream
3935
4142
  });
@@ -3939,7 +4146,7 @@ async function forwardCommandOutputDeltaPayload(args, state, delta, payloadConte
3939
4146
  turn_id: turnId,
3940
4147
  process_id: delta.processId ?? null,
3941
4148
  stream: delta.stream,
3942
- output_length: output.length
4149
+ output_length: persistedOutput.length
3943
4150
  });
3944
4151
  }
3945
4152
  async function forwardFileChangeDeltaPayload(args, state, delta, payloadContext) {
@@ -4134,11 +4341,6 @@ function webSearchProgressClientMessageId(instanceId, turnId) {
4134
4341
  function webSearchProgressItemKey(turnId) {
4135
4342
  return `web-search:${turnId}`;
4136
4343
  }
4137
- function commandOutputBody(command, output) {
4138
- return [`$ ${command}`, output].filter((part) => part.trim() !== "").join(`
4139
-
4140
- `);
4141
- }
4142
4344
  async function streamCompletedCodexOutput(args, state, payloadContext, params) {
4143
4345
  if (state.kandanThreadId === undefined || state.codexThreadId === undefined) {
4144
4346
  return;
@@ -6167,30 +6369,14 @@ function prepareCodeServerLaunch(options) {
6167
6369
  if (platform !== "darwin") {
6168
6370
  return filesystemSandboxUnavailable();
6169
6371
  }
6170
- const sandboxExecBin = options.sandboxExecBin ?? "/usr/bin/sandbox-exec";
6171
- if (!existsSync2(sandboxExecBin)) {
6172
- return filesystemSandboxUnavailable();
6173
- }
6174
6372
  const codeServerExecutable = resolveCodeServerExecutable(options.codeServerBin, options.envPath ?? process.env.PATH ?? "");
6175
6373
  if (!codeServerExecutable.ok) {
6176
6374
  return filesystemSandboxUnavailable();
6177
6375
  }
6178
6376
  return {
6179
6377
  ok: true,
6180
- command: sandboxExecBin,
6181
- args: [
6182
- "-p",
6183
- codeServerSandboxProfile(options, codeServerExecutable.directory),
6184
- "--",
6185
- "/bin/sh",
6186
- "-c",
6187
- 'export HOME="$1"; export PWD="$1"; export TMPDIR="$2"; export TMP="$2"; export TEMP="$2"; shift 2; exec "$@"',
6188
- "kandan-code-server-env",
6189
- options.cwd,
6190
- join4(options.userDataDir, "tmp"),
6191
- codeServerExecutable.command,
6192
- ...codeServerArgs(options.port, options.cwd, options.userDataDir, options.extensionsDir)
6193
- ]
6378
+ command: codeServerExecutable.command,
6379
+ args: codeServerArgs(options.port, options.cwd, options.userDataDir, options.extensionsDir)
6194
6380
  };
6195
6381
  }
6196
6382
  function prepareLinuxCodeServerLaunch(options) {
@@ -6261,43 +6447,6 @@ function filesystemSandboxUnavailable() {
6261
6447
  reason: "local_editor_filesystem_sandbox_unavailable"
6262
6448
  };
6263
6449
  }
6264
- function codeServerSandboxProfile(options, codeServerBinDir) {
6265
- const readOnlyRoots = uniquePaths([
6266
- "/System",
6267
- "/Library",
6268
- "/usr",
6269
- "/bin",
6270
- "/sbin",
6271
- "/etc",
6272
- "/private/etc",
6273
- "/opt/homebrew",
6274
- "/dev",
6275
- ...options.codeServerRuntimeRoot === undefined ? [] : sandboxPathAliases(options.codeServerRuntimeRoot),
6276
- codeServerBinDir
6277
- ]);
6278
- const readWriteRoots = uniquePaths([
6279
- ...sandboxPathAliases(options.cwd),
6280
- ...sandboxPathAliases(options.userDataDir),
6281
- ...options.extensionsDir === undefined ? [] : sandboxPathAliases(options.extensionsDir)
6282
- ]);
6283
- return [
6284
- "(version 1)",
6285
- "(deny default)",
6286
- "(allow process*)",
6287
- "(allow signal (target self))",
6288
- "(allow signal (target same-sandbox))",
6289
- "(allow sysctl*)",
6290
- "(allow mach*)",
6291
- "(allow ipc*)",
6292
- "(allow network*)",
6293
- "(allow file-read-metadata)",
6294
- "(allow file-map-executable)",
6295
- '(allow file-read* (literal "/") (literal "/private") (literal "/private/var"))',
6296
- `(allow file-read* ${readOnlyRoots.map(sandboxSubpath).join(" ")})`,
6297
- `(allow file-read* file-write* ${readWriteRoots.map(sandboxSubpath).join(" ")})`
6298
- ].join(`
6299
- `);
6300
- }
6301
6450
  function resolveCodeServerExecutable(command, envPath) {
6302
6451
  if (hasPathSeparator(command)) {
6303
6452
  const directory = safeRealpathDir(command);
@@ -6338,9 +6487,6 @@ function sandboxPathAliases(path) {
6338
6487
  function uniquePaths(paths) {
6339
6488
  return Array.from(new Set(paths.filter((path) => path.length > 0)));
6340
6489
  }
6341
- function sandboxSubpath(path) {
6342
- return `(subpath "${path.replaceAll("\\", "\\\\").replaceAll('"', "\\\"")}")`;
6343
- }
6344
6490
  function prepareLocalEditorCollaboration(collaboration, runnerId, serverPort, browserBaseUrl) {
6345
6491
  if (collaboration === undefined || serverPort === undefined) {
6346
6492
  return;
@@ -6466,19 +6612,11 @@ function installDirectory(sourceDir, destinationDir) {
6466
6612
  mkdirSync3(dirname2(destinationDir), { recursive: true });
6467
6613
  cpSync(sourceDir, destinationDir, { recursive: true });
6468
6614
  }
6469
- function codeServerEnv(env, cwd, userDataDir, collaboration) {
6615
+ function codeServerEnv(env, cwd, _userDataDir, collaboration) {
6470
6616
  const { PORT: _port, ...hostEnv } = env;
6471
- const tempDir = join4(userDataDir, "tmp");
6472
6617
  const base = {
6473
6618
  ...hostEnv,
6474
- HOME: cwd,
6475
- PWD: cwd,
6476
- TMPDIR: tempDir,
6477
- TMP: tempDir,
6478
- TEMP: tempDir,
6479
- XDG_CACHE_HOME: join4(userDataDir, "xdg-cache"),
6480
- XDG_CONFIG_HOME: join4(userDataDir, "xdg-config"),
6481
- XDG_DATA_HOME: join4(userDataDir, "xdg-data")
6619
+ PWD: cwd
6482
6620
  };
6483
6621
  if (collaboration === undefined) {
6484
6622
  return base;
@@ -7955,6 +8093,9 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
7955
8093
  };
7956
8094
  });
7957
8095
  const allowedCwds = { value: [...options.allowedCwds] };
8096
+ const missingAllowedCwds = {
8097
+ value: normalizeAllowedCwds(options.missingAllowedCwds ?? [])
8098
+ };
7958
8099
  const localEditorState = {
7959
8100
  value: { status: "disabled" }
7960
8101
  };
@@ -7970,7 +8111,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
7970
8111
  codexRemoteTui: true,
7971
8112
  startInstance: allowedCwds.value.length > 0,
7972
8113
  allowedCwds: allowedCwds.value,
7973
- missingConfiguredAllowedCwds: options.missingConfiguredAllowedCwds ?? [],
8114
+ missingAllowedCwds: missingAllowedCwds.value,
7974
8115
  allowedCwdSuggestions: allowedCwdSuggestions(options.cwd, allowedCwds.value),
7975
8116
  portForwarding: liveForwardPorts.size > 0,
7976
8117
  allowedPorts: Array.from(liveForwardPorts).sort((left, right) => left - right),
@@ -7985,7 +8126,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
7985
8126
  const joinPayload = () => ({
7986
8127
  clientName: "kandan-local-codex-runner",
7987
8128
  version: "0.0.1",
7988
- workspace: options.channelSession?.workspaceSlug ?? options.workspaceSlug ?? null,
8129
+ workspace: runnerWorkspaceSlug(options) ?? null,
7989
8130
  channel: options.channelSession?.channelSlug ?? null,
7990
8131
  capabilities: capabilitiesPayload()
7991
8132
  });
@@ -8055,6 +8196,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8055
8196
  const codexThreads = options.channelSession === undefined ? await discoverCodexThreads(codex, options.cwd) : [];
8056
8197
  const discoveredCodexThreads = { value: codexThreads };
8057
8198
  const runnerHost = hostname2();
8199
+ const runtimeDefaults = runnerRuntimeDefaults(options);
8058
8200
  const instancePayload = {
8059
8201
  instanceId,
8060
8202
  codexUrl,
@@ -8062,8 +8204,10 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8062
8204
  cwd: options.cwd,
8063
8205
  hostname: runnerHost,
8064
8206
  codexThreads,
8065
- model: options.channelSession?.model ?? null,
8066
- reasoningEffort: options.channelSession?.reasoningEffort ?? null,
8207
+ workspace: runnerWorkspaceSlug(options) ?? null,
8208
+ channel: options.channelSession?.channelSlug ?? null,
8209
+ model: runtimeDefaults.model ?? null,
8210
+ reasoningEffort: runtimeDefaults.reasoningEffort ?? null,
8067
8211
  fast: options.fast ?? false
8068
8212
  };
8069
8213
  await kandan.push(topic, "instance_started", instancePayload);
@@ -8108,9 +8252,9 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8108
8252
  dynamicChannelSessions.clear();
8109
8253
  });
8110
8254
  const attachThreadSession = async (control, cwd, codexThreadId) => {
8111
- const workspaceSlug = normalizedWorkDescription(control.workspace);
8112
- const channelSlug = normalizedWorkDescription(control.channel);
8113
- const kandanThreadId = normalizedWorkDescription(control.threadId);
8255
+ const workspaceSlug = optionalThreadControlField(control, "workspace");
8256
+ const channelSlug = optionalThreadControlField(control, "channel");
8257
+ const kandanThreadId = optionalThreadControlField(control, "threadId");
8114
8258
  if (workspaceSlug === undefined || channelSlug === undefined || kandanThreadId === undefined) {
8115
8259
  return;
8116
8260
  }
@@ -8171,13 +8315,13 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8171
8315
  codexUrl,
8172
8316
  cwd: options.cwd,
8173
8317
  hostname: runnerHost,
8174
- workspace: options.channelSession?.workspaceSlug ?? options.workspaceSlug ?? null,
8318
+ workspace: runnerWorkspaceSlug(options) ?? null,
8175
8319
  channel: options.channelSession?.channelSlug ?? null,
8176
8320
  threadId: channelSession?.currentKandanThreadId() ?? null,
8177
8321
  codexThreadId: channelSession?.currentCodexThreadId() ?? null,
8178
8322
  codexThreads: discoveredCodexThreads.value,
8179
- model: options.channelSession?.model ?? null,
8180
- reasoningEffort: options.channelSession?.reasoningEffort ?? null,
8323
+ model: runtimeDefaults.model ?? null,
8324
+ reasoningEffort: runtimeDefaults.reasoningEffort ?? null,
8181
8325
  fast: options.fast ?? false,
8182
8326
  capabilities: capabilitiesPayload()
8183
8327
  });
@@ -8342,7 +8486,9 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8342
8486
  return;
8343
8487
  }
8344
8488
  if (isUpdateRunnerConfigControl(control)) {
8345
- allowedCwds.value = normalizeAllowedCwds(control.allowedCwds);
8489
+ const updatedAllowedCwds = configuredAllowedCwds(control.allowedCwds);
8490
+ allowedCwds.value = updatedAllowedCwds.allowedCwds;
8491
+ missingAllowedCwds.value = updatedAllowedCwds.missingAllowedCwds;
8346
8492
  pushHeartbeat();
8347
8493
  return;
8348
8494
  }
@@ -8374,7 +8520,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8374
8520
  if (handled !== undefined) {
8375
8521
  return handled;
8376
8522
  }
8377
- return applyControl(codex, kandan, topic, instanceId, options, allowedCwds.value, control, attachThreadSession);
8523
+ return applyControl(codex, kandan, topic, instanceId, options, allowedCwds.value, control, log, attachThreadSession);
8378
8524
  }).then((response) => {
8379
8525
  return kandan.push(topic, "codex_response", response);
8380
8526
  }).catch((error) => {
@@ -8450,7 +8596,7 @@ async function discoverCodexThreads(codex, cwd) {
8450
8596
  }
8451
8597
  function extractStartedThreadId(response) {
8452
8598
  if ("error" in response) {
8453
- return;
8599
+ throw new Error(`thread/start failed: ${response.error.message}`);
8454
8600
  }
8455
8601
  return stringValue(objectValue(objectValue(response.result)?.thread)?.id);
8456
8602
  }
@@ -8560,10 +8706,12 @@ async function prepareCodexThreadForTuiResume(codex, codexThreadId) {
8560
8706
  throw new Error(`failed to verify Codex TUI resume: ${verified.error.message}`);
8561
8707
  }
8562
8708
  }
8563
- async function applyControl(codex, kandan, topic, instanceId, options, allowedCwds, control, onStartedThread) {
8709
+ async function applyControl(codex, kandan, topic, instanceId, options, allowedCwds, control, log, onStartedThread) {
8564
8710
  switch (control.type) {
8565
8711
  case "start_instance": {
8566
8712
  const cwd = resolveAllowedCwd(control.cwd, allowedCwds);
8713
+ let startupStage = "starting_codex_session";
8714
+ let startedCodexThreadId;
8567
8715
  if (!cwd.ok) {
8568
8716
  return {
8569
8717
  instanceId,
@@ -8572,56 +8720,87 @@ async function applyControl(codex, kandan, topic, instanceId, options, allowedCw
8572
8720
  error: cwd.reason
8573
8721
  };
8574
8722
  }
8575
- if (options.codexUrl === undefined) {
8576
- ensureCodexProjectTrusted(cwd.cwd);
8723
+ const processingStatePayload = startInstanceMessageStatePayload(control, "processing", "starting Codex session");
8724
+ if (processingStatePayload !== undefined) {
8725
+ kandan.push(topic, "message_state", processingStatePayload).catch((error) => {
8726
+ log("kandan.start_instance_processing_state_push_failed", {
8727
+ message: error instanceof Error ? error.message : String(error)
8728
+ });
8729
+ });
8577
8730
  }
8578
- const developerPrompt = normalizedWorkDescription(control.developerPrompt);
8579
- const runtimeSettings = startInstanceRuntimeSettings(options, control);
8580
- const response = await codex.request("thread/start", {
8581
- cwd: cwd.cwd,
8582
- serviceName: "kandan-local-runner",
8583
- personality: "pragmatic",
8584
- developerInstructions: commanderDeveloperInstructions({
8731
+ try {
8732
+ if (options.codexUrl === undefined) {
8733
+ startupStage = "checking_project_trust";
8734
+ ensureCodexProjectTrusted(cwd.cwd);
8735
+ }
8736
+ startupStage = "starting_codex_session";
8737
+ const developerPrompt = normalizedWorkDescription(control.developerPrompt);
8738
+ const runtimeSettings = startInstanceRuntimeSettings(options, control);
8739
+ const response = await codex.request("thread/start", {
8585
8740
  cwd: cwd.cwd,
8586
- developerPrompt
8587
- }),
8588
- ...runtimeSettings.model === undefined ? {} : { model: runtimeSettings.model },
8589
- ...runtimeSettings.reasoningEffort === undefined ? {} : { reasoningEffort: runtimeSettings.reasoningEffort },
8590
- ...runtimeSettings.approvalPolicy === undefined ? {} : { approvalPolicy: runtimeSettings.approvalPolicy },
8591
- ...runtimeSettings.sandbox === undefined ? {} : { sandbox: runtimeSettings.sandbox },
8592
- ...runtimeSettings.fast === true ? { serviceTier: "fast" } : {}
8593
- });
8594
- const codexThreadId = extractStartedThreadId(response);
8595
- const workDescription = normalizedWorkDescription(control.workDescription);
8596
- if (codexThreadId !== undefined && developerPrompt !== undefined) {
8597
- await postVisibleDeveloperPrompt(kandan, topic, control, developerPrompt, codexThreadId);
8598
- }
8599
- const startedThreadSession = codexThreadId !== undefined && onStartedThread !== undefined ? await onStartedThread(control, cwd.cwd, codexThreadId) : undefined;
8600
- if (codexThreadId !== undefined && workDescription !== undefined) {
8601
- const rootSeq = integerValue(control.rootSeq);
8602
- const sourceSeq = integerValue(control.sourceSeq) ?? rootSeq;
8603
- if (startedThreadSession !== undefined && sourceSeq !== undefined) {
8604
- const identity = identityFromAccessToken(options.token);
8605
- await startedThreadSession.startThreadMessageTurn({
8606
- seq: sourceSeq,
8607
- body: workDescription,
8608
- actorSlug: identity.actorUsername,
8609
- actorUserId: identity.actorUserId
8610
- });
8611
- } else {
8612
- await codex.request("turn/start", {
8613
- threadId: codexThreadId,
8614
- input: [{ type: "text", text: workDescription }]
8615
- });
8741
+ serviceName: "kandan-local-runner",
8742
+ personality: "pragmatic",
8743
+ developerInstructions: commanderDeveloperInstructions({
8744
+ cwd: cwd.cwd,
8745
+ developerPrompt
8746
+ }),
8747
+ ...runtimeSettings.model === undefined ? {} : { model: runtimeSettings.model },
8748
+ ...runtimeSettings.reasoningEffort === undefined ? {} : { reasoningEffort: runtimeSettings.reasoningEffort },
8749
+ ...runtimeSettings.approvalPolicy === undefined ? {} : { approvalPolicy: runtimeSettings.approvalPolicy },
8750
+ ...runtimeSettings.sandbox === undefined ? {} : { sandbox: runtimeSettings.sandbox },
8751
+ ...runtimeSettings.fast === true ? { serviceTier: "fast" } : {}
8752
+ });
8753
+ const codexThreadId = extractStartedThreadId(response);
8754
+ startedCodexThreadId = codexThreadId;
8755
+ const workDescription = normalizedWorkDescription(control.workDescription);
8756
+ if (codexThreadId !== undefined && developerPrompt !== undefined) {
8757
+ await postVisibleDeveloperPrompt(kandan, topic, control, developerPrompt, codexThreadId);
8758
+ }
8759
+ let startedThreadSession;
8760
+ if (codexThreadId !== undefined && onStartedThread !== undefined) {
8761
+ startupStage = "binding_kandan_thread";
8762
+ startedThreadSession = await onStartedThread(control, cwd.cwd, codexThreadId);
8763
+ }
8764
+ if (codexThreadId !== undefined && workDescription !== undefined) {
8765
+ startupStage = "starting_first_turn";
8766
+ const rootSeq = integerValue(control.rootSeq);
8767
+ const sourceSeq = integerValue(control.sourceSeq) ?? rootSeq;
8768
+ if (startedThreadSession !== undefined && sourceSeq !== undefined) {
8769
+ const identity = identityFromAccessToken(options.token);
8770
+ await startedThreadSession.startThreadMessageTurn({
8771
+ seq: sourceSeq,
8772
+ body: workDescription,
8773
+ actorSlug: identity.actorUsername,
8774
+ actorUserId: identity.actorUserId
8775
+ });
8776
+ } else {
8777
+ await codex.request("turn/start", {
8778
+ threadId: codexThreadId,
8779
+ input: [{ type: "text", text: workDescription }]
8780
+ });
8781
+ }
8616
8782
  }
8783
+ return {
8784
+ instanceId,
8785
+ controlType: control.type,
8786
+ cwd: cwd.cwd,
8787
+ matchedRoot: cwd.matchedRoot,
8788
+ response
8789
+ };
8790
+ } catch (error) {
8791
+ const failureReason = startInstanceFailureReason(startupStage, error);
8792
+ log("kandan.start_instance_failed", {
8793
+ stage: startupStage,
8794
+ message: error instanceof Error ? error.message : String(error),
8795
+ thread_id: optionalThreadControlField(control, "threadId") ?? null,
8796
+ source_seq: integerValue(control.sourceSeq) ?? integerValue(control.rootSeq) ?? null,
8797
+ codex_thread_id: startedCodexThreadId ?? null
8798
+ });
8799
+ try {
8800
+ await publishStartInstanceMessageState(kandan, topic, control, "failed", failureReason, { codexThreadId: startedCodexThreadId });
8801
+ } catch (_publishError) {}
8802
+ throw error;
8617
8803
  }
8618
- return {
8619
- instanceId,
8620
- controlType: control.type,
8621
- cwd: cwd.cwd,
8622
- matchedRoot: cwd.matchedRoot,
8623
- response
8624
- };
8625
8804
  }
8626
8805
  case "reconnect_thread": {
8627
8806
  const cwd = resolveAllowedCwd(control.cwd, allowedCwds);
@@ -8730,6 +8909,26 @@ async function applyControl(codex, kandan, topic, instanceId, options, allowedCw
8730
8909
  return { instanceId, controlType: control.type, skipped: true };
8731
8910
  }
8732
8911
  }
8912
+ function startInstanceFailureReason(stage, error) {
8913
+ const detail = truncateFailureDetail(error instanceof Error ? error.message : String(error));
8914
+ switch (stage) {
8915
+ case "checking_project_trust":
8916
+ return `Codex could not start because this folder is not trusted locally. Trust the folder with linzumi paths add, or restart the runner from a trusted folder. Detail: ${detail}`;
8917
+ case "starting_codex_session":
8918
+ return `Codex did not create a session. Restart the local runner and try again. Detail: ${detail}`;
8919
+ case "binding_kandan_thread":
8920
+ return `Codex started, but Linzumi could not attach it to this thread. Try starting the agent again; if it repeats, share this failed message with the runner owner. Detail: ${detail}`;
8921
+ case "starting_first_turn":
8922
+ return `Codex started, but the first prompt could not be delivered. Try sending the message again; if it repeats, reconnect the local runner. Detail: ${detail}`;
8923
+ }
8924
+ }
8925
+ function truncateFailureDetail(message) {
8926
+ const trimmed = message.trim();
8927
+ if (trimmed.length <= 240) {
8928
+ return trimmed;
8929
+ }
8930
+ return `${trimmed.slice(0, 237)}...`;
8931
+ }
8733
8932
  function commanderDeveloperInstructions(args) {
8734
8933
  const customPrompt = args.developerPrompt === undefined ? "" : `
8735
8934
  <invoker_developer_prompt>
@@ -8786,9 +8985,9 @@ secure tunnel, and keep the Linzumi thread truthful.
8786
8985
  </task_reminder>`;
8787
8986
  }
8788
8987
  async function postVisibleDeveloperPrompt(kandan, topic, control, developerPrompt, codexThreadId) {
8789
- const workspace = normalizedWorkDescription(control.workspace);
8790
- const channel = normalizedWorkDescription(control.channel);
8791
- const threadId = normalizedWorkDescription(control.threadId);
8988
+ const workspace = optionalThreadControlField(control, "workspace");
8989
+ const channel = optionalThreadControlField(control, "channel");
8990
+ const threadId = optionalThreadControlField(control, "threadId");
8792
8991
  if (workspace === undefined || channel === undefined || threadId === undefined) {
8793
8992
  return;
8794
8993
  }
@@ -8810,24 +9009,81 @@ ${developerPrompt}`,
8810
9009
  client_message_id: `codex-start-instructions-${threadId}`
8811
9010
  });
8812
9011
  }
9012
+ async function publishStartInstanceMessageState(kandan, topic, control, status, reason, diagnostics = {}) {
9013
+ const payload = startInstanceMessageStatePayload(control, status, reason, diagnostics);
9014
+ if (payload === undefined) {
9015
+ return;
9016
+ }
9017
+ await kandan.push(topic, "message_state", payload);
9018
+ }
9019
+ function startInstanceMessageStatePayload(control, status, reason, diagnostics = {}) {
9020
+ const rootSeq = integerValue(control.rootSeq);
9021
+ const sourceSeq = integerValue(control.sourceSeq) ?? rootSeq;
9022
+ if (sourceSeq === undefined) {
9023
+ return;
9024
+ }
9025
+ const workspace = requiredStartInstanceControlField(control, "workspace");
9026
+ const channel = requiredStartInstanceControlField(control, "channel");
9027
+ const threadId = requiredStartInstanceControlField(control, "threadId");
9028
+ return {
9029
+ workspace,
9030
+ channel,
9031
+ thread_id: threadId,
9032
+ seq: sourceSeq,
9033
+ status,
9034
+ reason,
9035
+ ...diagnostics.codexThreadId === undefined ? {} : { codex_thread_id: diagnostics.codexThreadId }
9036
+ };
9037
+ }
9038
+ function requiredStartInstanceControlField(control, field) {
9039
+ return requiredThreadControlField(control, field);
9040
+ }
9041
+ function requiredThreadControlField(control, field) {
9042
+ const value = optionalThreadControlField(control, field);
9043
+ if (value === undefined) {
9044
+ throw new Error(`${control.type} control missing ${field}`);
9045
+ }
9046
+ return value;
9047
+ }
9048
+ function optionalThreadControlField(control, field) {
9049
+ const value = stringValue(control[field])?.trim();
9050
+ if (value === undefined || value === "") {
9051
+ return;
9052
+ }
9053
+ return value;
9054
+ }
8813
9055
  function startInstanceRuntimeSettings(options, control) {
8814
- const session = options.channelSession;
9056
+ const defaults = runnerRuntimeDefaults(options);
8815
9057
  return {
8816
- model: control.model ?? session?.model,
8817
- reasoningEffort: control.reasoningEffort ?? session?.reasoningEffort,
8818
- approvalPolicy: control.approvalPolicy ?? session?.approvalPolicy,
8819
- sandbox: control.sandbox ?? session?.sandbox,
9058
+ model: control.model ?? defaults.model,
9059
+ reasoningEffort: control.reasoningEffort ?? defaults.reasoningEffort,
9060
+ approvalPolicy: control.approvalPolicy ?? defaults.approvalPolicy,
9061
+ sandbox: control.sandbox ?? defaults.sandbox,
8820
9062
  fast: control.fast ?? options.fast
8821
9063
  };
8822
9064
  }
8823
9065
  async function startOwnedCodexAppServer(options) {
8824
9066
  ensureCodexProjectTrusted(options.cwd);
9067
+ const defaults = runnerRuntimeDefaults(options);
8825
9068
  return await startCodexAppServer(options.codexBin, options.cwd, {
8826
- model: options.channelSession?.model,
8827
- reasoningEffort: options.channelSession?.reasoningEffort,
9069
+ model: defaults.model,
9070
+ reasoningEffort: defaults.reasoningEffort,
8828
9071
  fast: options.fast
8829
9072
  });
8830
9073
  }
9074
+ function runnerWorkspaceSlug(options) {
9075
+ return options.channelSession?.workspaceSlug ?? options.workspaceSlug;
9076
+ }
9077
+ function runnerRuntimeDefaults(options) {
9078
+ const session = options.channelSession;
9079
+ const defaults = options.runtimeDefaults;
9080
+ return {
9081
+ model: defaults?.model ?? session?.model,
9082
+ reasoningEffort: defaults?.reasoningEffort ?? session?.reasoningEffort,
9083
+ approvalPolicy: defaults?.approvalPolicy ?? session?.approvalPolicy,
9084
+ sandbox: defaults?.sandbox ?? session?.sandbox
9085
+ };
9086
+ }
8831
9087
  function isUpdateRunnerConfigControl(control) {
8832
9088
  return control.type === "update_runner_config";
8833
9089
  }
@@ -8840,6 +9096,30 @@ function normalizeAllowedCwds(values) {
8840
9096
  return normalized === "" ? [] : [normalized];
8841
9097
  })));
8842
9098
  }
9099
+ function configuredAllowedCwds(values) {
9100
+ const allowedCwds = [];
9101
+ const missingAllowedCwds = [];
9102
+ for (const value of normalizeAllowedCwds(values)) {
9103
+ const absolutePath = resolve5(expandUserPath(value));
9104
+ try {
9105
+ const realPath = realpathSync4(absolutePath);
9106
+ allowedCwds.push(...realPath === absolutePath ? [realPath] : [realPath, absolutePath]);
9107
+ } catch (error) {
9108
+ if (isMissingAllowedCwdError(error)) {
9109
+ missingAllowedCwds.push(absolutePath);
9110
+ continue;
9111
+ }
9112
+ throw error;
9113
+ }
9114
+ }
9115
+ return {
9116
+ allowedCwds: normalizeAllowedCwds(allowedCwds),
9117
+ missingAllowedCwds: normalizeAllowedCwds(missingAllowedCwds)
9118
+ };
9119
+ }
9120
+ function isMissingAllowedCwdError(error) {
9121
+ return typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR" || error.code === "EACCES" || error.code === "ELOOP" || error.code === "EIO");
9122
+ }
8843
9123
  function allowedCwdSuggestions(cwd, allowedCwds) {
8844
9124
  return normalizeAllowedCwds([cwd, ...allowedCwds]);
8845
9125
  }
@@ -8988,14 +9268,14 @@ import {
8988
9268
  existsSync as existsSync5,
8989
9269
  mkdirSync as mkdirSync6,
8990
9270
  readFileSync as readFileSync4,
8991
- realpathSync as realpathSync4,
9271
+ realpathSync as realpathSync5,
8992
9272
  writeFileSync as writeFileSync5
8993
9273
  } from "node:fs";
8994
9274
  import { homedir as homedir6 } from "node:os";
8995
- import { dirname as dirname5, resolve as resolve5 } from "node:path";
9275
+ import { dirname as dirname5, resolve as resolve6 } from "node:path";
8996
9276
  function localConfigPath(env = process.env) {
8997
9277
  const override = env.LINZUMI_CONFIG_FILE;
8998
- return override !== undefined && override.trim() !== "" ? resolve5(expandUserPath(override)) : resolve5(homedir6(), ".linzumi", "config.json");
9278
+ return override !== undefined && override.trim() !== "" ? resolve6(expandUserPath(override)) : resolve6(homedir6(), ".linzumi", "config.json");
8999
9279
  }
9000
9280
  function readLocalConfig(path = localConfigPath()) {
9001
9281
  if (!existsSync5(path)) {
@@ -9010,32 +9290,36 @@ function readLocalConfig(path = localConfigPath()) {
9010
9290
  allowedCwds: uniqueStrings(parsed.allowedCwds)
9011
9291
  };
9012
9292
  }
9013
- function readConfiguredAllowedCwdState(path = localConfigPath()) {
9293
+ function readConfiguredAllowedCwdDetails(path = localConfigPath()) {
9014
9294
  const allowedCwds = [];
9015
- const missingCwds = [];
9295
+ const missingAllowedCwds = [];
9016
9296
  for (const cwd of readLocalConfig(path).allowedCwds) {
9297
+ const absolutePath = resolve6(expandUserPath(cwd));
9017
9298
  try {
9018
- const absolutePath = resolve5(expandUserPath(cwd));
9019
- const realPath = realpathSync4(absolutePath);
9299
+ const realPath = realpathSync5(absolutePath);
9020
9300
  allowedCwds.push(...realPath === absolutePath ? [realPath] : [realPath, absolutePath]);
9021
- } catch (_error) {
9022
- missingCwds.push(cwd);
9301
+ } catch (error) {
9302
+ if (isMissingPathError(error)) {
9303
+ missingAllowedCwds.push(absolutePath);
9304
+ continue;
9305
+ }
9306
+ throw error;
9023
9307
  }
9024
9308
  }
9025
9309
  return {
9026
9310
  allowedCwds: uniqueStrings(allowedCwds),
9027
- missingCwds: uniqueStrings(missingCwds)
9311
+ missingAllowedCwds: uniqueStrings(missingAllowedCwds)
9028
9312
  };
9029
9313
  }
9030
9314
  function addAllowedCwd(pathValue, path = localConfigPath()) {
9031
- const normalizedPath = realpathSync4(resolve5(expandUserPath(pathValue)));
9315
+ const normalizedPath = realpathSync5(resolve6(expandUserPath(pathValue)));
9032
9316
  const config = readLocalConfig(path);
9033
9317
  const allowedCwds = uniqueStrings([...config.allowedCwds, normalizedPath]);
9034
9318
  writeLocalConfig({ version: 1, allowedCwds }, path);
9035
9319
  return allowedCwds;
9036
9320
  }
9037
9321
  function removeAllowedCwd(pathValue, path = localConfigPath()) {
9038
- const requestedPath = resolve5(expandUserPath(pathValue));
9322
+ const requestedPath = resolve6(expandUserPath(pathValue));
9039
9323
  const normalizedRequest = realpathOrResolved(requestedPath);
9040
9324
  const config = readLocalConfig(path);
9041
9325
  const allowedCwds = config.allowedCwds.filter((cwd) => {
@@ -9058,11 +9342,14 @@ function uniqueStrings(values) {
9058
9342
  ...new Set(values.map((value) => value.trim()).filter((value) => value !== ""))
9059
9343
  ];
9060
9344
  }
9345
+ function isMissingPathError(error) {
9346
+ return typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR" || error.code === "EACCES" || error.code === "ELOOP" || error.code === "EIO");
9347
+ }
9061
9348
  function realpathOrResolved(pathValue) {
9062
9349
  try {
9063
- return realpathSync4(resolve5(expandUserPath(pathValue)));
9350
+ return realpathSync5(resolve6(expandUserPath(pathValue)));
9064
9351
  } catch (_error) {
9065
- return resolve5(expandUserPath(pathValue));
9352
+ return resolve6(expandUserPath(pathValue));
9066
9353
  }
9067
9354
  }
9068
9355
 
@@ -9460,7 +9747,7 @@ async function runClaim(command, deps) {
9460
9747
  workspaceName: stringValue(response.workspace_name),
9461
9748
  channelId: stringValue(response.channel_id),
9462
9749
  ownerUsername: stringValue(response.owner_username),
9463
- channelUrl: requiredString(response, "channel_url"),
9750
+ channelUrl: stringValue(response.channel_url),
9464
9751
  loginUrl: requiredString(response, "login_url"),
9465
9752
  supportChannelId: requiredString(response, "support_channel_id"),
9466
9753
  supportChannelUrl: requiredString(response, "support_channel_url"),
@@ -9479,8 +9766,10 @@ async function runClaim(command, deps) {
9479
9766
  deps.stdout.write(`login_url: ${tokenFile.loginUrl}
9480
9767
  `);
9481
9768
  writeHumanLoginUrlWarning(deps);
9482
- deps.stdout.write(`channel_url: ${tokenFile.channelUrl}
9769
+ if (tokenFile.channelUrl !== undefined) {
9770
+ deps.stdout.write(`channel_url: ${tokenFile.channelUrl}
9483
9771
  `);
9772
+ }
9484
9773
  deps.stdout.write(`support_channel_id: ${tokenFile.supportChannelId}
9485
9774
  `);
9486
9775
  deps.stdout.write(`support_channel_url: ${tokenFile.supportChannelUrl}
@@ -9749,7 +10038,7 @@ function readStoredAgentTokenFile(path, readTextFile = readOptionalTextFile) {
9749
10038
  workspaceName: stringValue(parsed.workspaceName),
9750
10039
  channelId: stringValue(parsed.channelId),
9751
10040
  ownerUsername: stringValue(parsed.ownerUsername),
9752
- channelUrl: requiredString(parsed, "channelUrl"),
10041
+ channelUrl: stringValue(parsed.channelUrl),
9753
10042
  loginUrl: requiredString(parsed, "loginUrl"),
9754
10043
  supportChannelId: requiredString(parsed, "supportChannelId"),
9755
10044
  supportChannelUrl: stringValue(parsed.supportChannelUrl),
@@ -9803,7 +10092,7 @@ Launch target:
9803
10092
 
9804
10093
  // src/helloLinzumiProject.ts
9805
10094
  import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
9806
- import { dirname as dirname7, join as join9, resolve as resolve6 } from "node:path";
10095
+ import { dirname as dirname7, join as join9, resolve as resolve7 } from "node:path";
9807
10096
  import { fileURLToPath } from "node:url";
9808
10097
  var defaultHelloLinzumiProjectDir = "/tmp/hello_linzumi";
9809
10098
  var defaultHelloLinzumiProjectName = "hello_linzumi";
@@ -9837,10 +10126,10 @@ function resolveHelloProjectRoot(options) {
9837
10126
  throw new Error("linzumi init-hello-linzumi-demo-app accepts either --dir or --parent-dir/--name, not both");
9838
10127
  }
9839
10128
  if (options.rootPath !== undefined) {
9840
- return resolve6(options.rootPath);
10129
+ return resolve7(options.rootPath);
9841
10130
  }
9842
10131
  const name = normalizeProjectName(options.name ?? defaultHelloLinzumiProjectName);
9843
- return resolve6(options.parentDir ?? defaultHelloLinzumiParentDir, name);
10132
+ return resolve7(options.parentDir ?? defaultHelloLinzumiParentDir, name);
9844
10133
  }
9845
10134
  function normalizeProjectName(value) {
9846
10135
  const name = value.trim();
@@ -10375,7 +10664,7 @@ import {
10375
10664
  writeFileSync as writeFileSync8
10376
10665
  } from "node:fs";
10377
10666
  import { homedir as homedir8 } from "node:os";
10378
- import { dirname as dirname8, join as join10, resolve as resolve7 } from "node:path";
10667
+ import { dirname as dirname8, join as join10, resolve as resolve8 } from "node:path";
10379
10668
  import { execFileSync, spawn as spawn7 } from "node:child_process";
10380
10669
  import { fileURLToPath as fileURLToPath2 } from "node:url";
10381
10670
  var connectedMarker = "Runner connected:";
@@ -10391,7 +10680,7 @@ function defaultCommanderLogFile(runnerId, statusDir = commanderStatusDir()) {
10391
10680
  function startCommanderDaemon(options) {
10392
10681
  const statusDir = options.statusDir ?? commanderStatusDir();
10393
10682
  const statusFile = commanderStatusFile(options.runnerId, statusDir);
10394
- const logFile = resolve7(options.logFile ?? defaultCommanderLogFile(options.runnerId, statusDir));
10683
+ const logFile = resolve8(options.logFile ?? defaultCommanderLogFile(options.runnerId, statusDir));
10395
10684
  const entrypoint = options.entrypoint ?? currentEntrypoint();
10396
10685
  const nodeBin = options.nodeBin ?? process.execPath;
10397
10686
  const command = [
@@ -10588,7 +10877,7 @@ function safeRunnerId(runnerId) {
10588
10877
  }
10589
10878
  async function waitForFileChangeOrTimeout(path, deadline, now, ready2 = () => false) {
10590
10879
  const remaining = Math.max(0, deadline - now());
10591
- await new Promise((resolve8) => {
10880
+ await new Promise((resolve9) => {
10592
10881
  let resolved = false;
10593
10882
  let watcher;
10594
10883
  const finish = () => {
@@ -10598,7 +10887,7 @@ async function waitForFileChangeOrTimeout(path, deadline, now, ready2 = () => fa
10598
10887
  resolved = true;
10599
10888
  watcher?.close();
10600
10889
  clearTimeout(timer);
10601
- resolve8();
10890
+ resolve9();
10602
10891
  };
10603
10892
  const timer = setTimeout(finish, remaining);
10604
10893
  try {
@@ -10678,7 +10967,7 @@ function isMainModule() {
10678
10967
  if (scriptPath === undefined) {
10679
10968
  return false;
10680
10969
  }
10681
- return fileURLToPath3(import.meta.url) === resolve8(scriptPath);
10970
+ return fileURLToPath3(import.meta.url) === resolve9(scriptPath);
10682
10971
  }
10683
10972
  async function main(args) {
10684
10973
  const parsed = parseCommand(args);
@@ -10687,7 +10976,7 @@ async function main(args) {
10687
10976
  process.stdout.write(connectGuideText());
10688
10977
  return;
10689
10978
  case "version":
10690
- process.stdout.write(`linzumi 0.0.39-beta
10979
+ process.stdout.write(`linzumi 0.0.41-beta
10691
10980
  `);
10692
10981
  return;
10693
10982
  case "auth":
@@ -10840,7 +11129,7 @@ function runPathsCommand(args) {
10840
11129
  if (pathValue === undefined || pathValue.trim() === "") {
10841
11130
  throw new Error("missing path for linzumi paths add");
10842
11131
  }
10843
- const trustedPath = realpathSync5(resolve8(expandUserPath(pathValue)));
11132
+ const trustedPath = realpathSync6(resolve9(expandUserPath(pathValue)));
10844
11133
  addAllowedCwd(pathValue);
10845
11134
  process.stdout.write(`Trusted ${trustedPath}
10846
11135
  `);
@@ -11030,6 +11319,7 @@ async function parseStartRunnerArgs(args, deps = {
11030
11319
  kandanUrl,
11031
11320
  token: targetToken,
11032
11321
  runnerId: stringValue3(values, "runner-id") ?? `runner-${randomUUID3()}`,
11322
+ workspaceSlug: target.workspaceSlug,
11033
11323
  cwd,
11034
11324
  codexBin,
11035
11325
  codexUrl: stringValue3(values, "codex-url"),
@@ -11042,6 +11332,7 @@ async function parseStartRunnerArgs(args, deps = {
11042
11332
  editorRuntime: editorRuntime.runtime,
11043
11333
  socketFactory: trustedWebSocketFactory(kandanTlsTrustFromEnv()),
11044
11334
  dependencyStatus,
11335
+ runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
11045
11336
  channelSession: {
11046
11337
  workspaceSlug: target.workspaceSlug,
11047
11338
  channelSlug: target.channelSlug,
@@ -11073,12 +11364,23 @@ async function parseAgentRunnerArgs(args, deps = {
11073
11364
  const tokenFilePath = stringValue3(values, "agent-token-file") ?? defaultAgentTokenFilePath();
11074
11365
  const tokenFile = readStoredAgentTokenFile(tokenFilePath, deps.readTextFile);
11075
11366
  const channelSlug = tokenFile.channelId;
11076
- const listenUser = channelSlug === undefined ? undefined : stringValue3(values, "listen-user") ?? requiredStoredOwnerUsername(tokenFile.ownerUsername);
11367
+ rejectWorkspaceCommanderThreadFlags(values, channelSlug);
11368
+ const channelSession = channelSlug === undefined ? undefined : {
11369
+ workspaceSlug: tokenFile.workspaceId,
11370
+ channelSlug,
11371
+ kandanThreadId: stringValue3(values, "linzumi-thread-id"),
11372
+ listenUser: stringValue3(values, "listen-user") ?? requiredStoredOwnerUsername(tokenFile.ownerUsername),
11373
+ model: stringValue3(values, "model"),
11374
+ reasoningEffort: stringValue3(values, "reasoning-effort"),
11375
+ sandbox: stringValue3(values, "sandbox"),
11376
+ approvalPolicy: stringValue3(values, "approval-policy"),
11377
+ streamFlushMs: positiveIntegerValue(values, "stream-flush-ms")
11378
+ };
11077
11379
  const kandanUrl = stringValue3(values, "linzumi-url") ?? agentApiUrlToKandanUrl(tokenFile.apiUrl);
11078
11380
  const requestedCwdValue = cwdArg ?? stringValue3(values, "cwd");
11079
11381
  const requestedCwd = resolveUserPath(requestedCwdValue ?? process.cwd());
11080
- const configuredAllowedCwdState = values.has("allowed-cwd") || requestedCwdValue !== undefined ? { allowedCwds: [], missingCwds: [] } : readConfiguredAllowedCwdState();
11081
- const allowedCwds = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : requestedCwdValue === undefined ? configuredAllowedCwdState.allowedCwds : assertConfiguredAllowedCwds([requestedCwd]);
11382
+ const configuredAllowedCwds2 = requestedCwdValue === undefined && !values.has("allowed-cwd") ? readConfiguredAllowedCwdDetails() : { allowedCwds: [], missingAllowedCwds: [] };
11383
+ const allowedCwds = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : requestedCwdValue === undefined ? configuredAllowedCwds2.allowedCwds.length > 0 ? [...configuredAllowedCwds2.allowedCwds] : assertConfiguredAllowedCwds([requestedCwd]) : assertConfiguredAllowedCwds([requestedCwd]);
11082
11384
  const cwd = allowedCwds[0] ?? requestedCwd;
11083
11385
  const codexBin = stringValue3(values, "codex-bin") ?? "codex";
11084
11386
  const customCodeServerBin = stringValue3(values, "code-server-bin");
@@ -11105,6 +11407,7 @@ async function parseAgentRunnerArgs(args, deps = {
11105
11407
  kandanUrl,
11106
11408
  token: tokenFile.commanderToken,
11107
11409
  runnerId: stringValue3(values, "runner-id") ?? `agent-runner-${randomUUID3()}`,
11410
+ workspaceSlug: tokenFile.workspaceId,
11108
11411
  cwd,
11109
11412
  codexBin,
11110
11413
  codexUrl: stringValue3(values, "codex-url"),
@@ -11112,24 +11415,14 @@ async function parseAgentRunnerArgs(args, deps = {
11112
11415
  fast: values.get("fast") === true,
11113
11416
  logFile: stringValue3(values, "log-file"),
11114
11417
  allowedCwds,
11115
- missingConfiguredAllowedCwds: configuredAllowedCwdState.missingCwds,
11418
+ missingAllowedCwds: configuredAllowedCwds2.missingAllowedCwds,
11116
11419
  allowedForwardPorts: parseAllowedPortList(stringValue3(values, "forward-port")),
11117
11420
  codeServerBin: editorRuntime.codeServerBin,
11118
11421
  editorRuntime: editorRuntime.runtime,
11119
11422
  socketFactory: trustedWebSocketFactory(kandanTlsTrustFromEnv()),
11120
11423
  dependencyStatus,
11121
- workspaceSlug: tokenFile.workspaceId,
11122
- channelSession: channelSlug === undefined || listenUser === undefined ? undefined : {
11123
- workspaceSlug: tokenFile.workspaceId,
11124
- channelSlug,
11125
- kandanThreadId: stringValue3(values, "linzumi-thread-id"),
11126
- listenUser,
11127
- model: stringValue3(values, "model"),
11128
- reasoningEffort: stringValue3(values, "reasoning-effort"),
11129
- sandbox: stringValue3(values, "sandbox"),
11130
- approvalPolicy: stringValue3(values, "approval-policy"),
11131
- streamFlushMs: positiveIntegerValue(values, "stream-flush-ms")
11132
- }
11424
+ runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
11425
+ channelSession
11133
11426
  };
11134
11427
  }
11135
11428
  function readAgentTokenTextFile(path) {
@@ -11147,6 +11440,12 @@ function rejectAgentRunnerTargetingFlags(values) {
11147
11440
  throw new Error(`linzumi commander uses the claimed human Commander token scope; remove ${unsupportedFlags.map((flag) => `--${flag}`).join(", ")}.`);
11148
11441
  }
11149
11442
  }
11443
+ function rejectWorkspaceCommanderThreadFlags(values, channelSlug) {
11444
+ if (channelSlug !== undefined || !values.has("linzumi-thread-id")) {
11445
+ return;
11446
+ }
11447
+ throw new Error("linzumi commander cannot bind --linzumi-thread-id because the agent token file has no channelId");
11448
+ }
11150
11449
  function requiredStoredOwnerUsername(ownerUsername) {
11151
11450
  if (ownerUsername !== undefined) {
11152
11451
  return ownerUsername;
@@ -11203,7 +11502,7 @@ async function parseRunnerArgs(args, deps = {
11203
11502
  process.exit(0);
11204
11503
  }
11205
11504
  if (values.get("version") === true) {
11206
- process.stdout.write(`linzumi 0.0.39-beta
11505
+ process.stdout.write(`linzumi 0.0.41-beta
11207
11506
  `);
11208
11507
  process.exit(0);
11209
11508
  }
@@ -11211,10 +11510,8 @@ async function parseRunnerArgs(args, deps = {
11211
11510
  const kandanUrl = required(values, "linzumi-url");
11212
11511
  const cwd = stringValue3(values, "cwd") ?? process.cwd();
11213
11512
  const cwdAllowedCwds = assertConfiguredAllowedCwds([cwd]);
11214
- const configuredAllowedCwdState = values.has("allowed-cwd") ? {
11215
- allowedCwds: assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))),
11216
- missingCwds: []
11217
- } : readConfiguredAllowedCwdState();
11513
+ const localConfiguredAllowedCwds = values.has("allowed-cwd") ? { allowedCwds: [], missingAllowedCwds: [] } : readConfiguredAllowedCwdDetails();
11514
+ const configuredAllowedCwds2 = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : [...localConfiguredAllowedCwds.allowedCwds];
11218
11515
  const codexBin = stringValue3(values, "codex-bin") ?? "codex";
11219
11516
  const customCodeServerBin = stringValue3(values, "code-server-bin");
11220
11517
  const explicitToken = stringValue3(values, "token");
@@ -11254,17 +11551,26 @@ async function parseRunnerArgs(args, deps = {
11254
11551
  launchTui: values.get("launch-tui") === true,
11255
11552
  fast: values.get("fast") === true,
11256
11553
  logFile: stringValue3(values, "log-file"),
11257
- allowedCwds: Array.from(new Set([...cwdAllowedCwds, ...configuredAllowedCwdState.allowedCwds])),
11258
- missingConfiguredAllowedCwds: configuredAllowedCwdState.missingCwds,
11554
+ allowedCwds: Array.from(new Set([...cwdAllowedCwds, ...configuredAllowedCwds2])),
11555
+ missingAllowedCwds: localConfiguredAllowedCwds.missingAllowedCwds,
11259
11556
  allowedForwardPorts: parseAllowedPortList(stringValue3(values, "forward-port")),
11260
11557
  codeServerBin: editorRuntime.codeServerBin,
11261
11558
  editorRuntime: editorRuntime.runtime,
11262
11559
  socketFactory: trustedWebSocketFactory(kandanTlsTrustFromEnv()),
11263
11560
  dependencyStatus,
11264
11561
  workspaceSlug: channelSession?.workspaceSlug ?? singleWorkspaceScopeFromAccessToken(token),
11562
+ runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
11265
11563
  channelSession
11266
11564
  };
11267
11565
  }
11566
+ function runnerRuntimeDefaultsFromValues(values) {
11567
+ return {
11568
+ model: stringValue3(values, "model"),
11569
+ reasoningEffort: stringValue3(values, "reasoning-effort"),
11570
+ approvalPolicy: stringValue3(values, "approval-policy"),
11571
+ sandbox: stringValue3(values, "sandbox")
11572
+ };
11573
+ }
11268
11574
  function strictFlagValues(args, definitions = flagDefinitions) {
11269
11575
  const values = new Map;
11270
11576
  for (let index = 0;index < args.length; index += 1) {
@@ -11359,9 +11665,9 @@ function resolveUserPath(pathValue) {
11359
11665
  return homedir9();
11360
11666
  }
11361
11667
  if (pathValue.startsWith("~/")) {
11362
- return resolve8(homedir9(), pathValue.slice(2));
11668
+ return resolve9(homedir9(), pathValue.slice(2));
11363
11669
  }
11364
- return resolve8(pathValue);
11670
+ return resolve9(pathValue);
11365
11671
  }
11366
11672
  function parseChannelSession(values, token, target) {
11367
11673
  if (target === undefined) {
@@ -11645,10 +11951,10 @@ Usage:
11645
11951
 
11646
11952
  What it does:
11647
11953
  Starts this computer as the claimed human's scoped Linzumi Commander. The command
11648
- reads ~/.linzumi/agent-token.json, uses its workspace/channel scope, reads
11649
- trusted folders from ~/.linzumi/config.json when no folder is passed, and
11650
- listens only to the owning human recorded during claim unless --listen-user is
11651
- passed.
11954
+ reads ~/.linzumi/agent-token.json, connects to its workspace even when no
11955
+ channel is assigned, reads trusted folders from ~/.linzumi/config.json when no
11956
+ folder is passed, and uses the token's channel scope for channel-bound startup
11957
+ when one is present.
11652
11958
 
11653
11959
  Options:
11654
11960
  --agent-token-file <path> Agent token cache, default ~/.linzumi/agent-token.json