@kw-yeh/vibeflow 4.11.36 → 4.11.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vibeflow.mjs CHANGED
@@ -168,8 +168,8 @@ function shellArgs(shell, command) {
168
168
  const isPosixShell = process.platform !== "win32" || shell.toLowerCase().endsWith("bash.exe");
169
169
  if (isPosixShell) return command ? ["-lic", command] : ["-l"];
170
170
  if (command) {
171
- const message = GIT_BASH_MISSING_MESSAGE.replace(/'/g, "''");
172
- return ["-NoProfile", "-NonInteractive", "-Command", `Write-Host '${message}' -ForegroundColor Yellow; exit 1`];
171
+ const message2 = GIT_BASH_MISSING_MESSAGE.replace(/'/g, "''");
172
+ return ["-NoProfile", "-NonInteractive", "-Command", `Write-Host '${message2}' -ForegroundColor Yellow; exit 1`];
173
173
  }
174
174
  return ["-NoProfile"];
175
175
  }
@@ -637,7 +637,8 @@ function masterSystemPrompt(opts) {
637
637
  "1. \u5224\u65B7\uFF1A\u4E00\u5F35\u5361\u505A\u5F97\u5B8C\u5C31\u53EA\u5EFA\u4E00\u5F35\uFF1B\u6D89\u53CA\u591A\u500B repo \u6216\u53EF\u5E73\u884C\u7684\u7368\u7ACB\u5DE5\u4F5C\u624D\u62C6\u6210\u591A\u5F35\u3002\u6BCF\u5F35\u53EA\u52D5\u4E00\u500B repo\uFF0C\u5404\u81EA\u80FD\u7368\u7ACB\u9A57\u6536\u3002",
638
638
  '2. \u6D3E\u5DE5\uFF1AVF task create --store-path "$VIBEFLOW_STORE_DIR" --master "$VIBEFLOW_TASK_ID" --project <repo \u7D55\u5C0D\u8DEF\u5F91> --title <\u6A19\u984C> --prompt <\u5167\u5BB9>',
639
639
  " prompt \u5FC5\u9808\u81EA\u5305\u542B\uFF1A\u80CC\u666F\u3001\u7BC4\u570D\u3001\u9A57\u6536\u689D\u4EF6\u3001\u8207\u5176\u4ED6\u5361\u7684\u4ECB\u9762\u7D04\u5B9A\u2014\u2014slave \u770B\u4E0D\u5230\u4F60\u7684\u5C0D\u8A71\u3002",
640
- " \u9700\u8981\u6642\u53EF\u52A0 --agent / --model / --effort / --base-branch\u3002"
640
+ " \u6A19\u984C\u8981\u77ED\u3001\u5F7C\u6B64\u4E00\u773C\u5206\u5F97\u51FA\u4F86\uFF1A\u4EE5\u9019\u5F35\u5361\u8CA0\u8CAC\u7684\u90E8\u5206\u958B\u982D\uFF08\u4F8B\u5982\u300C\u5F8C\u7AEF\uFF1A\u2026\u300D\u300C\u9A57\u6536\uFF1A\u2026\u300D\uFF09\uFF0C\u4E0D\u8981\u8B93\u591A\u5F35\u5361\u5171\u7528\u540C\u4E00\u6BB5\u958B\u982D\u3002",
641
+ " \u9810\u8A2D\u4E0D\u8981\u52A0 --model / --effort\uFF1A\u6C92\u6307\u5B9A\u6642 Jev \u6703\u4F9D\u4EFB\u52D9\u8907\u96DC\u5EA6\u6311\u6A21\u578B\u8207 effort\uFF1B\u53EA\u6709\u4EFB\u52D9\u660E\u78BA\u9700\u8981\u7279\u5B9A\u6A21\u578B\u6216 effort \u6642\u624D\u6307\u5B9A\u3002\u9700\u8981\u6642\u53EF\u52A0 --agent / --base-branch\u3002"
641
642
  );
642
643
  lines.push(
643
644
  opts.autoMode ? '3. \u555F\u52D5\uFF1A\u6C92\u6709\u524D\u7F6E\u4F9D\u8CF4\u7684\u5361\u76F4\u63A5 VF task start --store-path "$VIBEFLOW_STORE_DIR" --task <id>\uFF1B\u6709\u4F9D\u8CF4\u7684\u5148\u7559\u5728 Backlog\uFF0C\u524D\u7F6E\u5361\u5B8C\u6210\u5F8C\u518D\u555F\u52D5\u3002' : "3. \u555F\u52D5\uFF1A\u4E0D\u8981\u81EA\u5DF1\u555F\u52D5\u5361\u7247\u3002\u5217\u51FA\u4F60\u5EFA\u4E86\u54EA\u4E9B\u5361\u3001\u5EFA\u8B70\u7684\u555F\u52D5\u9806\u5E8F\uFF0C\u7B49\u4F7F\u7528\u8005\u5728 VibeFlow \u6309\u958B\u59CB\u3002"
@@ -649,16 +650,21 @@ function masterSystemPrompt(opts) {
649
650
  " \u9700\u8981\u7368\u7ACB\u9A57\u6536\u6642\u53E6\u958B\u4E00\u5F35\u9A57\u6536\u5361\u3002\u9A57\u6536\u56DE\u5831\u554F\u984C\u6642\uFF0C\u628A\u5177\u9AD4\u554F\u984C\u8207\u91CD\u73FE\u6B65\u9A5F\u6574\u7406\u597D\u8F49\u7D66\u8CA0\u8CAC\u958B\u767C\u7684\u5361\u4FEE\u6B63\uFF0C\u4FEE\u5B8C\u518D\u8ACB\u9A57\u6536\u5361\u8907\u9A57\uFF0C\u76F4\u5230\u901A\u904E\u3002",
650
651
  " task send \u56DE TARGET_BLOCKED \u4EE3\u8868\u5C0D\u65B9\u5361\u5728\u6B0A\u9650\u78BA\u8A8D\uFF0C\u8ACB\u4F7F\u7528\u8005\u5230\u90A3\u5F35\u5361\u8655\u7406\u3002",
651
652
  "6. PR\uFF1A\u9A57\u6536\u901A\u904E\u5F8C\uFF0C\u4EA4\u4EE3\u958B\u767C\u5361\u7528 pr skill \u958B PR\uFF08\u74B0\u5883\u88E1\u6C92\u6709\u9019\u500B skill \u5C31\u7528 gh pr create\uFF09\uFF0C\u4E26\u56DE\u5831 PR \u9023\u7D50\u7D66\u4F60\u3002",
652
- "7. \u4F7F\u7528\u8005\u4E3B\u8981\u53EA\u8DDF\u4F60\u6E9D\u901A\uFF1A\u4E3B\u52D5\u5411\u4F7F\u7528\u8005\u56DE\u5831\u6574\u9AD4\u9032\u5EA6\u8207\u9700\u8981\u4F7F\u7528\u8005\u6C7A\u5B9A\u7684\u4E8B\u3002\u4F60\u4E0D\u6703\u628A\u5361\u7247\u79FB\u5230 Done\u3001\u4E0D\u6703\u5408\u4F75 PR\uFF1B\u5168\u90E8\u5B8C\u6210\u5F8C\u6574\u7406\u6BCF\u5F35\u5361\u7684\u7D50\u679C\u8207 PR \u9023\u7D50\uFF0C\u4EA4\u7D66\u4F7F\u7528\u8005\u505A\u6700\u5F8C\u78BA\u8A8D\u3002"
653
+ "7. \u4F7F\u7528\u8005\u4E3B\u8981\u53EA\u8DDF\u4F60\u6E9D\u901A\uFF1A\u4E3B\u52D5\u5411\u4F7F\u7528\u8005\u56DE\u5831\u6574\u9AD4\u9032\u5EA6\u8207\u9700\u8981\u4F7F\u7528\u8005\u6C7A\u5B9A\u7684\u4E8B\u3002\u4F60\u4E0D\u6703\u628A\u5361\u7247\u79FB\u5230 Done\u3001\u4E0D\u6703\u5408\u4F75 PR\uFF1B\u5168\u90E8\u5B8C\u6210\u5F8C\u6574\u7406\u6BCF\u5F35\u5361\u7684\u7D50\u679C\u8207 PR \u9023\u7D50\uFF0C\u4EA4\u7D66\u4F7F\u7528\u8005\u505A\u6700\u5F8C\u78BA\u8A8D\u3002",
654
+ " \u8DDF\u4F7F\u7528\u8005\u63D0\u5230\u4EFB\u4F55\u4E00\u5F35\u5361\u6642\uFF0C\u4E00\u5F8B\u5BEB\u6210 <id>\u300C<\u6A19\u984C>\u300D\u2014\u2014\u5361\u7247\u4E0A\u770B\u5F97\u5230 id\uFF0C\u4F7F\u7528\u8005\u9760\u5B83\u5C0D\u4E0A\u662F\u54EA\u4E00\u5F35\u3002"
653
655
  );
654
656
  return lines.join("\n");
655
657
  }
656
- function buildPrompt(task) {
658
+ function buildPrompt(task, preface) {
657
659
  const lines = [`\u4EFB\u52D9\u6A19\u984C\uFF1A${task.title}`];
658
660
  const description = task.description?.trim();
659
661
  if (description) {
660
662
  lines.push("", "\u4EFB\u52D9\u63CF\u8FF0\uFF1A", description);
661
663
  }
664
+ const context = preface?.context.trim();
665
+ if (preface && context) {
666
+ lines.push("", `\u524D\u7F6E\u5206\u6790\uFF08${preface.model}\uFF0C\u50C5\u4F9B\u53C3\u8003\uFF09\uFF1A`, context);
667
+ }
662
668
  return lines.join("\n");
663
669
  }
664
670
  function executorSessionId(taskId, runId) {
@@ -716,9 +722,9 @@ function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
716
722
  const custom = resolveSystemPrompt(systemPrompt);
717
723
  const canWrite = Boolean(opts?.boardCli?.cliPath);
718
724
  const role = task.role === "master" ? masterSystemPrompt({ canWrite, autoMode: opts?.autoMode === true }) : task.masterId ? slaveSystemPrompt({ canWrite }) : null;
719
- const sys = role ? [custom, role].filter(Boolean).join("\n\n") : custom;
725
+ const sys = task.masterId && task.role !== "master" ? role ?? "" : role ? [custom, role].filter(Boolean).join("\n\n") : custom;
720
726
  const includeTaskPrompt = opts?.includeTaskPrompt !== false;
721
- const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
727
+ const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task, opts?.preface) : "";
722
728
  const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
723
729
  return boardEnvPrefix(task, opts, workspacePath) + assembleCommand(
724
730
  agent,
@@ -984,13 +990,13 @@ function reduceClaudeLine(state, line) {
984
990
  if (!entry) return state;
985
991
  const at = parseTime(entry.timestamp);
986
992
  const next = { ...state, startedAt: state.startedAt ?? at };
987
- const message = entry.message;
988
- const content = Array.isArray(message?.content) ? message.content : [];
989
- if (entry.type === "assistant" && message) {
990
- const usage = message.usage;
991
- if (usage && typeof message.id === "string") {
993
+ const message2 = entry.message;
994
+ const content = Array.isArray(message2?.content) ? message2.content : [];
995
+ if (entry.type === "assistant" && message2) {
996
+ const usage = message2.usage;
997
+ if (usage && typeof message2.id === "string") {
992
998
  const u = claudeUsage(usage);
993
- next.usageById = { ...state.usageById, [message.id]: u };
999
+ next.usageById = { ...state.usageById, [message2.id]: u };
994
1000
  next.contextTokens = u.input + u.cacheRead + u.cacheWrite;
995
1001
  }
996
1002
  let tool;
@@ -1006,11 +1012,11 @@ function reduceClaudeLine(state, line) {
1006
1012
  }
1007
1013
  }
1008
1014
  if (tool) next.activity = { state: "working", tool, at };
1009
- else if (message.stop_reason === "end_turn") next.activity = { state: "waiting", waitingFor: "input", at };
1015
+ else if (message2.stop_reason === "end_turn") next.activity = { state: "waiting", waitingFor: "input", at };
1010
1016
  else next.activity = { state: "working", at };
1011
1017
  return next;
1012
1018
  }
1013
- if (entry.type === "user" && message) {
1019
+ if (entry.type === "user" && message2) {
1014
1020
  const result = entry.toolUseResult && typeof entry.toolUseResult === "object" ? entry.toolUseResult : void 0;
1015
1021
  let pending = next.pendingTasks;
1016
1022
  for (const block of content) {
@@ -1340,7 +1346,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1340
1346
  const [focus, setFocus] = useState(initialFocus);
1341
1347
  const [editing, setEditing] = useState(null);
1342
1348
  const [draft, setDraft] = useState("");
1343
- const [message, setMessage] = useState(initialMessage ?? "");
1349
+ const [message2, setMessage] = useState(initialMessage ?? "");
1344
1350
  const [busy, setBusy] = useState(null);
1345
1351
  const [loadingGit, setLoadingGit] = useState(false);
1346
1352
  const [confirmLeave, setConfirmLeave] = useState(false);
@@ -1559,7 +1565,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1559
1565
  )
1560
1566
  ),
1561
1567
  extra ? h(Text, { dimColor: true }, ` ${extra}`) : null,
1562
- confirmLeave ? h(Text, { color: "yellow" }, "\u6709\u672A\u5132\u5B58\u7684\u8B8A\u66F4\uFF0C\u78BA\u5B9A\u8981\u96E2\u958B\uFF1F\uFF08y/n\uFF09") : message ? h(Text, { color: "yellow" }, message) : h(Text, { dimColor: true }, hint)
1568
+ confirmLeave ? h(Text, { color: "yellow" }, "\u6709\u672A\u5132\u5B58\u7684\u8B8A\u66F4\uFF0C\u78BA\u5B9A\u8981\u96E2\u958B\uFF1F\uFF08y/n\uFF09") : message2 ? h(Text, { color: "yellow" }, message2) : h(Text, { dimColor: true }, hint)
1563
1569
  );
1564
1570
  }
1565
1571
  function showForm(api, ctx, initial, pristine, initialFocus = 0, initialMessage) {
@@ -1700,7 +1706,7 @@ function BoardView({ opts, initial, initialMessage, onAction }) {
1700
1706
  const [sel, setSel] = useState2(initial);
1701
1707
  const [running, setRunning] = useState2(/* @__PURE__ */ new Set());
1702
1708
  const [backend, setBackend] = useState2("pty");
1703
- const [message, setMessage] = useState2(initialMessage ?? "");
1709
+ const [message2, setMessage] = useState2(initialMessage ?? "");
1704
1710
  const [confirm, setConfirm] = useState2(null);
1705
1711
  const [help, setHelp] = useState2(false);
1706
1712
  const [progress, setProgress] = useState2({});
@@ -1911,7 +1917,7 @@ function BoardView({ opts, initial, initialMessage, onAction }) {
1911
1917
  Box2,
1912
1918
  { flexDirection: "column" },
1913
1919
  h2(Box2, { flexDirection: "row" }, ...columns),
1914
- confirm ? h2(Text2, { color: "yellow" }, confirm.prompt) : message ? h2(Text2, { color: "yellow" }, message) : h2(
1920
+ confirm ? h2(Text2, { color: "yellow" }, confirm.prompt) : message2 ? h2(Text2, { color: "yellow" }, message2) : h2(
1915
1921
  Text2,
1916
1922
  { dimColor: true },
1917
1923
  `h/l \u5207\u6B04 \xB7 j/k \u9078\u5361 \xB7 H/L \u79FB\u52D5 \xB7 Enter \u7D42\u7AEF \xB7 n \u65B0\u589E \xB7 e \u7DE8\u8F2F \xB7 d diff \xB7 w Web \xB7 ? \u8AAA\u660E \xB7 q \u96E2\u958B [${backend}]`
@@ -2035,24 +2041,24 @@ async function runForm(opts, action) {
2035
2041
  }
2036
2042
  async function runTui(opts) {
2037
2043
  let sel = { column: 1, index: 0 };
2038
- let message;
2044
+ let message2;
2039
2045
  for (; ; ) {
2040
- const result = await showBoard(opts, sel, message);
2046
+ const result = await showBoard(opts, sel, message2);
2041
2047
  sel = result.sel;
2042
- message = void 0;
2048
+ message2 = void 0;
2043
2049
  const { action } = result;
2044
2050
  if (action.type === "quit") return;
2045
2051
  try {
2046
- if (action.type === "attach") message = await openTerminal(opts, action.task);
2052
+ if (action.type === "attach") message2 = await openTerminal(opts, action.task);
2047
2053
  else if (action.type === "diff") showDiff(action.task);
2048
2054
  else {
2049
2055
  process.stdout.write(CLEAR_SCREEN);
2050
2056
  const done = await runForm(opts, action);
2051
- message = done.message;
2057
+ message2 = done.message;
2052
2058
  if (done.sel) sel = done.sel;
2053
2059
  }
2054
2060
  } catch (err) {
2055
- message = `\u5931\u6557\uFF1A${err.message}`;
2061
+ message2 = `\u5931\u6557\uFF1A${err.message}`;
2056
2062
  }
2057
2063
  process.stdout.write(CLEAR_SCREEN);
2058
2064
  }
@@ -2650,7 +2656,7 @@ function createBridge(t) {
2650
2656
  restoreBuiltinSkill: (payload) => t.invoke("library:restoreBuiltin", payload),
2651
2657
  /** Shipped skills the user deleted, offered for restore. */
2652
2658
  listRemovedBuiltinSkills: () => t.invoke("library:removedBuiltins"),
2653
- approve: (taskId, message) => t.invoke("git:approve", { taskId, message }),
2659
+ approve: (taskId, message2) => t.invoke("git:approve", { taskId, message: message2 }),
2654
2660
  /** Use the task's agent CLI to generate a commit message for branch changes. */
2655
2661
  generateCommitMessage: (taskId) => t.invoke("git:generateCommitMessage", taskId),
2656
2662
  /** Check whether a PR exists for the task's branch. */
@@ -4231,11 +4237,11 @@ async function getWorktreeDiff(worktreePath, baseBranch) {
4231
4237
  }
4232
4238
  return files;
4233
4239
  }
4234
- async function commitAndPush(worktreePath, message) {
4240
+ async function commitAndPush(worktreePath, message2) {
4235
4241
  await git(worktreePath, ["add", "-A"]);
4236
4242
  let committed = false;
4237
4243
  try {
4238
- await git(worktreePath, ["commit", "-m", message]);
4244
+ await git(worktreePath, ["commit", "-m", message2]);
4239
4245
  committed = true;
4240
4246
  } catch {
4241
4247
  committed = false;
@@ -4247,7 +4253,7 @@ async function commitAndPush(worktreePath, message) {
4247
4253
  } catch {
4248
4254
  pushed = false;
4249
4255
  }
4250
- return { committed, pushed, message };
4256
+ return { committed, pushed, message: message2 };
4251
4257
  }
4252
4258
  var MAX_OUTCOME_COMMITS = 50;
4253
4259
  var OUTCOME_LOG_FORMAT = "%h%x00%s";
@@ -4451,8 +4457,8 @@ init_attachments();
4451
4457
  function mastersRoot(storeFile) {
4452
4458
  return path13.join(path13.dirname(storeFile), "masters");
4453
4459
  }
4454
- function taskError(message, code2) {
4455
- return Object.assign(new Error(message), { code: code2 });
4460
+ function taskError(message2, code2) {
4461
+ return Object.assign(new Error(message2), { code: code2 });
4456
4462
  }
4457
4463
  function assertMasterRef(board, masterId, taskId) {
4458
4464
  const master = Object.values(board).flat().find((t) => t.id === masterId);
@@ -4731,8 +4737,8 @@ function createWsTransport(options) {
4731
4737
  function dispatch(channel, payload) {
4732
4738
  for (const cb of Array.from(listeners.get(channel) ?? [])) cb(payload);
4733
4739
  }
4734
- function post(message) {
4735
- const data = JSON.stringify(message);
4740
+ function post(message2) {
4741
+ const data = JSON.stringify(message2);
4736
4742
  if (socket && socket.readyState === OPEN) socket.send(data);
4737
4743
  else queue.push(data);
4738
4744
  }
@@ -4870,13 +4876,13 @@ Examples:
4870
4876
  `.trim();
4871
4877
  var CliFailure = class extends Error {
4872
4878
  code;
4873
- constructor(code2, message) {
4874
- super(message);
4879
+ constructor(code2, message2) {
4880
+ super(message2);
4875
4881
  this.code = code2;
4876
4882
  }
4877
4883
  };
4878
- function fail(code2, message) {
4879
- throw new CliFailure(code2, message);
4884
+ function fail(code2, message2) {
4885
+ throw new CliFailure(code2, message2);
4880
4886
  }
4881
4887
  function requireOneOf(flag, value, allowed) {
4882
4888
  if (value !== void 0 && !allowed.includes(value)) {
@@ -5615,10 +5621,10 @@ function spawnJsonLines(bin, args, opts) {
5615
5621
  failure = err;
5616
5622
  for (const w of waiters.splice(0)) w.reject(err);
5617
5623
  };
5618
- const deliver = (message) => {
5619
- const i = waiters.findIndex((w) => w.match(message));
5620
- if (i === -1) inbox.push(message);
5621
- else waiters.splice(i, 1)[0].resolve(message);
5624
+ const deliver = (message2) => {
5625
+ const i = waiters.findIndex((w) => w.match(message2));
5626
+ if (i === -1) inbox.push(message2);
5627
+ else waiters.splice(i, 1)[0].resolve(message2);
5622
5628
  };
5623
5629
  let buffered = "";
5624
5630
  proc.stdout.setEncoding("utf8");
@@ -5629,13 +5635,13 @@ function spawnJsonLines(bin, args, opts) {
5629
5635
  const line = buffered.slice(0, nl).trim();
5630
5636
  buffered = buffered.slice(nl + 1);
5631
5637
  if (!line) continue;
5632
- let message;
5638
+ let message2;
5633
5639
  try {
5634
- message = JSON.parse(line);
5640
+ message2 = JSON.parse(line);
5635
5641
  } catch {
5636
5642
  continue;
5637
5643
  }
5638
- deliver(message);
5644
+ deliver(message2);
5639
5645
  }
5640
5646
  });
5641
5647
  proc.stderr.setEncoding("utf8");
@@ -5672,8 +5678,8 @@ function spawnJsonLines(bin, args, opts) {
5672
5678
  }, opts.timeoutMs);
5673
5679
  let graceTimer;
5674
5680
  return {
5675
- send(message) {
5676
- if (!failure) proc.stdin.write(`${JSON.stringify(message)}
5681
+ send(message2) {
5682
+ if (!failure) proc.stdin.write(`${JSON.stringify(message2)}
5677
5683
  `);
5678
5684
  },
5679
5685
  next(match) {
@@ -5706,11 +5712,11 @@ function openClaude() {
5706
5712
  function isRecord(value) {
5707
5713
  return typeof value === "object" && value !== null && !Array.isArray(value);
5708
5714
  }
5709
- function isOurResponse(message) {
5710
- return isRecord(message) && message.type === "control_response" && isRecord(message.response) && message.response.request_id === REQUEST_ID;
5715
+ function isOurResponse(message2) {
5716
+ return isRecord(message2) && message2.type === "control_response" && isRecord(message2.response) && message2.response.request_id === REQUEST_ID;
5711
5717
  }
5712
- function parseClaudeInitialize(message) {
5713
- const outer = isRecord(message) && isRecord(message.response) ? message.response : null;
5718
+ function parseClaudeInitialize(message2) {
5719
+ const outer = isRecord(message2) && isRecord(message2.response) ? message2.response : null;
5714
5720
  if (outer?.subtype === "error") throw new Error(`claude refused the handshake: ${String(outer.error ?? "unknown error")}`);
5715
5721
  const body = outer && isRecord(outer.response) ? outer.response : null;
5716
5722
  if (!body || !Array.isArray(body.models)) throw new Error("claude handshake has no models array");
@@ -5771,12 +5777,12 @@ function openCodex() {
5771
5777
  function isRecord2(value) {
5772
5778
  return typeof value === "object" && value !== null && !Array.isArray(value);
5773
5779
  }
5774
- var responseTo = (id) => (message) => isRecord2(message) && message.id === id && ("result" in message || "error" in message);
5775
- function resultOf(message, method) {
5776
- if (!isRecord2(message)) throw new Error(`codex ${method}: malformed response`);
5777
- if (isRecord2(message.error)) throw new Error(`codex ${method}: ${String(message.error.message ?? "request failed")}`);
5778
- if (!isRecord2(message.result)) throw new Error(`codex ${method}: response has no result`);
5779
- return message.result;
5780
+ var responseTo = (id) => (message2) => isRecord2(message2) && message2.id === id && ("result" in message2 || "error" in message2);
5781
+ function resultOf(message2, method) {
5782
+ if (!isRecord2(message2)) throw new Error(`codex ${method}: malformed response`);
5783
+ if (isRecord2(message2.error)) throw new Error(`codex ${method}: ${String(message2.error.message ?? "request failed")}`);
5784
+ if (!isRecord2(message2.result)) throw new Error(`codex ${method}: response has no result`);
5785
+ return message2.result;
5780
5786
  }
5781
5787
  function isCodexSignedIn(result) {
5782
5788
  return result.account != null || result.requiresOpenaiAuth === false;
@@ -5939,53 +5945,175 @@ init_effort();
5939
5945
  init_launch();
5940
5946
  var API_URL = "https://api.typesafe.ai/v1/systemone";
5941
5947
  var MAX_TASK_CHARS = 12e3;
5948
+ var MAX_CONTEXT_CHARS = 8e3;
5949
+ var SCOUT_TIMEOUT_MS = 5 * 6e4;
5950
+ var SAFE_MODEL_ID = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
5951
+ function tier(model, agent) {
5952
+ const name = `${model.id} ${model.label}`.toLowerCase();
5953
+ if (/haiku|mini|nano|luna|flash/.test(name)) return 0;
5954
+ if (/opus|fable|astra|pro\b/.test(name)) return 2;
5955
+ if (agent === "codex" && /gpt-5\.5|gpt-5\.4(?!-mini)/.test(name)) return 2;
5956
+ return 1;
5957
+ }
5958
+ var TIER_LABELS = ["lowest cost", "balanced", "highest capability"];
5959
+ function routeCandidates(agent, models) {
5960
+ const byTier = /* @__PURE__ */ new Map();
5961
+ for (const model of models) {
5962
+ if (!SAFE_MODEL_ID.test(model.id) || model.id === "default") continue;
5963
+ const rank = tier(model, agent);
5964
+ if (!byTier.has(rank)) byTier.set(rank, model);
5965
+ }
5966
+ return [...byTier.entries()].sort(([a], [b]) => a - b).map(([, model]) => model);
5967
+ }
5968
+ function isChoice(value) {
5969
+ return !!value && typeof value === "object" && value.type === "choice" && typeof value.choice === "string";
5970
+ }
5971
+ var defaultScout = (agent, model, effort, prompt, cwd) => {
5972
+ const args = agent === "claude" ? ["-p", "--model", model, "--permission-mode", "plan", ...effort ? ["--effort", effort] : []] : [
5973
+ "exec",
5974
+ "--model",
5975
+ model,
5976
+ ...effort ? ["-c", `model_reasoning_effort=${effort}`] : [],
5977
+ "--sandbox",
5978
+ "read-only",
5979
+ "--skip-git-repo-check",
5980
+ "-"
5981
+ ];
5982
+ return runAgentPrint(agent, args, prompt, { cwd, timeout: SCOUT_TIMEOUT_MS, maxBuffer: 128 * 1024 });
5983
+ };
5942
5984
  function createJevRouter(opts = {}) {
5943
5985
  const getApiKey = opts.getApiKey ?? (() => opts.apiKey ?? process.env.TYPESAFE_API_KEY?.trim());
5944
5986
  const fetcher = opts.fetcher ?? fetch;
5987
+ const scout = opts.scout ?? defaultScout;
5988
+ async function ask(state, questions) {
5989
+ const apiKey = getApiKey();
5990
+ if (!apiKey) throw new Error("\u672A\u8A2D\u5B9A Jev API key\uFF08\u53EF\u5728\u8A2D\u5B9A\u4E2D\u8F38\u5165\u6216\u63D0\u4F9B TYPESAFE_API_KEY\uFF09");
5991
+ const response = await fetcher(API_URL, {
5992
+ method: "POST",
5993
+ headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
5994
+ body: JSON.stringify({ model: "jev-latest", state, questions }),
5995
+ signal: AbortSignal.timeout(15e3)
5996
+ });
5997
+ if (response.status === 402 || response.status === 429) {
5998
+ throw new Error(`Jev \u984D\u5EA6\u5DF2\u7528\u5B8C\u6216\u8ACB\u6C42\u904E\u591A\uFF08HTTP ${response.status}\uFF09\uFF0C\u6539\u7528 CLI \u76EE\u524D\u7684\u6A21\u578B`);
5999
+ }
6000
+ if (!response.ok) throw new Error(`Jev API \u56DE\u61C9 ${response.status}`);
6001
+ const body = await response.json();
6002
+ if (!body.answers) throw new Error("Jev API \u6C92\u6709\u56DE\u50B3\u5224\u65B7");
6003
+ return body.answers;
6004
+ }
6005
+ async function routeEffort(task, catalog) {
6006
+ const model = modelFor(catalog, taskModel(task));
6007
+ const levels = selectableEfforts(model);
6008
+ if (levels.length === 0) return { status: "fallback", reason: "\u76EE\u524D\u6A21\u578B\u4E0D\u652F\u63F4\u8ABF\u6574 effort" };
6009
+ try {
6010
+ const answers = await ask({
6011
+ task: buildPrompt(task).slice(0, MAX_TASK_CHARS),
6012
+ agent: taskAgent(task),
6013
+ currentModel: model?.id ?? taskModel(task) ?? "CLI default (catalog unavailable)"
6014
+ }, { effort: effortQuestion(levels) });
6015
+ const choice = answers.effort;
6016
+ if (!isChoice(choice) || !levels.includes(choice.choice)) {
6017
+ throw new Error("Jev \u9078\u4E86\u76EE\u524D\u6A21\u578B\u4E0D\u652F\u63F4\u7684 effort");
6018
+ }
6019
+ return { status: "routed", effort: choice.choice };
6020
+ } catch (err) {
6021
+ return { status: "fallback", reason: message(err) };
6022
+ }
6023
+ }
6024
+ async function routeModel(task, catalog, ro) {
6025
+ const agent = taskAgent(task);
6026
+ const candidates = routeCandidates(agent, catalog);
6027
+ if (candidates.length === 0) return { status: "fallback", reason: "\u6A21\u578B\u6E05\u55AE\u6C92\u6709\u53EF\u7528\u7684\u6A21\u578B" };
6028
+ const chooseEffort = task.effort === "auto";
6029
+ const strongest = candidates[candidates.length - 1];
6030
+ const levels = MANUAL_EFFORTS.filter((level) => candidates.some((m) => selectableEfforts(m).includes(level)));
6031
+ const taskText = buildPrompt(task).slice(0, MAX_TASK_CHARS);
6032
+ const modelQuestion = {
6033
+ type: "choice",
6034
+ instructions: "Choose the lowest-cost available model likely to implement this software task correctly. Prefer more capable models as complexity, ambiguity or risk rises.",
6035
+ criteria: Object.fromEntries(candidates.map((m) => [m.id, `${m.label}; ${TIER_LABELS[tier(m, agent)]}`]))
6036
+ };
6037
+ const questions = (withUnclear) => ({
6038
+ complexity: {
6039
+ type: "choice",
6040
+ instructions: "How difficult is this software task to implement correctly?",
6041
+ criteria: {
6042
+ simple: "Small, well scoped and routine",
6043
+ moderate: "Some design choices or multiple files",
6044
+ complex: "Architecture, ambiguous requirements, high risk, or many interacting parts",
6045
+ ...withUnclear ? { unclear: "Cannot be judged from the description alone; the repository must be read first" } : {}
6046
+ }
6047
+ },
6048
+ model: modelQuestion,
6049
+ ...chooseEffort && levels.length > 0 ? { effort: effortQuestion(levels) } : {}
6050
+ });
6051
+ const decide = (answers, extra) => {
6052
+ const model = candidates.find((m) => isChoice(answers.model) && m.id === answers.model.choice);
6053
+ if (!model) throw new Error("Jev \u9078\u4E86\u4E0D\u5728\u6A21\u578B\u6E05\u55AE\u4E2D\u7684\u6A21\u578B");
6054
+ let effort;
6055
+ if (chooseEffort && levels.length > 0) {
6056
+ const choice = answers.effort;
6057
+ if (!isChoice(choice) || !levels.includes(choice.choice)) throw new Error("Jev \u56DE\u50B3\u4E0D\u53D7\u652F\u63F4\u7684 effort");
6058
+ effort = clampEffort(choice.choice, selectableEfforts(model));
6059
+ }
6060
+ return { status: "routed", model: model.id, ...effort ? { effort } : {}, ...extra };
6061
+ };
6062
+ const conservative = (extra) => {
6063
+ const effort = chooseEffort ? clampEffort("high", selectableEfforts(strongest)) : void 0;
6064
+ return { model: strongest.id, ...effort ? { effort } : {}, ...extra };
6065
+ };
6066
+ let first;
6067
+ let complexity;
6068
+ try {
6069
+ ro.onPhase?.("judging");
6070
+ first = await ask({ task: taskText, agent }, questions(true));
6071
+ const c = first.complexity;
6072
+ if (!isChoice(c) || !["simple", "moderate", "complex", "unclear"].includes(c.choice)) throw new Error("Jev \u56DE\u50B3\u4E0D\u53D7\u652F\u63F4\u7684\u8907\u96DC\u5EA6");
6073
+ complexity = c.choice;
6074
+ if (complexity !== "unclear") return decide(first, { complexity });
6075
+ } catch (err) {
6076
+ return { status: "fallback", reason: message(err) };
6077
+ }
6078
+ if (ro.scout === false) return conservative({ status: "routed", complexity });
6079
+ let context;
6080
+ try {
6081
+ if (!ro.cwd) throw new Error("\u88DC\u5145 context \u6642\u627E\u4E0D\u5230 worktree");
6082
+ ro.onPhase?.("scouting");
6083
+ const scoutEffort = clampEffort("high", selectableEfforts(strongest));
6084
+ const instruction = "Read the repository to prepare the following software task. Do not edit any file. Report concisely: the affected areas and files, requirements that are unclear, the main risks, and how to verify the work. Return only the analysis.\n\n" + taskText;
6085
+ context = (await scout(agent, strongest.id, scoutEffort, instruction, ro.cwd)).trim().slice(0, MAX_CONTEXT_CHARS);
6086
+ if (!context) throw new Error("\u88DC\u5145 context \u7684\u6A21\u578B\u6C92\u6709\u56DE\u50B3\u5206\u6790");
6087
+ } catch (err) {
6088
+ return conservative({ status: "fallback", complexity, reason: message(err) });
6089
+ }
6090
+ try {
6091
+ ro.onPhase?.("selecting");
6092
+ const second = await ask({ task: taskText, agent, context }, questions(false));
6093
+ return decide(second, { complexity, scoutModel: strongest.id, context });
6094
+ } catch (err) {
6095
+ return { status: "fallback", complexity, reason: message(err), scoutModel: strongest.id, context };
6096
+ }
6097
+ }
5945
6098
  return {
5946
6099
  get enabled() {
5947
6100
  return Boolean(getApiKey());
5948
6101
  },
5949
- async route(task, catalog) {
5950
- const model = modelFor(catalog, taskModel(task));
5951
- const levels = selectableEfforts(model);
5952
- if (levels.length === 0) return { status: "fallback", reason: "\u76EE\u524D\u6A21\u578B\u4E0D\u652F\u63F4\u8ABF\u6574 effort" };
5953
- try {
5954
- const apiKey = getApiKey();
5955
- if (!apiKey) throw new Error("\u672A\u8A2D\u5B9A Jev API key\uFF08\u53EF\u5728\u8A2D\u5B9A\u4E2D\u8F38\u5165\u6216\u63D0\u4F9B TYPESAFE_API_KEY\uFF09");
5956
- const response = await fetcher(API_URL, {
5957
- method: "POST",
5958
- headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
5959
- body: JSON.stringify({
5960
- model: "jev-latest",
5961
- state: {
5962
- task: buildPrompt(task).slice(0, MAX_TASK_CHARS),
5963
- agent: task.agentCli === "codex" ? "codex" : "claude",
5964
- currentModel: model?.id ?? taskModel(task) ?? "CLI default (catalog unavailable)"
5965
- },
5966
- questions: {
5967
- effort: {
5968
- type: "choice",
5969
- instructions: "Choose the lowest reasoning effort likely to complete this software task correctly on the current model. Increase effort for complexity, ambiguity, or risk.",
5970
- criteria: Object.fromEntries(levels.map((level) => [level, effortDescription(level)]))
5971
- }
5972
- }
5973
- }),
5974
- signal: AbortSignal.timeout(15e3)
5975
- });
5976
- if (!response.ok) throw new Error(`Jev API \u56DE\u61C9 ${response.status}`);
5977
- const body = await response.json();
5978
- const choice = body.answers?.effort;
5979
- if (choice?.type !== "choice" || !levels.includes(choice.choice)) {
5980
- throw new Error("Jev \u9078\u4E86\u76EE\u524D\u6A21\u578B\u4E0D\u652F\u63F4\u7684 effort");
5981
- }
5982
- return { status: "routed", effort: choice.choice };
5983
- } catch (err) {
5984
- return { status: "fallback", reason: err instanceof Error ? err.message : String(err) };
5985
- }
6102
+ route(task, catalog, ro = {}) {
6103
+ return taskModel(task) ? routeEffort(task, catalog) : routeModel(task, catalog, ro);
5986
6104
  }
5987
6105
  };
5988
6106
  }
6107
+ function effortQuestion(levels) {
6108
+ return {
6109
+ type: "choice",
6110
+ instructions: "Choose the lowest reasoning effort likely to complete this software task correctly on the chosen model. Increase effort for complexity, ambiguity, or risk.",
6111
+ criteria: Object.fromEntries(levels.map((level) => [level, effortDescription(level)]))
6112
+ };
6113
+ }
6114
+ function message(err) {
6115
+ return err instanceof Error ? err.message : String(err);
6116
+ }
5989
6117
  function effortDescription(level) {
5990
6118
  const descriptions = {
5991
6119
  low: "Routine, small and well scoped work",
@@ -6114,11 +6242,11 @@ async function getGitHubCliAuthStatus() {
6114
6242
  } catch {
6115
6243
  }
6116
6244
  }
6117
- const message = errorMessage2(err);
6245
+ const message2 = errorMessage2(err);
6118
6246
  return {
6119
- installed: !/ENOENT|not found/i.test(message),
6247
+ installed: !/ENOENT|not found/i.test(message2),
6120
6248
  authenticated: false,
6121
- error: message
6249
+ error: message2
6122
6250
  };
6123
6251
  }
6124
6252
  }
@@ -6704,13 +6832,13 @@ function setConversation(taskId, conversation) {
6704
6832
  function loadConversation(taskId) {
6705
6833
  return getChatStore().get("conversations")[taskId] ?? null;
6706
6834
  }
6707
- function appendMessage(taskId, message) {
6835
+ function appendMessage(taskId, message2) {
6708
6836
  const existing = loadConversation(taskId) ?? {
6709
6837
  taskId,
6710
6838
  messages: [],
6711
6839
  updatedAt: 0
6712
6840
  };
6713
- existing.messages.push(message);
6841
+ existing.messages.push(message2);
6714
6842
  existing.updatedAt = Date.now();
6715
6843
  setConversation(taskId, existing);
6716
6844
  }
@@ -7265,8 +7393,8 @@ var PASTE_END = "\x1B[201~";
7265
7393
  var PASTE_GAP_MS = 200;
7266
7394
  var DEFAULT_QUIET_MS = 3e3;
7267
7395
  var DEFAULT_TIMEOUT_MS = 12e4;
7268
- function coded(code2, message) {
7269
- return Object.assign(new Error(message), { code: code2 });
7396
+ function coded(code2, message2) {
7397
+ return Object.assign(new Error(message2), { code: code2 });
7270
7398
  }
7271
7399
  function isBlocked(activity) {
7272
7400
  return activity?.state === "waiting" && activity.waitingFor === "permission";
@@ -7451,11 +7579,11 @@ var InvalidRequestError = class extends Error {
7451
7579
  };
7452
7580
  var STANDALONE_KEY = /^terminal_[0-9a-f-]{36}$/;
7453
7581
  var MAX_MESSAGE_LENGTH = 2e4;
7454
- function coded2(code2, message) {
7455
- return Object.assign(new Error(message), { code: code2 });
7582
+ function coded2(code2, message2) {
7583
+ return Object.assign(new Error(message2), { code: code2 });
7456
7584
  }
7457
- function invalid(message) {
7458
- throw new InvalidRequestError(message);
7585
+ function invalid(message2) {
7586
+ throw new InvalidRequestError(message2);
7459
7587
  }
7460
7588
  function str(value, name) {
7461
7589
  if (typeof value !== "string") invalid(`${name} must be a string`);
@@ -7636,16 +7764,23 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7636
7764
  const task = requireTask(key.split(":")[0]);
7637
7765
  return str(task.worktreePath ?? task.projectPath, "directory");
7638
7766
  }
7767
+ function launchModel(task, route) {
7768
+ return taskModel(task) ?? route?.model;
7769
+ }
7639
7770
  function launchEffort(task, route) {
7640
7771
  const effort = task.effort === "auto" ? route?.effort : task.effort;
7641
- const model = models.findModel(taskAgent(task), taskModel(task));
7772
+ const model = models.findModel(taskAgent(task), launchModel(task, route));
7642
7773
  return model?.efforts ? clampEffort(effort, model.efforts) : effort;
7643
7774
  }
7775
+ function settledRoute(task) {
7776
+ return task.jevRoute?.status === "routing" ? void 0 : task.jevRoute;
7777
+ }
7644
7778
  async function launchCommand(task, intent) {
7645
7779
  const settings = getSettings();
7646
7780
  const boardCli = await boardCliLaunchInfo();
7647
- let route = task.jevRoute?.status === "routing" ? void 0 : task.jevRoute;
7648
- if (task.effort === "auto" && !intent.resume && intent.includeTaskPrompt !== false) {
7781
+ let route = settledRoute(task);
7782
+ const needsRoute = !taskModel(task) || task.effort === "auto";
7783
+ if (needsRoute && !intent.resume && intent.includeTaskPrompt !== false) {
7649
7784
  let pending = routing.get(task.id);
7650
7785
  if (!pending) {
7651
7786
  const setRoute = (jevRoute) => {
@@ -7655,7 +7790,12 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7655
7790
  pending = (async () => {
7656
7791
  setRoute({ status: "routing" });
7657
7792
  const candidateModels = (await (router.enabled === false ? models.get(taskAgent(task)) : models.refresh(taskAgent(task)))).models;
7658
- const selected = await router.route(task, candidateModels);
7793
+ const selected = await router.route(task, candidateModels, {
7794
+ // A master has no repo to read, only its notes folder.
7795
+ scout: !isMasterTask(task),
7796
+ cwd: task.worktreePath,
7797
+ onPhase: (phase) => setRoute({ status: "routing", phase })
7798
+ });
7659
7799
  setRoute(selected);
7660
7800
  return selected;
7661
7801
  })().catch((err) => {
@@ -7666,14 +7806,16 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7666
7806
  }
7667
7807
  route = await pending;
7668
7808
  }
7809
+ const preface = route?.context && route.scoutModel ? { model: route.scoutModel, context: route.context } : void 0;
7669
7810
  return buildAgentCommand(
7670
- { ...task, model: taskModel(task), effort: launchEffort(task, route) },
7811
+ { ...task, model: launchModel(task, route), effort: launchEffort(task, route) },
7671
7812
  settings.systemPrompt ?? "",
7672
7813
  {
7673
7814
  resume: intent.resume === true,
7674
7815
  includeTaskPrompt: intent.includeTaskPrompt !== false,
7675
7816
  boardCli,
7676
- autoMode: task.autoMode ?? settings.autoMode
7817
+ autoMode: task.autoMode ?? settings.autoMode,
7818
+ preface
7677
7819
  },
7678
7820
  task.workspacePath
7679
7821
  );
@@ -7782,6 +7924,15 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7782
7924
  const task = findTask(taskId);
7783
7925
  if (task && sessions.isAttached?.(taskId) === false) await startTaskSession(task, taskId, { taskId });
7784
7926
  }
7927
+ function launchChoice(task) {
7928
+ const route = settledRoute(task);
7929
+ const model = launchModel(task, route);
7930
+ const effort = launchEffort(task, route);
7931
+ return {
7932
+ ...model ? { model } : {},
7933
+ ...effort && effort !== "auto" ? { effort } : {}
7934
+ };
7935
+ }
7785
7936
  async function summarizeTasks(filter) {
7786
7937
  const board = getState().board;
7787
7938
  const alive = new Set(await sessions.list());
@@ -7803,6 +7954,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
7803
7954
  ...task.masterId ? { masterId: task.masterId } : {},
7804
7955
  ...task.projectName ? { projectName: task.projectName } : {},
7805
7956
  ...task.branch ? { branch: task.branch } : {},
7957
+ ...launchChoice(task),
7806
7958
  running: alive.has(task.id),
7807
7959
  ...task.launchedAt ? { launchedAt: task.launchedAt } : {},
7808
7960
  ...task.launchError ? { launchError: task.launchError } : {},
@@ -8192,8 +8344,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
8192
8344
  const file = await readTerminalFile(terminalDirectory(p.sessionKey), str(p.path, "path"), true);
8193
8345
  if (!file.ok) return { ok: false, message: file.message };
8194
8346
  try {
8195
- const message = await getPlatform().openPath(file.path);
8196
- return message ? { ok: false, message } : { ok: true };
8347
+ const message2 = await getPlatform().openPath(file.path);
8348
+ return message2 ? { ok: false, message: message2 } : { ok: true };
8197
8349
  } catch (error) {
8198
8350
  return { ok: false, message: error instanceof Error ? error.message : "\u7121\u6CD5\u958B\u555F\u6A94\u6848\u3002" };
8199
8351
  }