@linzumi/cli 0.0.39-beta → 0.0.40-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 +345 -134
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -63,7 +63,7 @@ Install the CLI or run it with `npx`:
63
63
 
64
64
  ```bash
65
65
  npm install -g @linzumi/cli@latest
66
- npx -y @linzumi/cli@0.0.39-beta --version
66
+ npx -y @linzumi/cli@0.0.40-beta --version
67
67
  linzumi --version
68
68
  ```
69
69
 
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";
@@ -2544,7 +2545,10 @@ function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThread
2544
2545
  webSearchProgressForwardChain: Promise.resolve(),
2545
2546
  typingHeartbeat: undefined,
2546
2547
  typingHeartbeatInFlight: false,
2547
- runtimeSettings: runtimeSettingsFromOptions(options)
2548
+ runtimeSettings: runtimeSettingsFromOptions(options),
2549
+ pendingRuntimeSettingsResume: false,
2550
+ runtimeSettingsGeneration: 0,
2551
+ runtimeSettingsResumeChain: Promise.resolve()
2548
2552
  };
2549
2553
  }
2550
2554
  async function handleLostPortForwardCandidate(args, state, payloadContext, candidate) {
@@ -2682,7 +2686,7 @@ async function handleChannelSessionControl(args, state, payloadContext, control)
2682
2686
  interruptedQueuedMessages: interrupted.ok ? interrupted.selectedCount : 0
2683
2687
  };
2684
2688
  }
2685
- function updateSessionSettings(args, state, control) {
2689
+ async function updateSessionSettings(args, state, control) {
2686
2690
  if (state.codexThreadId !== control.threadId) {
2687
2691
  return;
2688
2692
  }
@@ -2694,6 +2698,8 @@ function updateSessionSettings(args, state, control) {
2694
2698
  };
2695
2699
  }
2696
2700
  state.runtimeSettings = mergeRuntimeSettings(state.runtimeSettings, control);
2701
+ state.pendingRuntimeSettingsResume = true;
2702
+ state.runtimeSettingsGeneration += 1;
2697
2703
  publishRuntimeSettings(args, state).catch((error) => {
2698
2704
  args.log("kandan.session_settings_publish_failed", {
2699
2705
  message: error instanceof Error ? error.message : String(error)
@@ -2706,6 +2712,7 @@ function updateSessionSettings(args, state, control) {
2706
2712
  sandbox: state.runtimeSettings.sandbox ?? null,
2707
2713
  fast: state.runtimeSettings.fast ?? null
2708
2714
  });
2715
+ await tryResumeCodexThreadForPendingRuntimeSettings(args, state);
2709
2716
  return {
2710
2717
  instanceId: args.instanceId,
2711
2718
  ok: true,
@@ -2716,6 +2723,71 @@ function updateSessionSettings(args, state, control) {
2716
2723
  fast: state.runtimeSettings.fast ?? null
2717
2724
  };
2718
2725
  }
2726
+ async function resumeCodexThreadForPendingRuntimeSettings(args, state) {
2727
+ const requestedGeneration = state.runtimeSettingsGeneration;
2728
+ const previousResume = state.runtimeSettingsResumeChain.catch(() => {
2729
+ return;
2730
+ });
2731
+ const resume = previousResume.then(() => performCodexThreadResumeForPendingRuntimeSettings(args, state, requestedGeneration));
2732
+ state.runtimeSettingsResumeChain = resume.then(() => {
2733
+ return;
2734
+ }, () => {
2735
+ return;
2736
+ });
2737
+ return await resume;
2738
+ }
2739
+ async function performCodexThreadResumeForPendingRuntimeSettings(args, state, requestedGeneration) {
2740
+ if (state.pendingRuntimeSettingsResume !== true || state.closed || state.turn.status !== "idle" || localTuiTurnIsActive(state)) {
2741
+ return true;
2742
+ }
2743
+ const codexThreadId = state.codexThreadId;
2744
+ if (codexThreadId === undefined) {
2745
+ return true;
2746
+ }
2747
+ if (state.runtimeSettingsGeneration !== requestedGeneration) {
2748
+ return false;
2749
+ }
2750
+ const resumeGeneration = requestedGeneration;
2751
+ const resumeSettings = state.runtimeSettings;
2752
+ const runtimeOptions = runtimeOptionsForSettings(args.options, resumeSettings);
2753
+ const resumeParams = {
2754
+ threadId: codexThreadId,
2755
+ ...codexThreadRuntimeOverrides(runtimeOptions)
2756
+ };
2757
+ args.log("codex.session_settings_resume_requested", {
2758
+ codex_thread_id: codexThreadId,
2759
+ model: resumeSettings.model ?? null,
2760
+ reasoning_effort: resumeSettings.reasoningEffort ?? null,
2761
+ approval_policy: resumeSettings.approvalPolicy ?? null,
2762
+ sandbox: resumeSettings.sandbox ?? null,
2763
+ fast: resumeSettings.fast ?? null,
2764
+ generation: resumeGeneration
2765
+ });
2766
+ const resumed = await args.codex.request("thread/resume", resumeParams);
2767
+ if ("error" in resumed) {
2768
+ throw new Error(`failed to resume Codex thread with updated settings: ${resumed.error.message}`);
2769
+ }
2770
+ if (state.pendingRuntimeSettingsResume === true && state.runtimeSettingsGeneration === resumeGeneration) {
2771
+ state.pendingRuntimeSettingsResume = false;
2772
+ }
2773
+ args.log("codex.session_settings_resume_completed", {
2774
+ codex_thread_id: codexThreadId,
2775
+ generation: resumeGeneration,
2776
+ current_generation: state.runtimeSettingsGeneration,
2777
+ pending: state.pendingRuntimeSettingsResume
2778
+ });
2779
+ return state.pendingRuntimeSettingsResume !== true;
2780
+ }
2781
+ async function tryResumeCodexThreadForPendingRuntimeSettings(args, state) {
2782
+ try {
2783
+ return await resumeCodexThreadForPendingRuntimeSettings(args, state);
2784
+ } catch (error) {
2785
+ args.log("codex.session_settings_resume_failed", {
2786
+ message: error instanceof Error ? error.message : String(error)
2787
+ });
2788
+ return false;
2789
+ }
2790
+ }
2719
2791
  async function resolvePendingCodexApprovalRequest(args, state, control) {
2720
2792
  if (state.codexThreadId !== control.threadId) {
2721
2793
  return;
@@ -3277,6 +3349,10 @@ async function drainKandanMessageQueue(args, state, payloadContext) {
3277
3349
  if (state.closed || state.turn.status !== "idle" || localTuiTurnIsActive(state)) {
3278
3350
  return;
3279
3351
  }
3352
+ const resumed = await tryResumeCodexThreadForPendingRuntimeSettings(args, state);
3353
+ if (!resumed) {
3354
+ return;
3355
+ }
3280
3356
  const next = dequeuePendingKandanMessage(state.queue);
3281
3357
  if (next === undefined) {
3282
3358
  return;
@@ -3602,11 +3678,17 @@ async function forwardCompletedCodexTurn(args, state, turnId, payloadContext) {
3602
3678
  if (completingActiveTurn && state.turn.status === "completing" && state.turn.turnId === turnId) {
3603
3679
  state.turn = { status: "idle" };
3604
3680
  await stopCodexTyping(args, state);
3605
- await drainKandanMessageQueue(args, state, payloadContext);
3681
+ const resumed = await tryResumeCodexThreadForPendingRuntimeSettings(args, state);
3682
+ if (resumed) {
3683
+ await drainKandanMessageQueue(args, state, payloadContext);
3684
+ }
3606
3685
  }
3607
3686
  if (completingLocalTuiTurn && !completingActiveTurn) {
3608
3687
  await stopCodexTyping(args, state);
3609
- await drainKandanMessageQueue(args, state, payloadContext);
3688
+ const resumed = await tryResumeCodexThreadForPendingRuntimeSettings(args, state);
3689
+ if (resumed) {
3690
+ await drainKandanMessageQueue(args, state, payloadContext);
3691
+ }
3610
3692
  }
3611
3693
  }
3612
3694
  }
@@ -6271,7 +6353,6 @@ function codeServerSandboxProfile(options, codeServerBinDir) {
6271
6353
  "/etc",
6272
6354
  "/private/etc",
6273
6355
  "/opt/homebrew",
6274
- "/dev",
6275
6356
  ...options.codeServerRuntimeRoot === undefined ? [] : sandboxPathAliases(options.codeServerRuntimeRoot),
6276
6357
  codeServerBinDir
6277
6358
  ]);
@@ -6294,6 +6375,7 @@ function codeServerSandboxProfile(options, codeServerBinDir) {
6294
6375
  "(allow file-map-executable)",
6295
6376
  '(allow file-read* (literal "/") (literal "/private") (literal "/private/var"))',
6296
6377
  `(allow file-read* ${readOnlyRoots.map(sandboxSubpath).join(" ")})`,
6378
+ '(allow file-read* file-write* (subpath "/dev"))',
6297
6379
  `(allow file-read* file-write* ${readWriteRoots.map(sandboxSubpath).join(" ")})`
6298
6380
  ].join(`
6299
6381
  `);
@@ -7955,6 +8037,9 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
7955
8037
  };
7956
8038
  });
7957
8039
  const allowedCwds = { value: [...options.allowedCwds] };
8040
+ const missingAllowedCwds = {
8041
+ value: normalizeAllowedCwds(options.missingAllowedCwds ?? [])
8042
+ };
7958
8043
  const localEditorState = {
7959
8044
  value: { status: "disabled" }
7960
8045
  };
@@ -7970,7 +8055,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
7970
8055
  codexRemoteTui: true,
7971
8056
  startInstance: allowedCwds.value.length > 0,
7972
8057
  allowedCwds: allowedCwds.value,
7973
- missingConfiguredAllowedCwds: options.missingConfiguredAllowedCwds ?? [],
8058
+ missingAllowedCwds: missingAllowedCwds.value,
7974
8059
  allowedCwdSuggestions: allowedCwdSuggestions(options.cwd, allowedCwds.value),
7975
8060
  portForwarding: liveForwardPorts.size > 0,
7976
8061
  allowedPorts: Array.from(liveForwardPorts).sort((left, right) => left - right),
@@ -7985,7 +8070,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
7985
8070
  const joinPayload = () => ({
7986
8071
  clientName: "kandan-local-codex-runner",
7987
8072
  version: "0.0.1",
7988
- workspace: options.channelSession?.workspaceSlug ?? options.workspaceSlug ?? null,
8073
+ workspace: runnerWorkspaceSlug(options) ?? null,
7989
8074
  channel: options.channelSession?.channelSlug ?? null,
7990
8075
  capabilities: capabilitiesPayload()
7991
8076
  });
@@ -8055,6 +8140,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8055
8140
  const codexThreads = options.channelSession === undefined ? await discoverCodexThreads(codex, options.cwd) : [];
8056
8141
  const discoveredCodexThreads = { value: codexThreads };
8057
8142
  const runnerHost = hostname2();
8143
+ const runtimeDefaults = runnerRuntimeDefaults(options);
8058
8144
  const instancePayload = {
8059
8145
  instanceId,
8060
8146
  codexUrl,
@@ -8062,8 +8148,10 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8062
8148
  cwd: options.cwd,
8063
8149
  hostname: runnerHost,
8064
8150
  codexThreads,
8065
- model: options.channelSession?.model ?? null,
8066
- reasoningEffort: options.channelSession?.reasoningEffort ?? null,
8151
+ workspace: runnerWorkspaceSlug(options) ?? null,
8152
+ channel: options.channelSession?.channelSlug ?? null,
8153
+ model: runtimeDefaults.model ?? null,
8154
+ reasoningEffort: runtimeDefaults.reasoningEffort ?? null,
8067
8155
  fast: options.fast ?? false
8068
8156
  };
8069
8157
  await kandan.push(topic, "instance_started", instancePayload);
@@ -8108,9 +8196,9 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8108
8196
  dynamicChannelSessions.clear();
8109
8197
  });
8110
8198
  const attachThreadSession = async (control, cwd, codexThreadId) => {
8111
- const workspaceSlug = normalizedWorkDescription(control.workspace);
8112
- const channelSlug = normalizedWorkDescription(control.channel);
8113
- const kandanThreadId = normalizedWorkDescription(control.threadId);
8199
+ const workspaceSlug = optionalThreadControlField(control, "workspace");
8200
+ const channelSlug = optionalThreadControlField(control, "channel");
8201
+ const kandanThreadId = optionalThreadControlField(control, "threadId");
8114
8202
  if (workspaceSlug === undefined || channelSlug === undefined || kandanThreadId === undefined) {
8115
8203
  return;
8116
8204
  }
@@ -8171,13 +8259,13 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8171
8259
  codexUrl,
8172
8260
  cwd: options.cwd,
8173
8261
  hostname: runnerHost,
8174
- workspace: options.channelSession?.workspaceSlug ?? options.workspaceSlug ?? null,
8262
+ workspace: runnerWorkspaceSlug(options) ?? null,
8175
8263
  channel: options.channelSession?.channelSlug ?? null,
8176
8264
  threadId: channelSession?.currentKandanThreadId() ?? null,
8177
8265
  codexThreadId: channelSession?.currentCodexThreadId() ?? null,
8178
8266
  codexThreads: discoveredCodexThreads.value,
8179
- model: options.channelSession?.model ?? null,
8180
- reasoningEffort: options.channelSession?.reasoningEffort ?? null,
8267
+ model: runtimeDefaults.model ?? null,
8268
+ reasoningEffort: runtimeDefaults.reasoningEffort ?? null,
8181
8269
  fast: options.fast ?? false,
8182
8270
  capabilities: capabilitiesPayload()
8183
8271
  });
@@ -8342,7 +8430,9 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8342
8430
  return;
8343
8431
  }
8344
8432
  if (isUpdateRunnerConfigControl(control)) {
8345
- allowedCwds.value = normalizeAllowedCwds(control.allowedCwds);
8433
+ const updatedAllowedCwds = configuredAllowedCwds(control.allowedCwds);
8434
+ allowedCwds.value = updatedAllowedCwds.allowedCwds;
8435
+ missingAllowedCwds.value = updatedAllowedCwds.missingAllowedCwds;
8346
8436
  pushHeartbeat();
8347
8437
  return;
8348
8438
  }
@@ -8374,7 +8464,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
8374
8464
  if (handled !== undefined) {
8375
8465
  return handled;
8376
8466
  }
8377
- return applyControl(codex, kandan, topic, instanceId, options, allowedCwds.value, control, attachThreadSession);
8467
+ return applyControl(codex, kandan, topic, instanceId, options, allowedCwds.value, control, log, attachThreadSession);
8378
8468
  }).then((response) => {
8379
8469
  return kandan.push(topic, "codex_response", response);
8380
8470
  }).catch((error) => {
@@ -8450,7 +8540,7 @@ async function discoverCodexThreads(codex, cwd) {
8450
8540
  }
8451
8541
  function extractStartedThreadId(response) {
8452
8542
  if ("error" in response) {
8453
- return;
8543
+ throw new Error(`thread/start failed: ${response.error.message}`);
8454
8544
  }
8455
8545
  return stringValue(objectValue(objectValue(response.result)?.thread)?.id);
8456
8546
  }
@@ -8560,7 +8650,7 @@ async function prepareCodexThreadForTuiResume(codex, codexThreadId) {
8560
8650
  throw new Error(`failed to verify Codex TUI resume: ${verified.error.message}`);
8561
8651
  }
8562
8652
  }
8563
- async function applyControl(codex, kandan, topic, instanceId, options, allowedCwds, control, onStartedThread) {
8653
+ async function applyControl(codex, kandan, topic, instanceId, options, allowedCwds, control, log, onStartedThread) {
8564
8654
  switch (control.type) {
8565
8655
  case "start_instance": {
8566
8656
  const cwd = resolveAllowedCwd(control.cwd, allowedCwds);
@@ -8572,56 +8662,71 @@ async function applyControl(codex, kandan, topic, instanceId, options, allowedCw
8572
8662
  error: cwd.reason
8573
8663
  };
8574
8664
  }
8575
- if (options.codexUrl === undefined) {
8576
- ensureCodexProjectTrusted(cwd.cwd);
8665
+ const processingStatePayload = startInstanceMessageStatePayload(control, "processing", "starting Codex session");
8666
+ if (processingStatePayload !== undefined) {
8667
+ kandan.push(topic, "message_state", processingStatePayload).catch((error) => {
8668
+ log("kandan.start_instance_processing_state_push_failed", {
8669
+ message: error instanceof Error ? error.message : String(error)
8670
+ });
8671
+ });
8577
8672
  }
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({
8673
+ try {
8674
+ if (options.codexUrl === undefined) {
8675
+ ensureCodexProjectTrusted(cwd.cwd);
8676
+ }
8677
+ const developerPrompt = normalizedWorkDescription(control.developerPrompt);
8678
+ const runtimeSettings = startInstanceRuntimeSettings(options, control);
8679
+ const response = await codex.request("thread/start", {
8585
8680
  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
- });
8681
+ serviceName: "kandan-local-runner",
8682
+ personality: "pragmatic",
8683
+ developerInstructions: commanderDeveloperInstructions({
8684
+ cwd: cwd.cwd,
8685
+ developerPrompt
8686
+ }),
8687
+ ...runtimeSettings.model === undefined ? {} : { model: runtimeSettings.model },
8688
+ ...runtimeSettings.reasoningEffort === undefined ? {} : { reasoningEffort: runtimeSettings.reasoningEffort },
8689
+ ...runtimeSettings.approvalPolicy === undefined ? {} : { approvalPolicy: runtimeSettings.approvalPolicy },
8690
+ ...runtimeSettings.sandbox === undefined ? {} : { sandbox: runtimeSettings.sandbox },
8691
+ ...runtimeSettings.fast === true ? { serviceTier: "fast" } : {}
8692
+ });
8693
+ const codexThreadId = extractStartedThreadId(response);
8694
+ const workDescription = normalizedWorkDescription(control.workDescription);
8695
+ if (codexThreadId !== undefined && developerPrompt !== undefined) {
8696
+ await postVisibleDeveloperPrompt(kandan, topic, control, developerPrompt, codexThreadId);
8616
8697
  }
8698
+ const startedThreadSession = codexThreadId !== undefined && onStartedThread !== undefined ? await onStartedThread(control, cwd.cwd, codexThreadId) : undefined;
8699
+ if (codexThreadId !== undefined && workDescription !== undefined) {
8700
+ const rootSeq = integerValue(control.rootSeq);
8701
+ const sourceSeq = integerValue(control.sourceSeq) ?? rootSeq;
8702
+ if (startedThreadSession !== undefined && sourceSeq !== undefined) {
8703
+ const identity = identityFromAccessToken(options.token);
8704
+ await startedThreadSession.startThreadMessageTurn({
8705
+ seq: sourceSeq,
8706
+ body: workDescription,
8707
+ actorSlug: identity.actorUsername,
8708
+ actorUserId: identity.actorUserId
8709
+ });
8710
+ } else {
8711
+ await codex.request("turn/start", {
8712
+ threadId: codexThreadId,
8713
+ input: [{ type: "text", text: workDescription }]
8714
+ });
8715
+ }
8716
+ }
8717
+ return {
8718
+ instanceId,
8719
+ controlType: control.type,
8720
+ cwd: cwd.cwd,
8721
+ matchedRoot: cwd.matchedRoot,
8722
+ response
8723
+ };
8724
+ } catch (error) {
8725
+ try {
8726
+ await publishStartInstanceMessageState(kandan, topic, control, "failed", "failed to start Codex session");
8727
+ } catch (_publishError) {}
8728
+ throw error;
8617
8729
  }
8618
- return {
8619
- instanceId,
8620
- controlType: control.type,
8621
- cwd: cwd.cwd,
8622
- matchedRoot: cwd.matchedRoot,
8623
- response
8624
- };
8625
8730
  }
8626
8731
  case "reconnect_thread": {
8627
8732
  const cwd = resolveAllowedCwd(control.cwd, allowedCwds);
@@ -8786,9 +8891,9 @@ secure tunnel, and keep the Linzumi thread truthful.
8786
8891
  </task_reminder>`;
8787
8892
  }
8788
8893
  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);
8894
+ const workspace = optionalThreadControlField(control, "workspace");
8895
+ const channel = optionalThreadControlField(control, "channel");
8896
+ const threadId = optionalThreadControlField(control, "threadId");
8792
8897
  if (workspace === undefined || channel === undefined || threadId === undefined) {
8793
8898
  return;
8794
8899
  }
@@ -8810,24 +8915,80 @@ ${developerPrompt}`,
8810
8915
  client_message_id: `codex-start-instructions-${threadId}`
8811
8916
  });
8812
8917
  }
8918
+ async function publishStartInstanceMessageState(kandan, topic, control, status, reason) {
8919
+ const payload = startInstanceMessageStatePayload(control, status, reason);
8920
+ if (payload === undefined) {
8921
+ return;
8922
+ }
8923
+ await kandan.push(topic, "message_state", payload);
8924
+ }
8925
+ function startInstanceMessageStatePayload(control, status, reason) {
8926
+ const rootSeq = integerValue(control.rootSeq);
8927
+ const sourceSeq = integerValue(control.sourceSeq) ?? rootSeq;
8928
+ if (sourceSeq === undefined) {
8929
+ return;
8930
+ }
8931
+ const workspace = requiredStartInstanceControlField(control, "workspace");
8932
+ const channel = requiredStartInstanceControlField(control, "channel");
8933
+ const threadId = requiredStartInstanceControlField(control, "threadId");
8934
+ return {
8935
+ workspace,
8936
+ channel,
8937
+ thread_id: threadId,
8938
+ seq: sourceSeq,
8939
+ status,
8940
+ reason
8941
+ };
8942
+ }
8943
+ function requiredStartInstanceControlField(control, field) {
8944
+ return requiredThreadControlField(control, field);
8945
+ }
8946
+ function requiredThreadControlField(control, field) {
8947
+ const value = optionalThreadControlField(control, field);
8948
+ if (value === undefined) {
8949
+ throw new Error(`${control.type} control missing ${field}`);
8950
+ }
8951
+ return value;
8952
+ }
8953
+ function optionalThreadControlField(control, field) {
8954
+ const value = stringValue(control[field])?.trim();
8955
+ if (value === undefined || value === "") {
8956
+ return;
8957
+ }
8958
+ return value;
8959
+ }
8813
8960
  function startInstanceRuntimeSettings(options, control) {
8814
- const session = options.channelSession;
8961
+ const defaults = runnerRuntimeDefaults(options);
8815
8962
  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,
8963
+ model: control.model ?? defaults.model,
8964
+ reasoningEffort: control.reasoningEffort ?? defaults.reasoningEffort,
8965
+ approvalPolicy: control.approvalPolicy ?? defaults.approvalPolicy,
8966
+ sandbox: control.sandbox ?? defaults.sandbox,
8820
8967
  fast: control.fast ?? options.fast
8821
8968
  };
8822
8969
  }
8823
8970
  async function startOwnedCodexAppServer(options) {
8824
8971
  ensureCodexProjectTrusted(options.cwd);
8972
+ const defaults = runnerRuntimeDefaults(options);
8825
8973
  return await startCodexAppServer(options.codexBin, options.cwd, {
8826
- model: options.channelSession?.model,
8827
- reasoningEffort: options.channelSession?.reasoningEffort,
8974
+ model: defaults.model,
8975
+ reasoningEffort: defaults.reasoningEffort,
8828
8976
  fast: options.fast
8829
8977
  });
8830
8978
  }
8979
+ function runnerWorkspaceSlug(options) {
8980
+ return options.channelSession?.workspaceSlug ?? options.workspaceSlug;
8981
+ }
8982
+ function runnerRuntimeDefaults(options) {
8983
+ const session = options.channelSession;
8984
+ const defaults = options.runtimeDefaults;
8985
+ return {
8986
+ model: defaults?.model ?? session?.model,
8987
+ reasoningEffort: defaults?.reasoningEffort ?? session?.reasoningEffort,
8988
+ approvalPolicy: defaults?.approvalPolicy ?? session?.approvalPolicy,
8989
+ sandbox: defaults?.sandbox ?? session?.sandbox
8990
+ };
8991
+ }
8831
8992
  function isUpdateRunnerConfigControl(control) {
8832
8993
  return control.type === "update_runner_config";
8833
8994
  }
@@ -8840,6 +9001,30 @@ function normalizeAllowedCwds(values) {
8840
9001
  return normalized === "" ? [] : [normalized];
8841
9002
  })));
8842
9003
  }
9004
+ function configuredAllowedCwds(values) {
9005
+ const allowedCwds = [];
9006
+ const missingAllowedCwds = [];
9007
+ for (const value of normalizeAllowedCwds(values)) {
9008
+ const absolutePath = resolve5(expandUserPath(value));
9009
+ try {
9010
+ const realPath = realpathSync4(absolutePath);
9011
+ allowedCwds.push(...realPath === absolutePath ? [realPath] : [realPath, absolutePath]);
9012
+ } catch (error) {
9013
+ if (isMissingAllowedCwdError(error)) {
9014
+ missingAllowedCwds.push(absolutePath);
9015
+ continue;
9016
+ }
9017
+ throw error;
9018
+ }
9019
+ }
9020
+ return {
9021
+ allowedCwds: normalizeAllowedCwds(allowedCwds),
9022
+ missingAllowedCwds: normalizeAllowedCwds(missingAllowedCwds)
9023
+ };
9024
+ }
9025
+ function isMissingAllowedCwdError(error) {
9026
+ return typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR" || error.code === "EACCES" || error.code === "ELOOP" || error.code === "EIO");
9027
+ }
8843
9028
  function allowedCwdSuggestions(cwd, allowedCwds) {
8844
9029
  return normalizeAllowedCwds([cwd, ...allowedCwds]);
8845
9030
  }
@@ -8988,14 +9173,14 @@ import {
8988
9173
  existsSync as existsSync5,
8989
9174
  mkdirSync as mkdirSync6,
8990
9175
  readFileSync as readFileSync4,
8991
- realpathSync as realpathSync4,
9176
+ realpathSync as realpathSync5,
8992
9177
  writeFileSync as writeFileSync5
8993
9178
  } from "node:fs";
8994
9179
  import { homedir as homedir6 } from "node:os";
8995
- import { dirname as dirname5, resolve as resolve5 } from "node:path";
9180
+ import { dirname as dirname5, resolve as resolve6 } from "node:path";
8996
9181
  function localConfigPath(env = process.env) {
8997
9182
  const override = env.LINZUMI_CONFIG_FILE;
8998
- return override !== undefined && override.trim() !== "" ? resolve5(expandUserPath(override)) : resolve5(homedir6(), ".linzumi", "config.json");
9183
+ return override !== undefined && override.trim() !== "" ? resolve6(expandUserPath(override)) : resolve6(homedir6(), ".linzumi", "config.json");
8999
9184
  }
9000
9185
  function readLocalConfig(path = localConfigPath()) {
9001
9186
  if (!existsSync5(path)) {
@@ -9010,32 +9195,36 @@ function readLocalConfig(path = localConfigPath()) {
9010
9195
  allowedCwds: uniqueStrings(parsed.allowedCwds)
9011
9196
  };
9012
9197
  }
9013
- function readConfiguredAllowedCwdState(path = localConfigPath()) {
9198
+ function readConfiguredAllowedCwdDetails(path = localConfigPath()) {
9014
9199
  const allowedCwds = [];
9015
- const missingCwds = [];
9200
+ const missingAllowedCwds = [];
9016
9201
  for (const cwd of readLocalConfig(path).allowedCwds) {
9202
+ const absolutePath = resolve6(expandUserPath(cwd));
9017
9203
  try {
9018
- const absolutePath = resolve5(expandUserPath(cwd));
9019
- const realPath = realpathSync4(absolutePath);
9204
+ const realPath = realpathSync5(absolutePath);
9020
9205
  allowedCwds.push(...realPath === absolutePath ? [realPath] : [realPath, absolutePath]);
9021
- } catch (_error) {
9022
- missingCwds.push(cwd);
9206
+ } catch (error) {
9207
+ if (isMissingPathError(error)) {
9208
+ missingAllowedCwds.push(absolutePath);
9209
+ continue;
9210
+ }
9211
+ throw error;
9023
9212
  }
9024
9213
  }
9025
9214
  return {
9026
9215
  allowedCwds: uniqueStrings(allowedCwds),
9027
- missingCwds: uniqueStrings(missingCwds)
9216
+ missingAllowedCwds: uniqueStrings(missingAllowedCwds)
9028
9217
  };
9029
9218
  }
9030
9219
  function addAllowedCwd(pathValue, path = localConfigPath()) {
9031
- const normalizedPath = realpathSync4(resolve5(expandUserPath(pathValue)));
9220
+ const normalizedPath = realpathSync5(resolve6(expandUserPath(pathValue)));
9032
9221
  const config = readLocalConfig(path);
9033
9222
  const allowedCwds = uniqueStrings([...config.allowedCwds, normalizedPath]);
9034
9223
  writeLocalConfig({ version: 1, allowedCwds }, path);
9035
9224
  return allowedCwds;
9036
9225
  }
9037
9226
  function removeAllowedCwd(pathValue, path = localConfigPath()) {
9038
- const requestedPath = resolve5(expandUserPath(pathValue));
9227
+ const requestedPath = resolve6(expandUserPath(pathValue));
9039
9228
  const normalizedRequest = realpathOrResolved(requestedPath);
9040
9229
  const config = readLocalConfig(path);
9041
9230
  const allowedCwds = config.allowedCwds.filter((cwd) => {
@@ -9058,11 +9247,14 @@ function uniqueStrings(values) {
9058
9247
  ...new Set(values.map((value) => value.trim()).filter((value) => value !== ""))
9059
9248
  ];
9060
9249
  }
9250
+ function isMissingPathError(error) {
9251
+ return typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR" || error.code === "EACCES" || error.code === "ELOOP" || error.code === "EIO");
9252
+ }
9061
9253
  function realpathOrResolved(pathValue) {
9062
9254
  try {
9063
- return realpathSync4(resolve5(expandUserPath(pathValue)));
9255
+ return realpathSync5(resolve6(expandUserPath(pathValue)));
9064
9256
  } catch (_error) {
9065
- return resolve5(expandUserPath(pathValue));
9257
+ return resolve6(expandUserPath(pathValue));
9066
9258
  }
9067
9259
  }
9068
9260
 
@@ -9460,7 +9652,7 @@ async function runClaim(command, deps) {
9460
9652
  workspaceName: stringValue(response.workspace_name),
9461
9653
  channelId: stringValue(response.channel_id),
9462
9654
  ownerUsername: stringValue(response.owner_username),
9463
- channelUrl: requiredString(response, "channel_url"),
9655
+ channelUrl: stringValue(response.channel_url),
9464
9656
  loginUrl: requiredString(response, "login_url"),
9465
9657
  supportChannelId: requiredString(response, "support_channel_id"),
9466
9658
  supportChannelUrl: requiredString(response, "support_channel_url"),
@@ -9479,8 +9671,10 @@ async function runClaim(command, deps) {
9479
9671
  deps.stdout.write(`login_url: ${tokenFile.loginUrl}
9480
9672
  `);
9481
9673
  writeHumanLoginUrlWarning(deps);
9482
- deps.stdout.write(`channel_url: ${tokenFile.channelUrl}
9674
+ if (tokenFile.channelUrl !== undefined) {
9675
+ deps.stdout.write(`channel_url: ${tokenFile.channelUrl}
9483
9676
  `);
9677
+ }
9484
9678
  deps.stdout.write(`support_channel_id: ${tokenFile.supportChannelId}
9485
9679
  `);
9486
9680
  deps.stdout.write(`support_channel_url: ${tokenFile.supportChannelUrl}
@@ -9749,7 +9943,7 @@ function readStoredAgentTokenFile(path, readTextFile = readOptionalTextFile) {
9749
9943
  workspaceName: stringValue(parsed.workspaceName),
9750
9944
  channelId: stringValue(parsed.channelId),
9751
9945
  ownerUsername: stringValue(parsed.ownerUsername),
9752
- channelUrl: requiredString(parsed, "channelUrl"),
9946
+ channelUrl: stringValue(parsed.channelUrl),
9753
9947
  loginUrl: requiredString(parsed, "loginUrl"),
9754
9948
  supportChannelId: requiredString(parsed, "supportChannelId"),
9755
9949
  supportChannelUrl: stringValue(parsed.supportChannelUrl),
@@ -9803,7 +9997,7 @@ Launch target:
9803
9997
 
9804
9998
  // src/helloLinzumiProject.ts
9805
9999
  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";
10000
+ import { dirname as dirname7, join as join9, resolve as resolve7 } from "node:path";
9807
10001
  import { fileURLToPath } from "node:url";
9808
10002
  var defaultHelloLinzumiProjectDir = "/tmp/hello_linzumi";
9809
10003
  var defaultHelloLinzumiProjectName = "hello_linzumi";
@@ -9837,10 +10031,10 @@ function resolveHelloProjectRoot(options) {
9837
10031
  throw new Error("linzumi init-hello-linzumi-demo-app accepts either --dir or --parent-dir/--name, not both");
9838
10032
  }
9839
10033
  if (options.rootPath !== undefined) {
9840
- return resolve6(options.rootPath);
10034
+ return resolve7(options.rootPath);
9841
10035
  }
9842
10036
  const name = normalizeProjectName(options.name ?? defaultHelloLinzumiProjectName);
9843
- return resolve6(options.parentDir ?? defaultHelloLinzumiParentDir, name);
10037
+ return resolve7(options.parentDir ?? defaultHelloLinzumiParentDir, name);
9844
10038
  }
9845
10039
  function normalizeProjectName(value) {
9846
10040
  const name = value.trim();
@@ -10375,7 +10569,7 @@ import {
10375
10569
  writeFileSync as writeFileSync8
10376
10570
  } from "node:fs";
10377
10571
  import { homedir as homedir8 } from "node:os";
10378
- import { dirname as dirname8, join as join10, resolve as resolve7 } from "node:path";
10572
+ import { dirname as dirname8, join as join10, resolve as resolve8 } from "node:path";
10379
10573
  import { execFileSync, spawn as spawn7 } from "node:child_process";
10380
10574
  import { fileURLToPath as fileURLToPath2 } from "node:url";
10381
10575
  var connectedMarker = "Runner connected:";
@@ -10391,7 +10585,7 @@ function defaultCommanderLogFile(runnerId, statusDir = commanderStatusDir()) {
10391
10585
  function startCommanderDaemon(options) {
10392
10586
  const statusDir = options.statusDir ?? commanderStatusDir();
10393
10587
  const statusFile = commanderStatusFile(options.runnerId, statusDir);
10394
- const logFile = resolve7(options.logFile ?? defaultCommanderLogFile(options.runnerId, statusDir));
10588
+ const logFile = resolve8(options.logFile ?? defaultCommanderLogFile(options.runnerId, statusDir));
10395
10589
  const entrypoint = options.entrypoint ?? currentEntrypoint();
10396
10590
  const nodeBin = options.nodeBin ?? process.execPath;
10397
10591
  const command = [
@@ -10588,7 +10782,7 @@ function safeRunnerId(runnerId) {
10588
10782
  }
10589
10783
  async function waitForFileChangeOrTimeout(path, deadline, now, ready2 = () => false) {
10590
10784
  const remaining = Math.max(0, deadline - now());
10591
- await new Promise((resolve8) => {
10785
+ await new Promise((resolve9) => {
10592
10786
  let resolved = false;
10593
10787
  let watcher;
10594
10788
  const finish = () => {
@@ -10598,7 +10792,7 @@ async function waitForFileChangeOrTimeout(path, deadline, now, ready2 = () => fa
10598
10792
  resolved = true;
10599
10793
  watcher?.close();
10600
10794
  clearTimeout(timer);
10601
- resolve8();
10795
+ resolve9();
10602
10796
  };
10603
10797
  const timer = setTimeout(finish, remaining);
10604
10798
  try {
@@ -10678,7 +10872,7 @@ function isMainModule() {
10678
10872
  if (scriptPath === undefined) {
10679
10873
  return false;
10680
10874
  }
10681
- return fileURLToPath3(import.meta.url) === resolve8(scriptPath);
10875
+ return fileURLToPath3(import.meta.url) === resolve9(scriptPath);
10682
10876
  }
10683
10877
  async function main(args) {
10684
10878
  const parsed = parseCommand(args);
@@ -10687,7 +10881,7 @@ async function main(args) {
10687
10881
  process.stdout.write(connectGuideText());
10688
10882
  return;
10689
10883
  case "version":
10690
- process.stdout.write(`linzumi 0.0.39-beta
10884
+ process.stdout.write(`linzumi 0.0.40-beta
10691
10885
  `);
10692
10886
  return;
10693
10887
  case "auth":
@@ -10840,7 +11034,7 @@ function runPathsCommand(args) {
10840
11034
  if (pathValue === undefined || pathValue.trim() === "") {
10841
11035
  throw new Error("missing path for linzumi paths add");
10842
11036
  }
10843
- const trustedPath = realpathSync5(resolve8(expandUserPath(pathValue)));
11037
+ const trustedPath = realpathSync6(resolve9(expandUserPath(pathValue)));
10844
11038
  addAllowedCwd(pathValue);
10845
11039
  process.stdout.write(`Trusted ${trustedPath}
10846
11040
  `);
@@ -11030,6 +11224,7 @@ async function parseStartRunnerArgs(args, deps = {
11030
11224
  kandanUrl,
11031
11225
  token: targetToken,
11032
11226
  runnerId: stringValue3(values, "runner-id") ?? `runner-${randomUUID3()}`,
11227
+ workspaceSlug: target.workspaceSlug,
11033
11228
  cwd,
11034
11229
  codexBin,
11035
11230
  codexUrl: stringValue3(values, "codex-url"),
@@ -11042,6 +11237,7 @@ async function parseStartRunnerArgs(args, deps = {
11042
11237
  editorRuntime: editorRuntime.runtime,
11043
11238
  socketFactory: trustedWebSocketFactory(kandanTlsTrustFromEnv()),
11044
11239
  dependencyStatus,
11240
+ runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
11045
11241
  channelSession: {
11046
11242
  workspaceSlug: target.workspaceSlug,
11047
11243
  channelSlug: target.channelSlug,
@@ -11073,12 +11269,23 @@ async function parseAgentRunnerArgs(args, deps = {
11073
11269
  const tokenFilePath = stringValue3(values, "agent-token-file") ?? defaultAgentTokenFilePath();
11074
11270
  const tokenFile = readStoredAgentTokenFile(tokenFilePath, deps.readTextFile);
11075
11271
  const channelSlug = tokenFile.channelId;
11076
- const listenUser = channelSlug === undefined ? undefined : stringValue3(values, "listen-user") ?? requiredStoredOwnerUsername(tokenFile.ownerUsername);
11272
+ rejectWorkspaceCommanderThreadFlags(values, channelSlug);
11273
+ const channelSession = channelSlug === undefined ? undefined : {
11274
+ workspaceSlug: tokenFile.workspaceId,
11275
+ channelSlug,
11276
+ kandanThreadId: stringValue3(values, "linzumi-thread-id"),
11277
+ listenUser: stringValue3(values, "listen-user") ?? requiredStoredOwnerUsername(tokenFile.ownerUsername),
11278
+ model: stringValue3(values, "model"),
11279
+ reasoningEffort: stringValue3(values, "reasoning-effort"),
11280
+ sandbox: stringValue3(values, "sandbox"),
11281
+ approvalPolicy: stringValue3(values, "approval-policy"),
11282
+ streamFlushMs: positiveIntegerValue(values, "stream-flush-ms")
11283
+ };
11077
11284
  const kandanUrl = stringValue3(values, "linzumi-url") ?? agentApiUrlToKandanUrl(tokenFile.apiUrl);
11078
11285
  const requestedCwdValue = cwdArg ?? stringValue3(values, "cwd");
11079
11286
  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]);
11287
+ const configuredAllowedCwds2 = requestedCwdValue === undefined && !values.has("allowed-cwd") ? readConfiguredAllowedCwdDetails() : { allowedCwds: [], missingAllowedCwds: [] };
11288
+ 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
11289
  const cwd = allowedCwds[0] ?? requestedCwd;
11083
11290
  const codexBin = stringValue3(values, "codex-bin") ?? "codex";
11084
11291
  const customCodeServerBin = stringValue3(values, "code-server-bin");
@@ -11105,6 +11312,7 @@ async function parseAgentRunnerArgs(args, deps = {
11105
11312
  kandanUrl,
11106
11313
  token: tokenFile.commanderToken,
11107
11314
  runnerId: stringValue3(values, "runner-id") ?? `agent-runner-${randomUUID3()}`,
11315
+ workspaceSlug: tokenFile.workspaceId,
11108
11316
  cwd,
11109
11317
  codexBin,
11110
11318
  codexUrl: stringValue3(values, "codex-url"),
@@ -11112,24 +11320,14 @@ async function parseAgentRunnerArgs(args, deps = {
11112
11320
  fast: values.get("fast") === true,
11113
11321
  logFile: stringValue3(values, "log-file"),
11114
11322
  allowedCwds,
11115
- missingConfiguredAllowedCwds: configuredAllowedCwdState.missingCwds,
11323
+ missingAllowedCwds: configuredAllowedCwds2.missingAllowedCwds,
11116
11324
  allowedForwardPorts: parseAllowedPortList(stringValue3(values, "forward-port")),
11117
11325
  codeServerBin: editorRuntime.codeServerBin,
11118
11326
  editorRuntime: editorRuntime.runtime,
11119
11327
  socketFactory: trustedWebSocketFactory(kandanTlsTrustFromEnv()),
11120
11328
  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
- }
11329
+ runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
11330
+ channelSession
11133
11331
  };
11134
11332
  }
11135
11333
  function readAgentTokenTextFile(path) {
@@ -11147,6 +11345,12 @@ function rejectAgentRunnerTargetingFlags(values) {
11147
11345
  throw new Error(`linzumi commander uses the claimed human Commander token scope; remove ${unsupportedFlags.map((flag) => `--${flag}`).join(", ")}.`);
11148
11346
  }
11149
11347
  }
11348
+ function rejectWorkspaceCommanderThreadFlags(values, channelSlug) {
11349
+ if (channelSlug !== undefined || !values.has("linzumi-thread-id")) {
11350
+ return;
11351
+ }
11352
+ throw new Error("linzumi commander cannot bind --linzumi-thread-id because the agent token file has no channelId");
11353
+ }
11150
11354
  function requiredStoredOwnerUsername(ownerUsername) {
11151
11355
  if (ownerUsername !== undefined) {
11152
11356
  return ownerUsername;
@@ -11203,7 +11407,7 @@ async function parseRunnerArgs(args, deps = {
11203
11407
  process.exit(0);
11204
11408
  }
11205
11409
  if (values.get("version") === true) {
11206
- process.stdout.write(`linzumi 0.0.39-beta
11410
+ process.stdout.write(`linzumi 0.0.40-beta
11207
11411
  `);
11208
11412
  process.exit(0);
11209
11413
  }
@@ -11211,10 +11415,8 @@ async function parseRunnerArgs(args, deps = {
11211
11415
  const kandanUrl = required(values, "linzumi-url");
11212
11416
  const cwd = stringValue3(values, "cwd") ?? process.cwd();
11213
11417
  const cwdAllowedCwds = assertConfiguredAllowedCwds([cwd]);
11214
- const configuredAllowedCwdState = values.has("allowed-cwd") ? {
11215
- allowedCwds: assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))),
11216
- missingCwds: []
11217
- } : readConfiguredAllowedCwdState();
11418
+ const localConfiguredAllowedCwds = values.has("allowed-cwd") ? { allowedCwds: [], missingAllowedCwds: [] } : readConfiguredAllowedCwdDetails();
11419
+ const configuredAllowedCwds2 = values.has("allowed-cwd") ? assertConfiguredAllowedCwds(parseAllowedCwdList(stringValue3(values, "allowed-cwd"))) : [...localConfiguredAllowedCwds.allowedCwds];
11218
11420
  const codexBin = stringValue3(values, "codex-bin") ?? "codex";
11219
11421
  const customCodeServerBin = stringValue3(values, "code-server-bin");
11220
11422
  const explicitToken = stringValue3(values, "token");
@@ -11254,17 +11456,26 @@ async function parseRunnerArgs(args, deps = {
11254
11456
  launchTui: values.get("launch-tui") === true,
11255
11457
  fast: values.get("fast") === true,
11256
11458
  logFile: stringValue3(values, "log-file"),
11257
- allowedCwds: Array.from(new Set([...cwdAllowedCwds, ...configuredAllowedCwdState.allowedCwds])),
11258
- missingConfiguredAllowedCwds: configuredAllowedCwdState.missingCwds,
11459
+ allowedCwds: Array.from(new Set([...cwdAllowedCwds, ...configuredAllowedCwds2])),
11460
+ missingAllowedCwds: localConfiguredAllowedCwds.missingAllowedCwds,
11259
11461
  allowedForwardPorts: parseAllowedPortList(stringValue3(values, "forward-port")),
11260
11462
  codeServerBin: editorRuntime.codeServerBin,
11261
11463
  editorRuntime: editorRuntime.runtime,
11262
11464
  socketFactory: trustedWebSocketFactory(kandanTlsTrustFromEnv()),
11263
11465
  dependencyStatus,
11264
11466
  workspaceSlug: channelSession?.workspaceSlug ?? singleWorkspaceScopeFromAccessToken(token),
11467
+ runtimeDefaults: runnerRuntimeDefaultsFromValues(values),
11265
11468
  channelSession
11266
11469
  };
11267
11470
  }
11471
+ function runnerRuntimeDefaultsFromValues(values) {
11472
+ return {
11473
+ model: stringValue3(values, "model"),
11474
+ reasoningEffort: stringValue3(values, "reasoning-effort"),
11475
+ approvalPolicy: stringValue3(values, "approval-policy"),
11476
+ sandbox: stringValue3(values, "sandbox")
11477
+ };
11478
+ }
11268
11479
  function strictFlagValues(args, definitions = flagDefinitions) {
11269
11480
  const values = new Map;
11270
11481
  for (let index = 0;index < args.length; index += 1) {
@@ -11359,9 +11570,9 @@ function resolveUserPath(pathValue) {
11359
11570
  return homedir9();
11360
11571
  }
11361
11572
  if (pathValue.startsWith("~/")) {
11362
- return resolve8(homedir9(), pathValue.slice(2));
11573
+ return resolve9(homedir9(), pathValue.slice(2));
11363
11574
  }
11364
- return resolve8(pathValue);
11575
+ return resolve9(pathValue);
11365
11576
  }
11366
11577
  function parseChannelSession(values, token, target) {
11367
11578
  if (target === undefined) {
@@ -11645,10 +11856,10 @@ Usage:
11645
11856
 
11646
11857
  What it does:
11647
11858
  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.
11859
+ reads ~/.linzumi/agent-token.json, connects to its workspace even when no
11860
+ channel is assigned, reads trusted folders from ~/.linzumi/config.json when no
11861
+ folder is passed, and uses the token's channel scope for channel-bound startup
11862
+ when one is present.
11652
11863
 
11653
11864
  Options:
11654
11865
  --agent-token-file <path> Agent token cache, default ~/.linzumi/agent-token.json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.39-beta",
3
+ "version": "0.0.40-beta",
4
4
  "description": "Linzumi CLI — point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
5
5
  "type": "module",
6
6
  "bin": {