@linzumi/cli 0.0.110-beta → 0.0.111-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 +307 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -58,7 +58,7 @@ Install the CLI or run it with `npx`:
58
58
  ```bash
59
59
  npm install -g @linzumi/cli@latest
60
60
  npx -y @linzumi/cli@latest signup
61
- npx -y @linzumi/cli@0.0.110-beta --version
61
+ npx -y @linzumi/cli@0.0.111-beta --version
62
62
  linzumi --version
63
63
  ```
64
64
 
package/dist/index.js CHANGED
@@ -5863,6 +5863,9 @@ function handleEvent(state, event, deps, effects) {
5863
5863
  }
5864
5864
  handleCodexNotification(state, event, deps, effects);
5865
5865
  }
5866
+ function modelInteractionMethod(method) {
5867
+ return modelInteractionMethods.has(method);
5868
+ }
5866
5869
  function handleCodexNotification(state, event, deps, effects) {
5867
5870
  const method = event.notification.method;
5868
5871
  const params = event.notification.params ?? {};
@@ -5879,9 +5882,7 @@ function handleCodexNotification(state, event, deps, effects) {
5879
5882
  const explicitTurnId = notificationTurnId(params);
5880
5883
  const turnId = explicitTurnId ?? singleActiveTurnId(state);
5881
5884
  if (turnId === void 0) {
5882
- effects.push(
5883
- log("pipeline.loop.notification_without_turn", { method })
5884
- );
5885
+ effects.push(log("pipeline.loop.notification_without_turn", { method }));
5885
5886
  return;
5886
5887
  }
5887
5888
  if (state.terminalTurnIds.has(turnId)) {
@@ -5902,6 +5903,10 @@ function handleCodexNotification(state, event, deps, effects) {
5902
5903
  }
5903
5904
  turn = createTurn(state, turnId, sourceSeq);
5904
5905
  }
5906
+ turn.notificationCount += 1;
5907
+ if (modelInteractionMethod(method)) {
5908
+ turn.modelInteractionSeen = true;
5909
+ }
5905
5910
  if (method === "turn/started") {
5906
5911
  return;
5907
5912
  }
@@ -5915,6 +5920,7 @@ function handleCodexNotification(state, event, deps, effects) {
5915
5920
  return;
5916
5921
  }
5917
5922
  if (method === "error") {
5923
+ turn.errorNotificationCount += 1;
5918
5924
  const upstreamReason = upstreamErrorReason(params);
5919
5925
  if (upstreamReason !== void 0) {
5920
5926
  turn.lastUpstreamErrorReason = upstreamReason;
@@ -5924,6 +5930,17 @@ function handleCodexNotification(state, event, deps, effects) {
5924
5930
  reason: upstreamReason
5925
5931
  })
5926
5932
  );
5933
+ } else {
5934
+ const diagnosticText = errorDiagnosticText(params);
5935
+ if (diagnosticText !== void 0) {
5936
+ turn.lastErrorDiagnosticText = diagnosticText;
5937
+ effects.push(
5938
+ log("codex.error_without_http_status_captured", {
5939
+ turn_id: turn.turnId,
5940
+ diagnostic_text: diagnosticText
5941
+ })
5942
+ );
5943
+ }
5927
5944
  }
5928
5945
  return;
5929
5946
  }
@@ -5978,6 +5995,10 @@ function createTurn(state, turnId, sourceSeq) {
5978
5995
  phase: "active",
5979
5996
  protocolFailureReason: void 0,
5980
5997
  lastUpstreamErrorReason: void 0,
5998
+ lastErrorDiagnosticText: void 0,
5999
+ errorNotificationCount: 0,
6000
+ notificationCount: 0,
6001
+ modelInteractionSeen: false,
5981
6002
  items: []
5982
6003
  };
5983
6004
  state.turns.set(turnId, turn);
@@ -6144,7 +6165,11 @@ function handleItemCompleted(turn, params, deps, effects) {
6144
6165
  );
6145
6166
  }
6146
6167
  function emitRowUpdate(turn, item, deltaIndex, streamState, deps, effects) {
6147
- const rendered = renderPipelineItem(item.wireItemKey, item.content, streamState);
6168
+ const rendered = renderPipelineItem(
6169
+ item.wireItemKey,
6170
+ item.content,
6171
+ streamState
6172
+ );
6148
6173
  if (!item.posted) {
6149
6174
  if (rendered.body.trim() === "") {
6150
6175
  return;
@@ -6238,9 +6263,7 @@ function createItem(turn, streamKind, itemId, deps) {
6238
6263
  }
6239
6264
  function handleSnapshot(state, turnId, snapshot, deps, effects) {
6240
6265
  if (state.terminalTurnIds.has(turnId)) {
6241
- effects.push(
6242
- log("pipeline.loop.duplicate_snapshot", { turn_id: turnId })
6243
- );
6266
+ effects.push(log("pipeline.loop.duplicate_snapshot", { turn_id: turnId }));
6244
6267
  return;
6245
6268
  }
6246
6269
  const turn = state.turns.get(turnId);
@@ -6363,7 +6386,9 @@ function handleSnapshot(state, turnId, snapshot, deps, effects) {
6363
6386
  const assistantResponseSeen = turn.items.some(
6364
6387
  (item) => item.identity.streamKind === "assistant" && item.posted
6365
6388
  ) || projected.some((proj) => proj.streamKind === "assistant");
6366
- const terminalFailureReason = turn.protocolFailureReason ?? (assistantResponseSeen ? void 0 : turn.lastUpstreamErrorReason ?? completedWithoutAssistantOutputReason);
6389
+ const emptyTurnDiagnostic = !assistantResponseSeen && turn.protocolFailureReason === void 0 && turn.lastUpstreamErrorReason === void 0 ? classifyEmptyTurn(turn, projected.length) : void 0;
6390
+ const baseNoOutputReason = turn.lastUpstreamErrorReason ?? (emptyTurnDiagnostic === void 0 ? completedWithoutAssistantOutputReason : `${completedWithoutAssistantOutputReason} [${emptyTurnDiagnostic.cause}]`);
6391
+ const terminalFailureReason = turn.protocolFailureReason ?? (assistantResponseSeen ? void 0 : baseNoOutputReason);
6367
6392
  if (turn.protocolFailureReason !== void 0) {
6368
6393
  effects.push(
6369
6394
  log("codex.turn_completed_with_protocol_failure", {
@@ -6381,7 +6406,19 @@ function handleSnapshot(state, turnId, snapshot, deps, effects) {
6381
6406
  turn_id: turn.turnId,
6382
6407
  source_seq: turn.sourceSeq,
6383
6408
  streamed_item_count: turn.items.length,
6384
- snapshot_item_count: projected.length
6409
+ snapshot_item_count: projected.length,
6410
+ // LAUNCH-LIVE self-diagnosing fields: tell apart "codex never called the
6411
+ // model" from "model returned empty" from "error without HTTP status".
6412
+ notification_count: turn.notificationCount,
6413
+ error_notification_count: turn.errorNotificationCount,
6414
+ model_interaction_seen: turn.modelInteractionSeen,
6415
+ last_error_diagnostic_text: turn.lastErrorDiagnosticText ?? null,
6416
+ last_upstream_error_reason: turn.lastUpstreamErrorReason ?? null,
6417
+ empty_turn_cause: emptyTurnDiagnostic?.cause ?? null,
6418
+ // True iff the surfaced failure is the safe, retryable empty/no-response
6419
+ // terminal (no output produced, no real upstream error) - the signal
6420
+ // the client uses to offer a one-click retry (PART B, loud affordance).
6421
+ retryable_empty_turn: emptyTurnDiagnostic !== void 0
6385
6422
  })
6386
6423
  );
6387
6424
  }
@@ -6394,7 +6431,13 @@ function handleSnapshot(state, turnId, snapshot, deps, effects) {
6394
6431
  seq: turn.sourceSeq,
6395
6432
  ...terminalFailureReason === void 0 ? { status: "processed" } : {
6396
6433
  status: "failed",
6397
- reason: terminalFailureReason
6434
+ reason: terminalFailureReason,
6435
+ // PART B (loud retry affordance): mark the safe empty/no-response
6436
+ // terminal as retryable so the client can offer a one-click retry.
6437
+ // Additive optional field; older servers ignore it. Only the empty
6438
+ // terminal is retryable - real upstream errors (402/429/gateway,
6439
+ // protocol failures) carry their own handling and are NOT flagged.
6440
+ ...emptyTurnDiagnostic !== void 0 ? { retryable: true } : {}
6398
6441
  }
6399
6442
  },
6400
6443
  resolution: "must_ack"
@@ -6483,7 +6526,9 @@ function failureReason(params) {
6483
6526
  return stringValue(params.reason) ?? stringValue(params.message) ?? stringValue(objectValue(params.error)?.message) ?? stringValue(params.error) ?? "codex_turn_aborted";
6484
6527
  }
6485
6528
  function upstreamErrorReason(params) {
6486
- const summaryRaw = stringValue(params.diagnostic_summary) ?? stringValue(objectValue(objectValue(params.diagnostic_payload)?.error)?.message);
6529
+ const summaryRaw = stringValue(params.diagnostic_summary) ?? stringValue(
6530
+ objectValue(objectValue(params.diagnostic_payload)?.error)?.message
6531
+ );
6487
6532
  if (summaryRaw === void 0) {
6488
6533
  return void 0;
6489
6534
  }
@@ -6501,10 +6546,33 @@ function upstreamErrorHttpStatus(params) {
6501
6546
  const code = disconnected?.httpStatusCode;
6502
6547
  return typeof code === "number" ? code : void 0;
6503
6548
  }
6549
+ function errorDiagnosticText(params) {
6550
+ const raw = stringValue(params.message) ?? stringValue(params.reason) ?? stringValue(objectValue(params.error)?.message) ?? stringValue(params.error) ?? stringValue(params.diagnostic);
6551
+ if (raw === void 0) {
6552
+ return void 0;
6553
+ }
6554
+ const cleaned = raw.replace(/<[^>]*>/g, " ").replace(/data:[^"' )]+/g, "").replace(/\s+/g, " ").trim().slice(0, 180);
6555
+ return cleaned.length === 0 ? void 0 : cleaned;
6556
+ }
6557
+ function classifyEmptyTurn(turn, snapshotItemCount) {
6558
+ if (turn.lastErrorDiagnosticText !== void 0) {
6559
+ return {
6560
+ cause: `codex error without HTTP status: ${turn.lastErrorDiagnosticText}`
6561
+ };
6562
+ }
6563
+ if (!turn.modelInteractionSeen) {
6564
+ return {
6565
+ cause: `codex never called the model: 0 model interactions, ${turn.notificationCount} notifications, ${turn.errorNotificationCount} errors`
6566
+ };
6567
+ }
6568
+ return {
6569
+ cause: `model returned no assistant output: ${turn.items.length} streamed items, ${snapshotItemCount} snapshot items`
6570
+ };
6571
+ }
6504
6572
  function log(event, fields) {
6505
6573
  return { type: "log", event, fields };
6506
6574
  }
6507
- var terminalRowStreamStates, turnMappingReadyMethod, turnMappingFailedMethod, maxTerminalTurnIds, completedWithoutAssistantOutputReason, unsupportedCodexToolCallReasonPrefix, turnTerminalFailureMethods, handledNotificationMethods;
6575
+ var terminalRowStreamStates, turnMappingReadyMethod, turnMappingFailedMethod, maxTerminalTurnIds, completedWithoutAssistantOutputReason, unsupportedCodexToolCallReasonPrefix, turnTerminalFailureMethods, handledNotificationMethods, modelInteractionMethods;
6508
6576
  var init_transition = __esm({
6509
6577
  "src/pipeline/transition.ts"() {
6510
6578
  "use strict";
@@ -6545,6 +6613,17 @@ var init_transition = __esm({
6545
6613
  "item/completed",
6546
6614
  "response_item"
6547
6615
  ]);
6616
+ modelInteractionMethods = /* @__PURE__ */ new Set([
6617
+ "item/agentMessage/delta",
6618
+ "item/reasoning/textDelta",
6619
+ "item/commandExecution/outputDelta",
6620
+ "command/exec/outputDelta",
6621
+ "item/fileChange/outputDelta",
6622
+ "item/commandExecution/terminalInteraction",
6623
+ "item/started",
6624
+ "item/completed",
6625
+ "response_item"
6626
+ ]);
6548
6627
  }
6549
6628
  });
6550
6629
 
@@ -20078,7 +20157,7 @@ var linzumiCliVersion, linzumiCliVersionText;
20078
20157
  var init_version = __esm({
20079
20158
  "src/version.ts"() {
20080
20159
  "use strict";
20081
- linzumiCliVersion = "0.0.110-beta";
20160
+ linzumiCliVersion = "0.0.111-beta";
20082
20161
  linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
20083
20162
  }
20084
20163
  });
@@ -23176,6 +23255,172 @@ var init_threadCodexWorkerIpc = __esm({
23176
23255
  }
23177
23256
  });
23178
23257
 
23258
+ // src/signupTaskSuggestionsProgress.ts
23259
+ function basenameOf(rawPath) {
23260
+ const trimmed = rawPath.trim();
23261
+ if (trimmed === "") {
23262
+ return void 0;
23263
+ }
23264
+ const withoutTrailing = trimmed.replace(/[/\\]+$/, "");
23265
+ const segments = withoutTrailing.split(/[/\\]+/);
23266
+ const last = segments[segments.length - 1];
23267
+ if (last === void 0 || last === "" || last === "." || last === "..") {
23268
+ return void 0;
23269
+ }
23270
+ if (last.length > MAX_BASENAME_LENGTH) {
23271
+ return `${last.slice(0, MAX_BASENAME_LENGTH - 1)}\u2026`;
23272
+ }
23273
+ return last;
23274
+ }
23275
+ function lookingAtFileLabel(rawPath) {
23276
+ if (typeof rawPath !== "string") {
23277
+ return void 0;
23278
+ }
23279
+ const basename10 = basenameOf(rawPath);
23280
+ return basename10 === void 0 ? void 0 : `Looking at ${basename10}...`;
23281
+ }
23282
+ function commandLabel2(command) {
23283
+ let head;
23284
+ let target;
23285
+ if (Array.isArray(command)) {
23286
+ const parts = command.filter((part) => typeof part === "string");
23287
+ const meaningful = parts.filter(
23288
+ (part) => part !== "bash" && part !== "sh" && part !== "-lc" && part !== "-c"
23289
+ );
23290
+ head = meaningful[0];
23291
+ target = meaningful.slice(1).find((part) => !part.startsWith("-"));
23292
+ } else if (typeof command === "string") {
23293
+ const tokens = command.trim().split(/\s+/);
23294
+ head = tokens[0];
23295
+ target = tokens.slice(1).find((part) => !part.startsWith("-"));
23296
+ } else {
23297
+ return void 0;
23298
+ }
23299
+ if (head === void 0) {
23300
+ return void 0;
23301
+ }
23302
+ const baseCommand = basenameOf(head) ?? head;
23303
+ if (!READ_COMMAND_PREFIXES.includes(baseCommand)) {
23304
+ return READING_REPO;
23305
+ }
23306
+ const fileLabel = target === void 0 ? void 0 : lookingAtFileLabel(target);
23307
+ return fileLabel ?? READING_REPO;
23308
+ }
23309
+ function progressLabelForCodexEvent(event) {
23310
+ if (typeof event !== "object" || event === null || Array.isArray(event)) {
23311
+ return void 0;
23312
+ }
23313
+ const record = event;
23314
+ const item = record["item"];
23315
+ if (typeof item === "object" && item !== null && !Array.isArray(item)) {
23316
+ const itemRecord = item;
23317
+ const itemType = itemRecord["item_type"] ?? itemRecord["type"];
23318
+ switch (itemType) {
23319
+ case "reasoning":
23320
+ return READING_REPO;
23321
+ case "command_execution":
23322
+ case "local_shell_call":
23323
+ return commandLabel2(itemRecord["command"] ?? itemRecord["argv"]) ?? READING_REPO;
23324
+ case "file_change":
23325
+ case "file_read":
23326
+ return lookingAtFileLabel(itemRecord["path"]) ?? READING_REPO;
23327
+ case "agent_message":
23328
+ case "assistant_message":
23329
+ return DRAFTING_TASKS;
23330
+ default:
23331
+ return void 0;
23332
+ }
23333
+ }
23334
+ const msg = record["msg"];
23335
+ const msgRecord = typeof msg === "object" && msg !== null && !Array.isArray(msg) ? msg : record;
23336
+ const msgType = msgRecord["type"] ?? record["type"];
23337
+ switch (msgType) {
23338
+ case "task_started":
23339
+ case "agent_reasoning":
23340
+ case "agent_reasoning_delta":
23341
+ case "agent_reasoning_section_break":
23342
+ return READING_REPO;
23343
+ case "exec_command_begin":
23344
+ case "exec_command_end":
23345
+ return commandLabel2(msgRecord["command"]) ?? READING_REPO;
23346
+ case "agent_message":
23347
+ case "agent_message_delta":
23348
+ return DRAFTING_TASKS;
23349
+ default:
23350
+ return void 0;
23351
+ }
23352
+ }
23353
+ function createTaskSuggestionProgressParser(onLabel) {
23354
+ let buffer = "";
23355
+ let lastLabel;
23356
+ const emit = (label) => {
23357
+ if (label === void 0 || label === lastLabel) {
23358
+ return;
23359
+ }
23360
+ lastLabel = label;
23361
+ try {
23362
+ onLabel(label);
23363
+ } catch {
23364
+ }
23365
+ };
23366
+ const consumeLine = (line) => {
23367
+ const trimmed = line.trim();
23368
+ if (trimmed === "" || trimmed[0] !== "{") {
23369
+ return;
23370
+ }
23371
+ let parsed;
23372
+ try {
23373
+ parsed = JSON.parse(trimmed);
23374
+ } catch {
23375
+ return;
23376
+ }
23377
+ emit(progressLabelForCodexEvent(parsed));
23378
+ };
23379
+ return {
23380
+ push: (chunk) => {
23381
+ buffer += chunk;
23382
+ let newlineIndex = buffer.indexOf("\n");
23383
+ while (newlineIndex !== -1) {
23384
+ const line = buffer.slice(0, newlineIndex);
23385
+ buffer = buffer.slice(newlineIndex + 1);
23386
+ consumeLine(line);
23387
+ newlineIndex = buffer.indexOf("\n");
23388
+ }
23389
+ },
23390
+ flush: () => {
23391
+ if (buffer.trim() !== "") {
23392
+ consumeLine(buffer);
23393
+ buffer = "";
23394
+ }
23395
+ }
23396
+ };
23397
+ }
23398
+ var READING_REPO, DRAFTING_TASKS, MAX_BASENAME_LENGTH, READ_COMMAND_PREFIXES;
23399
+ var init_signupTaskSuggestionsProgress = __esm({
23400
+ "src/signupTaskSuggestionsProgress.ts"() {
23401
+ "use strict";
23402
+ READING_REPO = "Reading your repository...";
23403
+ DRAFTING_TASKS = "Drafting tasks...";
23404
+ MAX_BASENAME_LENGTH = 60;
23405
+ READ_COMMAND_PREFIXES = [
23406
+ "ls",
23407
+ "cat",
23408
+ "head",
23409
+ "tail",
23410
+ "find",
23411
+ "rg",
23412
+ "grep",
23413
+ "tree",
23414
+ "wc",
23415
+ "sed",
23416
+ "awk",
23417
+ "pwd",
23418
+ "stat",
23419
+ "file"
23420
+ ];
23421
+ }
23422
+ });
23423
+
23179
23424
  // src/signupTaskSuggestions.ts
23180
23425
  import { spawn as spawn7 } from "node:child_process";
23181
23426
  import { mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
@@ -23200,7 +23445,8 @@ async function suggestSignupTasksWithCodex(args) {
23200
23445
  model,
23201
23446
  modelProvider: args.modelProvider,
23202
23447
  previousResponse,
23203
- timeoutMs: remaining
23448
+ timeoutMs: remaining,
23449
+ onProgress: args.onProgress
23204
23450
  });
23205
23451
  } catch (error) {
23206
23452
  throw error instanceof Error ? error : new Error(String(error));
@@ -23251,7 +23497,8 @@ async function runCodexTaskSuggestion(args) {
23251
23497
  outputPath,
23252
23498
  prompt,
23253
23499
  timeoutMs: args.timeoutMs
23254
- })
23500
+ }),
23501
+ args.onProgress
23255
23502
  );
23256
23503
  return readFileSync16(outputPath, "utf8");
23257
23504
  } finally {
@@ -23288,6 +23535,14 @@ function codexTaskSuggestionProcess(args) {
23288
23535
  args.schemaPath,
23289
23536
  "--output-last-message",
23290
23537
  args.outputPath,
23538
+ // LIVE PROGRESS: stream the agentic turn as JSONL events on stdout so we
23539
+ // can parse privacy-safe step labels ("Reading your repository...",
23540
+ // "Looking at <file>...", "Drafting tasks...") and relay them to the
23541
+ // onboarding wizard while the (up to ~150s) turn runs. The final task
23542
+ // JSON still comes from --output-last-message, so this is purely additive
23543
+ // - if a codex build ignores or changes --json, we simply get no progress
23544
+ // and the suggestion result is unaffected.
23545
+ "--json",
23291
23546
  "--color",
23292
23547
  "never",
23293
23548
  "-"
@@ -23381,15 +23636,28 @@ function parseTaskSuggestionResponse(response) {
23381
23636
  return void 0;
23382
23637
  }
23383
23638
  }
23384
- function runProcess2(args) {
23639
+ function runProcess2(args, onProgress) {
23385
23640
  return new Promise((resolveProcess, rejectProcess) => {
23386
23641
  const child = spawn7(args.command, [...args.args], {
23387
23642
  cwd: args.cwd,
23388
23643
  // Inherit the runner env (codex auth, PATH, etc.) and layer the wafer
23389
23644
  // proxy credential (LINZUMI_LLM_PROXY_TOKEN) on top when wafer-routed.
23390
23645
  ...args.env === void 0 ? {} : { env: { ...process.env, ...args.env } },
23391
- stdio: ["pipe", "ignore", "pipe"]
23646
+ // LIVE PROGRESS: capture stdout (the `--json` JSONL event stream) only
23647
+ // when a progress callback is supplied; otherwise discard it as before.
23648
+ // We never depend on stdout for the result (that's --output-last-message),
23649
+ // so stdout parsing is purely a best-effort enhancement.
23650
+ stdio: ["pipe", onProgress === void 0 ? "ignore" : "pipe", "pipe"]
23392
23651
  });
23652
+ const progress = onProgress === void 0 ? void 0 : createTaskSuggestionProgressParser(onProgress);
23653
+ if (progress !== void 0) {
23654
+ child.stdout?.on("data", (chunk) => {
23655
+ try {
23656
+ progress.push(String(chunk));
23657
+ } catch {
23658
+ }
23659
+ });
23660
+ }
23393
23661
  let stderr = "";
23394
23662
  let settled = false;
23395
23663
  const timeout = setTimeout(() => {
@@ -23402,6 +23670,10 @@ function runProcess2(args) {
23402
23670
  }
23403
23671
  settled = true;
23404
23672
  clearTimeout(timeout);
23673
+ try {
23674
+ progress?.flush();
23675
+ } catch {
23676
+ }
23405
23677
  if (error === void 0) {
23406
23678
  resolveProcess();
23407
23679
  return;
@@ -23433,6 +23705,7 @@ var init_signupTaskSuggestions = __esm({
23433
23705
  "src/signupTaskSuggestions.ts"() {
23434
23706
  "use strict";
23435
23707
  init_codexAppServer();
23708
+ init_signupTaskSuggestionsProgress();
23436
23709
  DEFAULT_CODEX_AUTH_SUGGESTION_MODEL = "gpt-5.4-mini";
23437
23710
  DEFAULT_WAFER_SUGGESTION_MODEL = "GLM-5.2";
23438
23711
  SUGGESTION_TIMEOUT_MS = 15e4;
@@ -29596,7 +29869,10 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
29596
29869
  return createRunnerProject(control, options, allowedCwds);
29597
29870
  }
29598
29871
  case "suggest_tasks": {
29599
- return suggestRunnerTasks(control, options, allowedCwds.value);
29872
+ return suggestRunnerTasks(control, options, allowedCwds.value, {
29873
+ kandan,
29874
+ topic
29875
+ });
29600
29876
  }
29601
29877
  case "remote_codex_exec": {
29602
29878
  return await applyRemoteCodexExecControl(
@@ -33553,7 +33829,7 @@ function resolveSuggestTasksModelProvider(control) {
33553
33829
  env: codexModelProviderAppServerEnv(runtime)
33554
33830
  };
33555
33831
  }
33556
- async function suggestRunnerTasks(control, options, allowedCwds) {
33832
+ async function suggestRunnerTasks(control, options, allowedCwds, progressSink) {
33557
33833
  const requestId = stringValue(control.requestId) ?? null;
33558
33834
  const cwd = resolveAllowedCwd(control.cwd, allowedCwds);
33559
33835
  if (!cwd.ok) {
@@ -33567,12 +33843,23 @@ async function suggestRunnerTasks(control, options, allowedCwds) {
33567
33843
  }
33568
33844
  const modelProvider = resolveSuggestTasksModelProvider(control);
33569
33845
  const model = stringValue(control.model)?.trim();
33846
+ const workspaceSlug = stringValue(control.workspace)?.trim();
33847
+ const onProgress = progressSink === void 0 || requestId === null ? void 0 : (label) => {
33848
+ void progressSink.kandan.pushIfConnected(progressSink.topic, "suggest_tasks_progress", {
33849
+ instanceId: options.runnerId,
33850
+ controlType: control.type,
33851
+ requestId,
33852
+ ...workspaceSlug === void 0 || workspaceSlug === "" ? {} : { workspace: workspaceSlug },
33853
+ label
33854
+ }).catch(() => void 0);
33855
+ };
33570
33856
  try {
33571
33857
  const tasks = await suggestSignupTasksWithCodex({
33572
33858
  projectPath: cwd.cwd,
33573
33859
  codexBin: options.codexBin,
33574
33860
  ...model === void 0 || model === "" ? {} : { model },
33575
- ...modelProvider === void 0 ? {} : { modelProvider }
33861
+ ...modelProvider === void 0 ? {} : { modelProvider },
33862
+ ...onProgress === void 0 ? {} : { onProgress }
33576
33863
  });
33577
33864
  return {
33578
33865
  instanceId: options.runnerId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.110-beta",
3
+ "version": "0.0.111-beta",
4
4
  "description": "Linzumi CLI \u2014 point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
5
5
  "type": "module",
6
6
  "bin": {