@linzumi/cli 0.0.109-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 +404 -46
  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.109-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.109-beta";
20160
+ linzumiCliVersion = "0.0.111-beta";
20082
20161
  linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
20083
20162
  }
20084
20163
  });
@@ -23176,30 +23255,225 @@ 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";
23182
23427
  import { tmpdir as tmpdir2 } from "node:os";
23183
23428
  import { join as join20 } from "node:path";
23184
23429
  async function suggestSignupTasksWithCodex(args) {
23185
- const attempts = 2;
23430
+ const model = resolveTaskSuggestionModel(args.model, args.modelProvider);
23431
+ const startedAt = Date.now();
23186
23432
  let previousResponse;
23187
- for (let attempt = 1; attempt <= attempts; attempt += 1) {
23188
- const response = await runCodexTaskSuggestion({
23189
- projectPath: args.projectPath,
23190
- codexBin: args.codexBin,
23191
- model: args.model ?? process.env.LINZUMI_SIGNUP_TASK_MODEL ?? "gpt-5.4-mini",
23192
- previousResponse
23193
- });
23433
+ let lastFailure;
23434
+ for (let attempt = 0; attempt <= MAX_SCHEMA_REPAIR_ATTEMPTS; attempt += 1) {
23435
+ const elapsed = Date.now() - startedAt;
23436
+ const remaining = SUGGESTION_TIMEOUT_MS - elapsed;
23437
+ if (remaining <= 0) {
23438
+ break;
23439
+ }
23440
+ let response;
23441
+ try {
23442
+ response = await runCodexTaskSuggestion({
23443
+ projectPath: args.projectPath,
23444
+ codexBin: args.codexBin,
23445
+ model,
23446
+ modelProvider: args.modelProvider,
23447
+ previousResponse,
23448
+ timeoutMs: remaining,
23449
+ onProgress: args.onProgress
23450
+ });
23451
+ } catch (error) {
23452
+ throw error instanceof Error ? error : new Error(String(error));
23453
+ }
23194
23454
  const tasks = parseTaskSuggestionResponse(response);
23195
23455
  if (tasks !== void 0) {
23196
23456
  return tasks;
23197
23457
  }
23198
23458
  previousResponse = response;
23459
+ lastFailure = new Error(
23460
+ "Codex returned a response that did not match the task suggestion schema"
23461
+ );
23199
23462
  }
23200
- throw new Error(
23201
- "Codex did not return valid task suggestions after 2 attempts"
23202
- );
23463
+ throw lastFailure ?? new Error("Codex did not return valid task suggestions in the time budget");
23464
+ }
23465
+ function resolveTaskSuggestionModel(explicitModel, modelProvider) {
23466
+ const envModel = process.env.LINZUMI_SIGNUP_TASK_MODEL;
23467
+ if (explicitModel !== void 0 && explicitModel !== "") {
23468
+ return explicitModel;
23469
+ }
23470
+ if (envModel !== void 0 && envModel !== "") {
23471
+ return envModel;
23472
+ }
23473
+ if (modelProvider !== void 0) {
23474
+ return DEFAULT_WAFER_SUGGESTION_MODEL;
23475
+ }
23476
+ return DEFAULT_CODEX_AUTH_SUGGESTION_MODEL;
23203
23477
  }
23204
23478
  async function runCodexTaskSuggestion(args) {
23205
23479
  const tempRoot = mkdtempSync3(join20(tmpdir2(), "linzumi-signup-codex-tasks-"));
@@ -23217,11 +23491,14 @@ async function runCodexTaskSuggestion(args) {
23217
23491
  codexTaskSuggestionProcess({
23218
23492
  command: args.codexBin,
23219
23493
  model: args.model,
23494
+ modelProvider: args.modelProvider,
23220
23495
  projectPath: args.projectPath,
23221
23496
  schemaPath,
23222
23497
  outputPath,
23223
- prompt
23224
- })
23498
+ prompt,
23499
+ timeoutMs: args.timeoutMs
23500
+ }),
23501
+ args.onProgress
23225
23502
  );
23226
23503
  return readFileSync16(outputPath, "utf8");
23227
23504
  } finally {
@@ -23236,9 +23513,19 @@ function codexTaskSuggestionProcess(args) {
23236
23513
  "--model",
23237
23514
  args.model,
23238
23515
  "-c",
23239
- 'model_reasoning_effort="low"',
23516
+ // RELIABILITY: minimal reasoning effort keeps the agentic turn fast and
23517
+ // far less flaky while still producing repo-specific tasks (it still
23518
+ // inspects the real repo below). The latency/flake budget was the main
23519
+ // driver of runner_task_suggestion_failed.
23520
+ 'model_reasoning_effort="minimal"',
23240
23521
  "-c",
23241
23522
  'approval_policy="never"',
23523
+ // Wafer routing: render the `-c model_providers.linzumi.*` config args
23524
+ // plus `-c model_provider=linzumi`, identical to the main per-thread codex
23525
+ // app-server start (codexModelProviderConfigArgs). Secrets stay in env via
23526
+ // env_key/env_http_headers, never argv. Omitted for native-codex-auth
23527
+ // users, who keep their own provider.
23528
+ ...args.modelProvider === void 0 ? [] : codexModelProviderConfigArgs(args.modelProvider.config),
23242
23529
  "--sandbox",
23243
23530
  "read-only",
23244
23531
  "--skip-git-repo-check",
@@ -23248,13 +23535,22 @@ function codexTaskSuggestionProcess(args) {
23248
23535
  args.schemaPath,
23249
23536
  "--output-last-message",
23250
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",
23251
23546
  "--color",
23252
23547
  "never",
23253
23548
  "-"
23254
23549
  ],
23255
23550
  cwd: args.projectPath,
23256
23551
  stdin: args.prompt,
23257
- timeoutMs: 55e3
23552
+ ...args.modelProvider === void 0 ? {} : { env: args.modelProvider.env },
23553
+ timeoutMs: args.timeoutMs ?? SUGGESTION_TIMEOUT_MS
23258
23554
  };
23259
23555
  }
23260
23556
  function taskSuggestionPrompt(previousResponse) {
@@ -23272,16 +23568,13 @@ function taskSuggestionPrompt(previousResponse) {
23272
23568
  "Task:",
23273
23569
  "Inspect this repository read-only and suggest exactly 5 useful quick starter tasks for Codex agents.",
23274
23570
  "",
23275
- "GitHub context:",
23276
- "- First check whether the GitHub CLI is available and authenticated with a cheap command such as `gh auth status`.",
23277
- "- If `gh` is missing, unauthenticated, or this repository has no GitHub remote, skip GitHub context and rely on local files.",
23278
- "- When `gh` is authenticated, use bounded reads such as `gh pr list --limit 30 --state all` and `gh issue list --limit 30 --state all` to inspect recent open and closed pull requests and issues.",
23279
- "- Be mindful of GitHub rate limits, CPU usage, and network usage; prefer one or two small calls and do not paginate or crawl exhaustively.",
23280
- "- If a GitHub call returns unauthorized, forbidden, or not found for this repository, stop GitHub lookups for this repository.",
23281
- "- Use pull request and issue titles, states, labels, and recent activity to infer what the repo is about, what the user has been working on, and likely next tasks.",
23571
+ "How to inspect (be fast - aim to answer within a single short pass):",
23572
+ "- Read the repo locally: top-level files, README, package manifests, and a shallow look at the main source directories are enough to infer what the project is.",
23573
+ "- Base the suggestions on the ACTUAL repository contents you observe, not on generic templates.",
23574
+ "- Do NOT crawl GitHub. Skip `gh` entirely; local files are sufficient and far faster.",
23282
23575
  "",
23283
23576
  "Constraints:",
23284
- "- Prefer tasks that are small, concrete, and likely to produce useful first results.",
23577
+ "- Prefer tasks that are small, concrete, and specific to THIS repository (reference real files, modules, or features you saw).",
23285
23578
  "- Do not modify files.",
23286
23579
  "- Do not include markdown fences or prose outside the JSON response.",
23287
23580
  "- Keep each task title under 120 characters.",
@@ -23343,12 +23636,28 @@ function parseTaskSuggestionResponse(response) {
23343
23636
  return void 0;
23344
23637
  }
23345
23638
  }
23346
- function runProcess2(args) {
23639
+ function runProcess2(args, onProgress) {
23347
23640
  return new Promise((resolveProcess, rejectProcess) => {
23348
23641
  const child = spawn7(args.command, [...args.args], {
23349
23642
  cwd: args.cwd,
23350
- stdio: ["pipe", "ignore", "pipe"]
23643
+ // Inherit the runner env (codex auth, PATH, etc.) and layer the wafer
23644
+ // proxy credential (LINZUMI_LLM_PROXY_TOKEN) on top when wafer-routed.
23645
+ ...args.env === void 0 ? {} : { env: { ...process.env, ...args.env } },
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"]
23351
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
+ }
23352
23661
  let stderr = "";
23353
23662
  let settled = false;
23354
23663
  const timeout = setTimeout(() => {
@@ -23361,6 +23670,10 @@ function runProcess2(args) {
23361
23670
  }
23362
23671
  settled = true;
23363
23672
  clearTimeout(timeout);
23673
+ try {
23674
+ progress?.flush();
23675
+ } catch {
23676
+ }
23364
23677
  if (error === void 0) {
23365
23678
  resolveProcess();
23366
23679
  return;
@@ -23387,9 +23700,16 @@ function runProcess2(args) {
23387
23700
  child.stdin?.end(args.stdin);
23388
23701
  });
23389
23702
  }
23703
+ var DEFAULT_CODEX_AUTH_SUGGESTION_MODEL, DEFAULT_WAFER_SUGGESTION_MODEL, SUGGESTION_TIMEOUT_MS, MAX_SCHEMA_REPAIR_ATTEMPTS;
23390
23704
  var init_signupTaskSuggestions = __esm({
23391
23705
  "src/signupTaskSuggestions.ts"() {
23392
23706
  "use strict";
23707
+ init_codexAppServer();
23708
+ init_signupTaskSuggestionsProgress();
23709
+ DEFAULT_CODEX_AUTH_SUGGESTION_MODEL = "gpt-5.4-mini";
23710
+ DEFAULT_WAFER_SUGGESTION_MODEL = "GLM-5.2";
23711
+ SUGGESTION_TIMEOUT_MS = 15e4;
23712
+ MAX_SCHEMA_REPAIR_ATTEMPTS = 1;
23393
23713
  }
23394
23714
  });
23395
23715
 
@@ -27033,7 +27353,10 @@ async function openLocalCodexRunner(options, log2, cleanup, close, runnerDrainHo
27033
27353
  const entries = parseResumeReconcileEntries(reconcileValue);
27034
27354
  for (const entry of entries) {
27035
27355
  if (entry.leaseEpoch !== void 0) {
27036
- const adopted = leaseEpochRegistry.adopt(entry.threadId, entry.leaseEpoch);
27356
+ const adopted = leaseEpochRegistry.adopt(
27357
+ entry.threadId,
27358
+ entry.leaseEpoch
27359
+ );
27037
27360
  if (adopted) {
27038
27361
  log2("runner.resume_reconcile_lease_adopted", {
27039
27362
  kandanThreadId: entry.threadId,
@@ -27896,10 +28219,7 @@ function appliedSourceSeqStorePath(runnerId) {
27896
28219
  const sanitized = runnerId.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, 80);
27897
28220
  const digest = createHash7("sha256").update(runnerId).digest("hex").slice(0, 8);
27898
28221
  const stem = sanitized === "" ? "runner" : sanitized;
27899
- return join22(
27900
- controlCursorsDir(),
27901
- `${stem}.${digest}.source-seq.json`
27902
- );
28222
+ return join22(controlCursorsDir(), `${stem}.${digest}.source-seq.json`);
27903
28223
  }
27904
28224
  function startTurnWatermarkKey(topic, threadId) {
27905
28225
  return `${topic} ${threadId}`;
@@ -29549,7 +29869,10 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
29549
29869
  return createRunnerProject(control, options, allowedCwds);
29550
29870
  }
29551
29871
  case "suggest_tasks": {
29552
- return suggestRunnerTasks(control, options, allowedCwds.value);
29872
+ return suggestRunnerTasks(control, options, allowedCwds.value, {
29873
+ kandan,
29874
+ topic
29875
+ });
29553
29876
  }
29554
29877
  case "remote_codex_exec": {
29555
29878
  return await applyRemoteCodexExecControl(
@@ -33486,7 +33809,27 @@ function projectPathExists(projectPath) {
33486
33809
  throw error;
33487
33810
  }
33488
33811
  }
33489
- async function suggestRunnerTasks(control, options, allowedCwds) {
33812
+ function resolveSuggestTasksModelProvider(control) {
33813
+ if (stringValue(control.modelProvider)?.trim() !== "wafer") {
33814
+ return void 0;
33815
+ }
33816
+ const llmProxy = objectValue(control.llmProxy);
33817
+ const baseUrl = stringValue(llmProxy?.baseUrl)?.trim();
33818
+ const token = stringValue(llmProxy?.token)?.trim();
33819
+ if (baseUrl === void 0 || baseUrl === "" || token === void 0 || token === "") {
33820
+ return void 0;
33821
+ }
33822
+ const runtime = {
33823
+ provider: "wafer",
33824
+ llmProxyBaseUrl: baseUrl,
33825
+ llmProxyToken: token
33826
+ };
33827
+ return {
33828
+ config: linzumiCodexModelProviderConfig(runtime),
33829
+ env: codexModelProviderAppServerEnv(runtime)
33830
+ };
33831
+ }
33832
+ async function suggestRunnerTasks(control, options, allowedCwds, progressSink) {
33490
33833
  const requestId = stringValue(control.requestId) ?? null;
33491
33834
  const cwd = resolveAllowedCwd(control.cwd, allowedCwds);
33492
33835
  if (!cwd.ok) {
@@ -33498,10 +33841,25 @@ async function suggestRunnerTasks(control, options, allowedCwds) {
33498
33841
  error: cwd.reason
33499
33842
  };
33500
33843
  }
33844
+ const modelProvider = resolveSuggestTasksModelProvider(control);
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
+ };
33501
33856
  try {
33502
33857
  const tasks = await suggestSignupTasksWithCodex({
33503
33858
  projectPath: cwd.cwd,
33504
- codexBin: options.codexBin
33859
+ codexBin: options.codexBin,
33860
+ ...model === void 0 || model === "" ? {} : { model },
33861
+ ...modelProvider === void 0 ? {} : { modelProvider },
33862
+ ...onProgress === void 0 ? {} : { onProgress }
33505
33863
  });
33506
33864
  return {
33507
33865
  instanceId: options.runnerId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.109-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": {