@kw-yeh/vibeflow 4.7.1 → 4.9.0

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
@@ -499,6 +499,192 @@ var init_tmux_backend = __esm({
499
499
  }
500
500
  });
501
501
 
502
+ // packages/core/src/launch.ts
503
+ function pathBasename(p) {
504
+ const norm = p.replace(/\\/g, "/").replace(/\/+$/, "");
505
+ const i = norm.lastIndexOf("/");
506
+ return i >= 0 ? norm.slice(i + 1) : norm;
507
+ }
508
+ function agentArtifactsDir(worktreePath, workspacePath) {
509
+ if (!worktreePath || !workspacePath) return null;
510
+ const dir = toShellPath(workspacePath);
511
+ const ws = pathBasename(worktreePath);
512
+ return `${dir}/${ws}${ARTIFACTS_DIR_SUFFIX}`;
513
+ }
514
+ function buildArtifactPrompt(artifactsDir = ARTIFACTS_FALLBACK_DIR) {
515
+ return [
516
+ "Artifact \u8A2D\u5B9A\uFF08\u672C\u6B21 session \u5168\u7A0B\u9069\u7528\uFF09\uFF1A",
517
+ `1. \u672C\u4EFB\u52D9\u7684 Artifact \u8CC7\u6599\u593E\u662F ${artifactsDir}/\uFF1B\u9700\u8981\u4FDD\u5B58\u7D66\u4F7F\u7528\u8005\u7684\u9A57\u8B49\u8B49\u64DA\u6216\u5831\u544A\u6642\uFF0C\u8ACB\u5BEB\u5165\u6B64\u8CC7\u6599\u593E\uFF08\u4E0D\u5B58\u5728\u8ACB\u5EFA\u7ACB\uFF09\u3002`,
518
+ `2. \u4F7F\u7528\u8005\u6703\u5728 VibeFlow \u7684 Artifacts \u5206\u9801\u6AA2\u8996\u6839\u76EE\u9304\u5167\u5BB9\uFF1B\u53EA\u653E\u4F60\u6703\u4E3B\u52D5\u8ACB\u4F7F\u7528\u8005\u904E\u76EE\u7684\u6A94\u6848\u3002`,
519
+ `3. \u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u66AB\u5B58\uFF08\u4E00\u6B21\u6027 script\u3001log\u3001\u4E2D\u9593\u8F38\u51FA\u3001debug \u6A94\uFF09\u653E\u5728 ${artifactsDir}/${SCRATCH_DIR_NAME}/\u3002`,
520
+ `4. \u82E5\u5DE5\u5177\u5148\u8F38\u51FA\u5230 /tmp\u3001/private/tmp\u3001$TMPDIR \u6216 ~/Downloads\uFF0C\u8ACB\u628A\u6700\u7D42\u8981\u5448\u73FE\u7684\u6A94\u6848\u8907\u88FD\u5230 ${artifactsDir}/ \u4E26\u78BA\u8A8D\u5B58\u5728\u3002`,
521
+ "5. Artifact \u8CC7\u6599\u593E\u4F4D\u65BC worktree \u4E4B\u5916\uFF1B\u4E0D\u8981\u628A\u5176\u4E2D\u5167\u5BB9\u52A0\u5165 git commit\uFF0C\u4E5F\u4E0D\u8981\u628A\u6700\u7D42\u4EA4\u4ED8\u7269\u53EA\u7559\u5728\u9019\u88E1\u3002"
522
+ ].join("\n");
523
+ }
524
+ function claudePermissionFlag(autoMode) {
525
+ return autoMode ? ` --permission-mode ${AUTO_PERMISSION_MODE}` : "";
526
+ }
527
+ function shellQuote(s) {
528
+ return `'${s.replace(/'/g, `'\\''`)}'`;
529
+ }
530
+ function toShellPath(p) {
531
+ return p.replace(/\\/g, "/");
532
+ }
533
+ function boardEnvPrefix(task, opts) {
534
+ const board = opts?.boardCli;
535
+ if (!board) return "";
536
+ const entries = [
537
+ ["VIBEFLOW_TASK_ID", task.id],
538
+ ["VIBEFLOW_PROJECT_PATH", task.projectPath],
539
+ ["VIBEFLOW_BRANCH", task.branch],
540
+ ["VIBEFLOW_BASE_BRANCH", task.baseBranch],
541
+ ["VIBEFLOW_STORE_DIR", board?.storeDir],
542
+ ["VIBEFLOW_CLI", board?.cliPath],
543
+ ["VIBEFLOW_CLI_LOADER", board?.loaderPath],
544
+ ["VIBEFLOW_AUTO_MODE", opts?.autoMode ? "1" : "0"]
545
+ ];
546
+ const exports = entries.filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${shellQuote(toShellPath(value))}`);
547
+ return exports.length ? `export ${exports.join(" ")}; ` : "";
548
+ }
549
+ function codexAutoFlag(autoMode) {
550
+ return autoMode ? "--dangerously-bypass-approvals-and-sandbox " : "";
551
+ }
552
+ function claudeResumeOrFresh(sessionId, worktreePath, tail) {
553
+ if (!worktreePath) return `claude --resume ${sessionId} ${tail}\r`;
554
+ const munged = worktreePath.replace(/[^a-zA-Z0-9]/g, "-");
555
+ const sessionFile = `"$HOME/.claude/projects/${munged}/${sessionId}.jsonl"`;
556
+ return `if [ -f ${sessionFile} ]; then claude --resume ${sessionId} ${tail}; else claude --session-id ${sessionId} ${tail}; fi\r`;
557
+ }
558
+ function buildClaudeSettings(worktreePath) {
559
+ const settings = { theme: "dark" };
560
+ if (worktreePath) {
561
+ const dir = `${toShellPath(worktreePath)}/${SUBAGENTS_DIR2}`;
562
+ const command = `mkdir -p "${dir}" && cat > "${dir}/$(date +%s)-$$-$RANDOM.json"`;
563
+ const taskHook = {
564
+ matcher: "Task",
565
+ hooks: [{ type: "command", command }]
566
+ };
567
+ const eventsDir = `${toShellPath(worktreePath)}/${PROGRESS_EVENTS_DIR}`;
568
+ const progress = {
569
+ hooks: [{ type: "command", command: `mkdir -p "${eventsDir}" && cat > "${eventsDir}/$(date +%s)-$$-$RANDOM.json"; exit 0` }]
570
+ };
571
+ settings.hooks = {
572
+ PreToolUse: [taskHook],
573
+ PostToolUse: [taskHook, { matcher: TODO_TOOLS, ...progress }],
574
+ Stop: [progress],
575
+ Notification: [progress],
576
+ UserPromptSubmit: [progress]
577
+ };
578
+ }
579
+ return JSON.stringify(settings);
580
+ }
581
+ function resolveSystemPrompt(custom) {
582
+ return custom && custom.trim() ? custom : "";
583
+ }
584
+ function buildPrompt(task) {
585
+ const lines = [`\u4EFB\u52D9\u6A19\u984C\uFF1A${task.title}`];
586
+ const description = task.description?.trim();
587
+ if (description) {
588
+ lines.push("", "\u4EFB\u52D9\u63CF\u8FF0\uFF1A", description);
589
+ }
590
+ return lines.join("\n");
591
+ }
592
+ function executorSessionId(taskId, runId) {
593
+ const source = runId ? `${taskId}:${runId}` : taskId;
594
+ const raw = source.replace(/[^0-9a-f]/gi, "").toLowerCase();
595
+ const hex = runId ? `${raw.slice(0, 24).padEnd(24, "0")}${namespaceHash(`executor:${source}`)}` : raw.padEnd(32, "0").slice(0, 32);
596
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-8${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
597
+ }
598
+ function namespaceHash(namespace) {
599
+ let hash = 2166136261;
600
+ for (let i = 0; i < namespace.length; i += 1) {
601
+ hash ^= namespace.charCodeAt(i);
602
+ hash = Math.imul(hash, 16777619);
603
+ }
604
+ return (hash >>> 0).toString(16).padStart(8, "0");
605
+ }
606
+ function assembleCommand(agent, systemPrompt, prompt, model, effort, opts, worktreePath, sessionId, workspacePath) {
607
+ let cmd;
608
+ if (agent === "claude") {
609
+ const settings = ` --settings ${shellQuote(buildClaudeSettings(worktreePath))}`;
610
+ const addDir = workspacePath ? ` --add-dir ${shellQuote(toShellPath(workspacePath))}` : "";
611
+ const modelFlag = model ? ` --model ${model}` : "";
612
+ const effortFlag = effort ? ` --effort ${effort}` : "";
613
+ const library = opts?.library;
614
+ const libraryFlags = library ? ` --plugin-dir ${shellQuote(toShellPath(library.pluginDir))} --add-dir ${shellQuote(toShellPath(library.libraryDir))}` : "";
615
+ const flags = `--chrome${claudePermissionFlag(opts?.autoMode)}${modelFlag}${effortFlag}${settings}${addDir}${libraryFlags}`;
616
+ const sysFlag = systemPrompt ? ` --append-system-prompt ${shellQuote(systemPrompt)}` : "";
617
+ const promptArg = prompt ? ` ${shellQuote(prompt)}` : "";
618
+ const tail = `${flags}${sysFlag}${promptArg}`;
619
+ cmd = sessionId && opts?.resume ? claudeResumeOrFresh(sessionId, worktreePath, tail) : `claude ${sessionId ? `--session-id ${sessionId} ` : opts?.resume ? "--continue " : ""}${tail}\r`;
620
+ } else {
621
+ const combined = [systemPrompt, prompt].filter(Boolean).join("\n\n");
622
+ const codexEffortFlag = effort ? `-c ${shellQuote(`model_reasoning_effort="${effort}"`)} ` : "";
623
+ const codexHome = opts?.library ? `CODEX_HOME=${shellQuote(toShellPath(opts.library.codexHome))} ` : "";
624
+ const args = [
625
+ codexAutoFlag(opts?.autoMode).trim(),
626
+ codexEffortFlag.trim(),
627
+ model ? `--model ${model}` : "",
628
+ combined ? shellQuote(combined) : ""
629
+ ].filter(Boolean);
630
+ cmd = `${codexHome}${["codex", ...args].join(" ")}\r`;
631
+ }
632
+ if (cmd.length > 2e5) console.warn(`[VibeFlow] launch command is ${cmd.length} bytes \u2014 approaching ARG_MAX`);
633
+ return cmd;
634
+ }
635
+ function taskAgent(task) {
636
+ const agent = task.agentCli;
637
+ return agent && agent in AGENT_NAMES ? agent : "claude";
638
+ }
639
+ function taskModel(task) {
640
+ const model = task.model?.trim();
641
+ if (!model || RETIRED_MODELS[taskAgent(task)]?.includes(model)) return void 0;
642
+ return model;
643
+ }
644
+ function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
645
+ const agent = taskAgent(task);
646
+ const model = taskModel(task);
647
+ const artifactsDir = agentArtifactsDir(task.worktreePath, workspacePath) ?? ARTIFACTS_FALLBACK_DIR;
648
+ const builtInPrompt = buildArtifactPrompt(artifactsDir);
649
+ const libraryPrompt = opts?.library?.promptText?.trim();
650
+ const customPrompt = resolveSystemPrompt(systemPrompt);
651
+ const sys = [builtInPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
652
+ const includeTaskPrompt = opts?.includeTaskPrompt !== false;
653
+ const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
654
+ const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
655
+ return boardEnvPrefix(task, opts) + assembleCommand(
656
+ agent,
657
+ sys,
658
+ prompt,
659
+ model,
660
+ task.effort,
661
+ opts,
662
+ task.worktreePath,
663
+ sessionId,
664
+ workspacePath
665
+ );
666
+ }
667
+ var ARTIFACTS_DIR_SUFFIX, SCRATCH_DIR_NAME, ARTIFACTS_FALLBACK_DIR, AUTO_PERMISSION_MODE, SUBAGENTS_DIR2, PROGRESS_EVENTS_DIR, TODO_TOOLS, AGENT_NAMES, RETIRED_MODELS;
668
+ var init_launch = __esm({
669
+ "packages/core/src/launch.ts"() {
670
+ "use strict";
671
+ ARTIFACTS_DIR_SUFFIX = ".artifacts";
672
+ SCRATCH_DIR_NAME = "scratch";
673
+ ARTIFACTS_FALLBACK_DIR = ".vibeflow-artifacts";
674
+ AUTO_PERMISSION_MODE = "auto";
675
+ SUBAGENTS_DIR2 = ".vibeflow-subagents";
676
+ PROGRESS_EVENTS_DIR = ".vibeflow-events";
677
+ TODO_TOOLS = "TodoWrite|TaskCreate|TaskUpdate";
678
+ AGENT_NAMES = {
679
+ claude: "Claude Code",
680
+ codex: "Codex CLI"
681
+ };
682
+ RETIRED_MODELS = {
683
+ codex: ["gpt-5-codex", "gpt-5"]
684
+ };
685
+ }
686
+ });
687
+
502
688
  // packages/core/src/attachments.ts
503
689
  import { randomUUID } from "crypto";
504
690
  import { execFileSync as execFileSync3 } from "child_process";
@@ -599,6 +785,36 @@ var init_attachments = __esm({
599
785
  }
600
786
  });
601
787
 
788
+ // packages/core/src/effort.ts
789
+ function modelFor(models, modelId) {
790
+ return modelId ? models?.find((m) => m.id === modelId) : models?.find((m) => m.isDefault);
791
+ }
792
+ function isAgentEffort(value) {
793
+ return typeof value === "string" && AGENT_EFFORTS.includes(value);
794
+ }
795
+ function allowedEfforts(model) {
796
+ return model?.efforts ?? null;
797
+ }
798
+ function selectableEfforts(model) {
799
+ return allowedEfforts(model) ?? UNKNOWN_MODEL_EFFORTS;
800
+ }
801
+ function clampEffort(effort, allowed) {
802
+ if (allowed.length === 0) return void 0;
803
+ if (effort === void 0 || allowed.includes(effort)) return effort;
804
+ const rank = AGENT_EFFORTS.indexOf(effort);
805
+ const ordered = AGENT_EFFORTS.filter((level) => allowed.includes(level));
806
+ const notDeeper = ordered.filter((level) => AGENT_EFFORTS.indexOf(level) <= rank);
807
+ return notDeeper.length > 0 ? notDeeper[notDeeper.length - 1] : ordered[0];
808
+ }
809
+ var AGENT_EFFORTS, UNKNOWN_MODEL_EFFORTS;
810
+ var init_effort = __esm({
811
+ "packages/core/src/effort.ts"() {
812
+ "use strict";
813
+ AGENT_EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra"];
814
+ UNKNOWN_MODEL_EFFORTS = ["low", "medium", "high", "xhigh"];
815
+ }
816
+ });
817
+
602
818
  // packages/core/src/progress.ts
603
819
  function emptyUsage() {
604
820
  return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0 };
@@ -883,9 +1099,9 @@ var init_progress = __esm({
883
1099
 
884
1100
  // packages/tui/src/task-form.ts
885
1101
  import { spawnSync } from "child_process";
886
- import fs21 from "fs";
887
- import os7 from "os";
888
- import path25 from "path";
1102
+ import fs22 from "fs";
1103
+ import os9 from "os";
1104
+ import path26 from "path";
889
1105
  import React, { useEffect, useRef, useState } from "react";
890
1106
  import { Box, Text, render, useApp, useInput } from "ink";
891
1107
  function newFormState(defaultAutoMode, agents) {
@@ -951,11 +1167,18 @@ function visibleFields(s, ctx) {
951
1167
  return fields;
952
1168
  }
953
1169
  function modelOptions(ctx, agentCli, current) {
954
- const listed = ctx.models[agentCli] ?? [];
1170
+ const listed = (ctx.models[agentCli] ?? []).map((m) => m.id);
955
1171
  const options = ["", ...listed];
956
1172
  if (current && !listed.includes(current)) options.push(current);
957
1173
  return options;
958
1174
  }
1175
+ function effortOptions(ctx, agentCli, model) {
1176
+ return selectableEfforts(modelFor(ctx.models[agentCli], taskModel({ agentCli, model })));
1177
+ }
1178
+ function fitEffort(ctx, s) {
1179
+ const effort = clampEffort(s.effort, effortOptions(ctx, s.agentCli, s.model)) ?? s.effort;
1180
+ return effort === s.effort ? s : { ...s, effort };
1181
+ }
959
1182
  function blockReason(s, ctx, loadingGit) {
960
1183
  if (!s.title.trim()) return "\u8ACB\u8F38\u5165\u4EFB\u52D9\u6A19\u984C";
961
1184
  if (loadingGit) return "\u5075\u6E2C Git \u72C0\u614B\u4E2D\u2026";
@@ -1004,17 +1227,17 @@ function editorCommand(env, platform) {
1004
1227
  return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
1005
1228
  }
1006
1229
  function editInEditor(text) {
1007
- const dir = fs21.mkdtempSync(path25.join(os7.tmpdir(), "vibeflow-"));
1008
- const file = path25.join(dir, "description.md");
1230
+ const dir = fs22.mkdtempSync(path26.join(os9.tmpdir(), "vibeflow-"));
1231
+ const file = path26.join(dir, "description.md");
1009
1232
  try {
1010
- fs21.writeFileSync(file, text);
1233
+ fs22.writeFileSync(file, text);
1011
1234
  const cmd = editorCommand(process.env, process.platform);
1012
1235
  const r = spawnSync(`${cmd} "${file}"`, { stdio: "inherit", shell: true });
1013
1236
  if (r.error) return { error: `\u7121\u6CD5\u555F\u52D5\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\uFF1A${r.error.message}` };
1014
1237
  if (r.status !== 0) return { error: `\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\u7D50\u675F\u78BC ${r.status}\uFF0C\u63CF\u8FF0\u672A\u8B8A\u66F4` };
1015
- return { text: fs21.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
1238
+ return { text: fs22.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
1016
1239
  } finally {
1017
- fs21.rmSync(dir, { recursive: true, force: true });
1240
+ fs22.rmSync(dir, { recursive: true, force: true });
1018
1241
  }
1019
1242
  }
1020
1243
  function cycle(list, current, step) {
@@ -1085,10 +1308,10 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1085
1308
  if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
1086
1309
  else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
1087
1310
  else if (editing === "project") {
1088
- if (value2) setProject(path25.resolve(value2.replace(/^~(?=$|\/)/, os7.homedir())));
1311
+ if (value2) setProject(path26.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir())));
1089
1312
  } else if (editing === "attachments" && value2) {
1090
- const file = path25.resolve(value2.replace(/^~(?=$|\/)/, os7.homedir()));
1091
- if (!fs21.existsSync(file) || !fs21.statSync(file).isFile()) {
1313
+ const file = path26.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir()));
1314
+ if (!fs22.existsSync(file) || !fs22.statSync(file).isFile()) {
1092
1315
  setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
1093
1316
  return;
1094
1317
  }
@@ -1127,13 +1350,16 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1127
1350
  } else if (field === "baseBranch") {
1128
1351
  setS((prev) => ({ ...prev, baseBranch: cycle(prev.gitInfo?.branches ?? [], prev.baseBranch, step) }));
1129
1352
  } else if (field === "effort") {
1130
- setS((prev) => ({ ...prev, effort: cycle(EFFORTS.map((e) => e.value), prev.effort, step) }));
1353
+ setS((prev) => {
1354
+ const levels = effortOptions(ctx, prev.agentCli, prev.model);
1355
+ return levels.length ? { ...prev, effort: cycle([...levels], prev.effort, step) } : prev;
1356
+ });
1131
1357
  } else if (field === "autoMode") {
1132
1358
  setS((prev) => ({ ...prev, autoMode: !prev.autoMode }));
1133
1359
  } else if (field === "agent" && ctx.agents?.length) {
1134
- setS((prev) => ({ ...prev, agentCli: cycle(ctx.agents.map((a) => a.id), prev.agentCli, step), model: "" }));
1360
+ setS((prev) => fitEffort(ctx, { ...prev, agentCli: cycle(ctx.agents.map((a) => a.id), prev.agentCli, step), model: "" }));
1135
1361
  } else if (field === "model") {
1136
- setS((prev) => ({ ...prev, model: cycle(modelOptions(ctx, prev.agentCli, prev.model), prev.model, step) }));
1362
+ setS((prev) => fitEffort(ctx, { ...prev, model: cycle(modelOptions(ctx, prev.agentCli, prev.model), prev.model, step) }));
1137
1363
  }
1138
1364
  return;
1139
1365
  }
@@ -1168,14 +1394,15 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1168
1394
  case "branch":
1169
1395
  return s.branch || "\uFF08\u7559\u7A7A\u5247\u81EA\u52D5\u547D\u540D\uFF0C\u4F8B\u5982 feature/login-fix\uFF09";
1170
1396
  case "attachments":
1171
- return s.attachments.length ? s.attachments.map((a) => path25.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
1397
+ return s.attachments.length ? s.attachments.map((a) => path26.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
1172
1398
  case "description": {
1173
1399
  if (!s.description) return "\uFF08Enter \u7528\u7DE8\u8F2F\u5668\u64B0\u5BEB\uFF09";
1174
1400
  const lines = s.description.split("\n");
1175
1401
  return lines.length > 1 ? `${lines[0]} \u2026\uFF08\u5171 ${lines.length} \u884C\uFF09` : lines[0];
1176
1402
  }
1177
1403
  case "effort": {
1178
- const e = EFFORTS.find((x) => x.value === s.effort);
1404
+ if (effortOptions(ctx, s.agentCli, s.model).length === 0) return "\u4E0D\u9069\u7528";
1405
+ const e = EFFORTS.find((x) => x.value === s.effort) ?? EFFORTS[0];
1179
1406
  return `\u2039 ${e.label} \xB7 ${e.value} \u203A`;
1180
1407
  }
1181
1408
  case "autoMode":
@@ -1185,8 +1412,12 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1185
1412
  if (ctx.agents.length === 0) return "\u672A\u5075\u6E2C\u5230 Agent CLI\uFF08claude / codex\uFF09";
1186
1413
  return `\u2039 ${ctx.agents.find((a) => a.id === s.agentCli)?.name ?? s.agentCli} \u203A`;
1187
1414
  }
1188
- case "model":
1189
- return modelOptions(ctx, s.agentCli, s.model).length > 1 ? `\u2039 ${s.model || "\u4F7F\u7528\u9810\u8A2D model"} \u203A` : "\u7121\u6CD5\u53D6\u5F97 model list\uFF0C\u5C07\u4F7F\u7528\u9810\u8A2D model";
1415
+ case "model": {
1416
+ if (modelOptions(ctx, s.agentCli, s.model).length <= 1) return "\u7121\u6CD5\u53D6\u5F97 model list\uFF0C\u5C07\u4F7F\u7528\u9810\u8A2D model";
1417
+ const listed = ctx.models[s.agentCli]?.find((m) => m.id === s.model);
1418
+ const fallback = ctx.models[s.agentCli]?.find((m) => m.isDefault)?.label;
1419
+ return `\u2039 ${s.model ? listed?.label ?? s.model : fallback ? `\u4F7F\u7528\u9810\u8A2D model\uFF08${fallback}\uFF09` : "\u4F7F\u7528\u9810\u8A2D model"} \u203A`;
1420
+ }
1190
1421
  case "submit":
1191
1422
  return busy ?? (s.mode === "new" ? "\uFF3B \u5EFA\u7ACB\u4EFB\u52D9 \uFF3D" : "\uFF3B \u5132\u5B58\u8B8A\u66F4 \uFF3D");
1192
1423
  }
@@ -1199,13 +1430,17 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1199
1430
  if (info && !info.isRepo) return "\u9019\u500B\u8CC7\u6599\u593E\u4E0D\u662F Git repository\u3002\u8ACB\u6539\u9078\u4E00\u500B Git \u5C08\u6848\uFF0C\u6216\u5728\u4E0B\u65B9\u521D\u59CB\u5316\u3002";
1200
1431
  if (s.mode === "edit" && projectChanged(s)) return "\u5206\u652F\u6703\u5728\u65B0\u5C08\u6848\u3001\u5361\u7247\u958B\u59CB\u57F7\u884C\u6642\u624D\u5EFA\u7ACB\u3002";
1201
1432
  if (info?.isRepo && !info.hasRemote) return `\u6B64 repository \u6C92\u6709 remote\uFF0C\u5C07\u4EE5\u76EE\u524D\u5206\u652F (${info.currentBranch ?? "HEAD"}) \u70BA\u57FA\u6E96\u5EFA\u7ACB\u672C\u5730 worktree\u3002`;
1202
- if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path25.basename(s.projectPath)}`;
1433
+ if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path26.basename(s.projectPath)}`;
1203
1434
  }
1204
1435
  if (field === "branch") {
1205
1436
  const when = info?.hasRemote ? "\u958B\u59CB\u57F7\u884C\u6642\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u4E26\u63A8\u5230 origin\uFF1B\u82E5 remote \u5DF2\u6709\u9019\u500B\u5206\u652F\uFF0C\u5247\u76F4\u63A5\u53D6\u56DE\u4E26\u63A5\u7E8C\u4E0A\u9762\u7684\u5DE5\u4F5C\u3002" : "\u958B\u59CB\u57F7\u884C\u6642\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u3002";
1206
1437
  return s.mode === "edit" && s.worktreePath ? `${when} \u9019\u5F35\u5361\u5DF2\u6709 worktree\uFF1A\u66F4\u6539\u5206\u652F\u3001\u5C08\u6848\u6216\u57FA\u6E96\u5206\u652F\u6703\u79FB\u9664\u5B83\uFF08\u6709\u672A\u5B8C\u6210\u8B8A\u66F4\u6642\u7121\u6CD5\u5132\u5B58\uFF09\u3002` : when;
1207
1438
  }
1208
- if (field === "effort") return `${EFFORTS.find((x) => x.value === s.effort).description} Claude Code \u8207 Codex \u6703\u5728\u555F\u52D5\u6B64\u4EFB\u52D9\u6642\u5957\u7528\uFF1B\u5BE6\u969B\u53EF\u7528\u7D1A\u8DDD\u4F9D model \u800C\u5B9A\u3002`;
1439
+ if (field === "effort") {
1440
+ if (effortOptions(ctx, s.agentCli, s.model).length === 0) return "\u9019\u500B model \u4E0D\u652F\u63F4\u8ABF\u6574\u63A8\u7406\u5F37\u5EA6\uFF0C\u555F\u52D5\u6642\u4E0D\u6703\u6307\u5B9A\u4EFB\u52D9\u8907\u96DC\u5EA6\u3002";
1441
+ const e = EFFORTS.find((x) => x.value === s.effort) ?? EFFORTS[0];
1442
+ return `${e.description} Claude Code \u8207 Codex \u6703\u5728\u555F\u52D5\u6B64\u4EFB\u52D9\u6642\u5957\u7528\uFF1B\u53EF\u9078\u7684\u7D1A\u8DDD\u4F9D model \u800C\u5B9A\u3002`;
1443
+ }
1209
1444
  if (field === "autoMode") return s.autoMode ? "\u958B\u555F\uFF1AAgent \u6703\u76F4\u63A5\u4FEE\u6539\u6A94\u6848\u3001\u57F7\u884C\u6307\u4EE4\uFF0C\u4E0D\u6703\u9010\u6B21\u5411\u4F60\u78BA\u8A8D\u3002" : "\u95DC\u9589\uFF1AAgent \u6BCF\u6B21\u8981\u52D5\u4F5C\u524D\uFF0C\u90FD\u6703\u5728\u7D42\u7AEF\u6A5F\u88E1\u7B49\u4F60\u5141\u8A31\u3002";
1210
1445
  if (field === "submit") return blockReason(s, ctx, loadingGit);
1211
1446
  return null;
@@ -1260,7 +1495,7 @@ async function loadFormContext(api) {
1260
1495
  const lists = await Promise.all(
1261
1496
  (agents ?? []).map(async (a) => {
1262
1497
  const list = await api.listAgentModels(a.id).catch(() => null);
1263
- return [a.id, list ? list.models.map((m) => m.id) : []];
1498
+ return [a.id, list ? list.models : []];
1264
1499
  })
1265
1500
  );
1266
1501
  return {
@@ -1277,13 +1512,17 @@ var h, EFFORTS, DEFAULT_EFFORT, LABELS, TEXT_FIELDS;
1277
1512
  var init_task_form = __esm({
1278
1513
  "packages/tui/src/task-form.ts"() {
1279
1514
  "use strict";
1515
+ init_effort();
1516
+ init_launch();
1280
1517
  init_attachments();
1281
1518
  h = React.createElement;
1282
1519
  EFFORTS = [
1283
1520
  { value: "low", label: "\u5FEB\u901F", description: "\u9069\u5408\u7BC4\u570D\u660E\u78BA\u3001\u4F4E\u98A8\u96AA\u7684\u5C0F\u4EFB\u52D9\u3002" },
1284
1521
  { value: "medium", label: "\u6A19\u6E96", description: "\u517C\u9867\u901F\u5EA6\u8207\u63A8\u7406\u6DF1\u5EA6\uFF0C\u9069\u5408\u4E00\u822C\u958B\u767C\u4EFB\u52D9\u3002" },
1285
1522
  { value: "high", label: "\u6DF1\u5165", description: "\u9069\u5408\u8DE8\u6A94\u6848\u3001\u9664\u932F\u6216\u9700\u8981\u8F03\u591A\u5224\u65B7\u7684\u4EFB\u52D9\u3002" },
1286
- { value: "xhigh", label: "\u6975\u81F4", description: "\u9069\u5408\u9AD8\u8907\u96DC\u5EA6\u3001\u9577\u93C8\u63A8\u7406\u6216\u9AD8\u98A8\u96AA\u8B8A\u66F4\u3002" }
1523
+ { value: "xhigh", label: "\u6975\u81F4", description: "\u9069\u5408\u9AD8\u8907\u96DC\u5EA6\u3001\u9577\u93C8\u63A8\u7406\u6216\u9AD8\u98A8\u96AA\u8B8A\u66F4\u3002" },
1524
+ { value: "max", label: "\u6700\u5927", description: "\u6700\u6DF1\u7684\u63A8\u7406\uFF0C\u9069\u5408\u6700\u56F0\u96E3\u7684\u554F\u984C\u3002" },
1525
+ { value: "ultra", label: "\u59D4\u6D3E", description: "\u6700\u6DF1\u63A8\u7406\u4E26\u81EA\u52D5\u59D4\u6D3E\u5B50\u4EFB\u52D9\uFF0C\u8017\u6642\u8207\u7528\u91CF\u53EF\u80FD\u660E\u986F\u589E\u52A0\u3002" }
1287
1526
  ];
1288
1527
  DEFAULT_EFFORT = "medium";
1289
1528
  LABELS = {
@@ -1319,7 +1558,7 @@ __export(src_exports, {
1319
1558
  showDiff: () => showDiff
1320
1559
  });
1321
1560
  import { spawnSync as spawnSync2 } from "child_process";
1322
- import path26 from "path";
1561
+ import path27 from "path";
1323
1562
  import React2, { useEffect as useEffect2, useState as useState2 } from "react";
1324
1563
  import { Box as Box2, Text as Text2, render as render2, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
1325
1564
  function findDetachKey(text) {
@@ -1615,7 +1854,7 @@ function showDiff(task) {
1615
1854
  });
1616
1855
  }
1617
1856
  function tmuxAttach(opts, sessionName) {
1618
- const conf = path26.join(opts.userDataDir, "tmux.conf");
1857
+ const conf = path27.join(opts.userDataDir, "tmux.conf");
1619
1858
  const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
1620
1859
  const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
1621
1860
  spawnSync2("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
@@ -1746,8 +1985,8 @@ var init_src = __esm({
1746
1985
  });
1747
1986
 
1748
1987
  // packages/cli/src/main.ts
1749
- import fs22 from "fs";
1750
- import path27 from "path";
1988
+ import fs23 from "fs";
1989
+ import path28 from "path";
1751
1990
  import { fileURLToPath } from "url";
1752
1991
  import { parseArgs as parseArgs2 } from "node:util";
1753
1992
 
@@ -2699,191 +2938,8 @@ function unwatchAllSubAgents() {
2699
2938
  }
2700
2939
  }
2701
2940
 
2702
- // packages/core/src/launch.ts
2703
- var ARTIFACTS_DIR_SUFFIX = ".artifacts";
2704
- var SCRATCH_DIR_NAME = "scratch";
2705
- var ARTIFACTS_FALLBACK_DIR = ".vibeflow-artifacts";
2706
- function pathBasename(p) {
2707
- const norm = p.replace(/\\/g, "/").replace(/\/+$/, "");
2708
- const i = norm.lastIndexOf("/");
2709
- return i >= 0 ? norm.slice(i + 1) : norm;
2710
- }
2711
- function agentArtifactsDir(worktreePath, workspacePath) {
2712
- if (!worktreePath || !workspacePath) return null;
2713
- const dir = toShellPath(workspacePath);
2714
- const ws = pathBasename(worktreePath);
2715
- return `${dir}/${ws}${ARTIFACTS_DIR_SUFFIX}`;
2716
- }
2717
- function buildArtifactPrompt(artifactsDir = ARTIFACTS_FALLBACK_DIR) {
2718
- return [
2719
- "Artifact \u8A2D\u5B9A\uFF08\u672C\u6B21 session \u5168\u7A0B\u9069\u7528\uFF09\uFF1A",
2720
- `1. \u672C\u4EFB\u52D9\u7684 Artifact \u8CC7\u6599\u593E\u662F ${artifactsDir}/\uFF1B\u9700\u8981\u4FDD\u5B58\u7D66\u4F7F\u7528\u8005\u7684\u9A57\u8B49\u8B49\u64DA\u6216\u5831\u544A\u6642\uFF0C\u8ACB\u5BEB\u5165\u6B64\u8CC7\u6599\u593E\uFF08\u4E0D\u5B58\u5728\u8ACB\u5EFA\u7ACB\uFF09\u3002`,
2721
- `2. \u4F7F\u7528\u8005\u6703\u5728 VibeFlow \u7684 Artifacts \u5206\u9801\u6AA2\u8996\u6839\u76EE\u9304\u5167\u5BB9\uFF1B\u53EA\u653E\u4F60\u6703\u4E3B\u52D5\u8ACB\u4F7F\u7528\u8005\u904E\u76EE\u7684\u6A94\u6848\u3002`,
2722
- `3. \u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u66AB\u5B58\uFF08\u4E00\u6B21\u6027 script\u3001log\u3001\u4E2D\u9593\u8F38\u51FA\u3001debug \u6A94\uFF09\u653E\u5728 ${artifactsDir}/${SCRATCH_DIR_NAME}/\u3002`,
2723
- `4. \u82E5\u5DE5\u5177\u5148\u8F38\u51FA\u5230 /tmp\u3001/private/tmp\u3001$TMPDIR \u6216 ~/Downloads\uFF0C\u8ACB\u628A\u6700\u7D42\u8981\u5448\u73FE\u7684\u6A94\u6848\u8907\u88FD\u5230 ${artifactsDir}/ \u4E26\u78BA\u8A8D\u5B58\u5728\u3002`,
2724
- "5. Artifact \u8CC7\u6599\u593E\u4F4D\u65BC worktree \u4E4B\u5916\uFF1B\u4E0D\u8981\u628A\u5176\u4E2D\u5167\u5BB9\u52A0\u5165 git commit\uFF0C\u4E5F\u4E0D\u8981\u628A\u6700\u7D42\u4EA4\u4ED8\u7269\u53EA\u7559\u5728\u9019\u88E1\u3002"
2725
- ].join("\n");
2726
- }
2727
- var AUTO_PERMISSION_MODE = "auto";
2728
- function claudePermissionFlag(autoMode) {
2729
- return autoMode ? ` --permission-mode ${AUTO_PERMISSION_MODE}` : "";
2730
- }
2731
- function shellQuote(s) {
2732
- return `'${s.replace(/'/g, `'\\''`)}'`;
2733
- }
2734
- function toShellPath(p) {
2735
- return p.replace(/\\/g, "/");
2736
- }
2737
- function boardEnvPrefix(task, opts) {
2738
- const board = opts?.boardCli;
2739
- if (!board) return "";
2740
- const entries = [
2741
- ["VIBEFLOW_TASK_ID", task.id],
2742
- ["VIBEFLOW_PROJECT_PATH", task.projectPath],
2743
- ["VIBEFLOW_BRANCH", task.branch],
2744
- ["VIBEFLOW_BASE_BRANCH", task.baseBranch],
2745
- ["VIBEFLOW_STORE_DIR", board?.storeDir],
2746
- ["VIBEFLOW_CLI", board?.cliPath],
2747
- ["VIBEFLOW_CLI_LOADER", board?.loaderPath],
2748
- ["VIBEFLOW_AUTO_MODE", opts?.autoMode ? "1" : "0"]
2749
- ];
2750
- const exports = entries.filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${shellQuote(toShellPath(value))}`);
2751
- return exports.length ? `export ${exports.join(" ")}; ` : "";
2752
- }
2753
- function codexAutoFlag(autoMode) {
2754
- return autoMode ? "--dangerously-bypass-approvals-and-sandbox " : "";
2755
- }
2756
- function claudeResumeOrFresh(sessionId, worktreePath, tail) {
2757
- if (!worktreePath) return `claude --resume ${sessionId} ${tail}\r`;
2758
- const munged = worktreePath.replace(/[^a-zA-Z0-9]/g, "-");
2759
- const sessionFile = `"$HOME/.claude/projects/${munged}/${sessionId}.jsonl"`;
2760
- return `if [ -f ${sessionFile} ]; then claude --resume ${sessionId} ${tail}; else claude --session-id ${sessionId} ${tail}; fi\r`;
2761
- }
2762
- var SUBAGENTS_DIR2 = ".vibeflow-subagents";
2763
- var PROGRESS_EVENTS_DIR = ".vibeflow-events";
2764
- var TODO_TOOLS = "TodoWrite|TaskCreate|TaskUpdate";
2765
- function buildClaudeSettings(worktreePath) {
2766
- const settings = { theme: "dark" };
2767
- if (worktreePath) {
2768
- const dir = `${toShellPath(worktreePath)}/${SUBAGENTS_DIR2}`;
2769
- const command = `mkdir -p "${dir}" && cat > "${dir}/$(date +%s)-$$-$RANDOM.json"`;
2770
- const taskHook = {
2771
- matcher: "Task",
2772
- hooks: [{ type: "command", command }]
2773
- };
2774
- const eventsDir = `${toShellPath(worktreePath)}/${PROGRESS_EVENTS_DIR}`;
2775
- const progress = {
2776
- hooks: [{ type: "command", command: `mkdir -p "${eventsDir}" && cat > "${eventsDir}/$(date +%s)-$$-$RANDOM.json"; exit 0` }]
2777
- };
2778
- settings.hooks = {
2779
- PreToolUse: [taskHook],
2780
- PostToolUse: [taskHook, { matcher: TODO_TOOLS, ...progress }],
2781
- Stop: [progress],
2782
- Notification: [progress],
2783
- UserPromptSubmit: [progress]
2784
- };
2785
- }
2786
- return JSON.stringify(settings);
2787
- }
2788
- function resolveSystemPrompt(custom) {
2789
- return custom && custom.trim() ? custom : "";
2790
- }
2791
- function buildPrompt(task) {
2792
- const lines = [`\u4EFB\u52D9\u6A19\u984C\uFF1A${task.title}`];
2793
- const description = task.description?.trim();
2794
- if (description) {
2795
- lines.push("", "\u4EFB\u52D9\u63CF\u8FF0\uFF1A", description);
2796
- }
2797
- return lines.join("\n");
2798
- }
2799
- function executorSessionId(taskId, runId) {
2800
- const source = runId ? `${taskId}:${runId}` : taskId;
2801
- const raw = source.replace(/[^0-9a-f]/gi, "").toLowerCase();
2802
- const hex = runId ? `${raw.slice(0, 24).padEnd(24, "0")}${namespaceHash(`executor:${source}`)}` : raw.padEnd(32, "0").slice(0, 32);
2803
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-8${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
2804
- }
2805
- function namespaceHash(namespace) {
2806
- let hash = 2166136261;
2807
- for (let i = 0; i < namespace.length; i += 1) {
2808
- hash ^= namespace.charCodeAt(i);
2809
- hash = Math.imul(hash, 16777619);
2810
- }
2811
- return (hash >>> 0).toString(16).padStart(8, "0");
2812
- }
2813
- function assembleCommand(agent, systemPrompt, prompt, model, effort, opts, worktreePath, sessionId, workspacePath) {
2814
- let cmd;
2815
- if (agent === "claude") {
2816
- const settings = ` --settings ${shellQuote(buildClaudeSettings(worktreePath))}`;
2817
- const addDir = workspacePath ? ` --add-dir ${shellQuote(toShellPath(workspacePath))}` : "";
2818
- const modelFlag = model ? ` --model ${model}` : "";
2819
- const effortFlag = effort ? ` --effort ${effort}` : "";
2820
- const library = opts?.library;
2821
- const libraryFlags = library ? ` --plugin-dir ${shellQuote(toShellPath(library.pluginDir))} --add-dir ${shellQuote(toShellPath(library.libraryDir))}` : "";
2822
- const flags = `--chrome${claudePermissionFlag(opts?.autoMode)}${modelFlag}${effortFlag}${settings}${addDir}${libraryFlags}`;
2823
- const sysFlag = systemPrompt ? ` --append-system-prompt ${shellQuote(systemPrompt)}` : "";
2824
- const promptArg = prompt ? ` ${shellQuote(prompt)}` : "";
2825
- const tail = `${flags}${sysFlag}${promptArg}`;
2826
- cmd = sessionId && opts?.resume ? claudeResumeOrFresh(sessionId, worktreePath, tail) : `claude ${sessionId ? `--session-id ${sessionId} ` : opts?.resume ? "--continue " : ""}${tail}\r`;
2827
- } else {
2828
- const combined = [systemPrompt, prompt].filter(Boolean).join("\n\n");
2829
- const codexEffortFlag = effort ? `-c ${shellQuote(`model_reasoning_effort="${effort}"`)} ` : "";
2830
- const codexHome = opts?.library ? `CODEX_HOME=${shellQuote(toShellPath(opts.library.codexHome))} ` : "";
2831
- const promptArg = combined ? ` ${shellQuote(combined)}` : "";
2832
- cmd = `${codexHome}codex ${codexAutoFlag(opts?.autoMode)}${codexEffortFlag}--model ${model}${promptArg}\r`;
2833
- }
2834
- if (cmd.length > 2e5) console.warn(`[VibeFlow] launch command is ${cmd.length} bytes \u2014 approaching ARG_MAX`);
2835
- return cmd;
2836
- }
2837
- var AGENT_NAMES = {
2838
- claude: "Claude Code",
2839
- codex: "Codex CLI"
2840
- };
2841
- var DEFAULT_MODELS = {
2842
- claude: "sonnet",
2843
- codex: "gpt-5.5"
2844
- };
2845
- var LEGACY_MODEL_FALLBACKS = {
2846
- codex: {
2847
- "gpt-5-codex": "gpt-5.5",
2848
- "gpt-5": "gpt-5.5"
2849
- }
2850
- };
2851
- function normalizeModel(agent, model) {
2852
- return LEGACY_MODEL_FALLBACKS[agent]?.[model] ?? model;
2853
- }
2854
- function taskAgent(task) {
2855
- const agent = task.agentCli;
2856
- return agent && agent in AGENT_NAMES ? agent : "claude";
2857
- }
2858
- function taskModel(task) {
2859
- const agent = taskAgent(task);
2860
- return normalizeModel(agent, task.model || DEFAULT_MODELS[agent]);
2861
- }
2862
- function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
2863
- const agent = taskAgent(task);
2864
- const model = taskModel(task);
2865
- const artifactsDir = agentArtifactsDir(task.worktreePath, workspacePath) ?? ARTIFACTS_FALLBACK_DIR;
2866
- const builtInPrompt = buildArtifactPrompt(artifactsDir);
2867
- const libraryPrompt = opts?.library?.promptText?.trim();
2868
- const customPrompt = resolveSystemPrompt(systemPrompt);
2869
- const sys = [builtInPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
2870
- const includeTaskPrompt = opts?.includeTaskPrompt !== false;
2871
- const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
2872
- const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
2873
- return boardEnvPrefix(task, opts) + assembleCommand(
2874
- agent,
2875
- sys,
2876
- prompt,
2877
- model,
2878
- task.effort,
2879
- opts,
2880
- task.worktreePath,
2881
- sessionId,
2882
- workspacePath
2883
- );
2884
- }
2885
-
2886
2941
  // packages/core/src/git.ts
2942
+ init_launch();
2887
2943
  init_env();
2888
2944
 
2889
2945
  // packages/core/src/github.ts
@@ -3001,16 +3057,17 @@ async function listPrs(run3, repo, filter) {
3001
3057
  return rows.map((r) => toPr(r, repo));
3002
3058
  }
3003
3059
  async function fetchRepoItems(run3, repo) {
3004
- const [assignedIssues, authoredIssues, assignedPrs, authoredPrs, reviewPrs] = await Promise.all([
3060
+ const [assignedIssues, authoredIssues, assignedPrs, authoredPrs, reviewPrs, reviewedPrs] = await Promise.all([
3005
3061
  listIssues(run3, repo, ["--assignee", "@me"]),
3006
3062
  listIssues(run3, repo, ["--author", "@me"]),
3007
3063
  listPrs(run3, repo, ["--assignee", "@me"]),
3008
3064
  listPrs(run3, repo, ["--author", "@me"]),
3009
- listPrs(run3, repo, ["--search", "review-requested:@me"])
3065
+ listPrs(run3, repo, ["--search", "review-requested:@me"]),
3066
+ listPrs(run3, repo, ["--search", "reviewed-by:@me"])
3010
3067
  ]);
3011
3068
  return {
3012
3069
  issues: mergeItems([assignedIssues, authoredIssues]),
3013
- prs: mergeItems([assignedPrs, authoredPrs, reviewPrs])
3070
+ prs: mergeItems([assignedPrs, authoredPrs, reviewPrs, reviewedPrs])
3014
3071
  };
3015
3072
  }
3016
3073
  async function fetchPrIndex(run3, repo) {
@@ -4142,10 +4199,10 @@ function taskAttachmentStagingPath(workspacePath, taskId) {
4142
4199
 
4143
4200
  // packages/core/src/agents.ts
4144
4201
  init_env();
4202
+ init_effort();
4145
4203
  import { execFile as execFile6 } from "child_process";
4146
4204
  import { promisify as promisify3 } from "util";
4147
4205
  var pexec3 = promisify3(execFile6);
4148
- var AGENT_EFFORTS = ["low", "medium", "high", "xhigh"];
4149
4206
  var DEFAULT_TASK_EFFORT = "medium";
4150
4207
  var AGENT_CLIS = [
4151
4208
  {
@@ -4153,7 +4210,7 @@ var AGENT_CLIS = [
4153
4210
  bin: "claude",
4154
4211
  name: "Claude Code",
4155
4212
  models: [
4156
- { id: "sonnet", label: "Sonnet\uFF08\u5E73\u8861\u30FB\u9810\u8A2D\uFF09" },
4213
+ { id: "sonnet", label: "Sonnet\uFF08\u5E73\u8861\uFF09" },
4157
4214
  { id: "haiku", label: "Haiku\uFF08\u8F15\u91CF\uFF09" },
4158
4215
  { id: "opus", label: "Opus\uFF08\u6700\u5F37\uFF09" }
4159
4216
  ]
@@ -4163,7 +4220,7 @@ var AGENT_CLIS = [
4163
4220
  bin: "codex",
4164
4221
  name: "Codex CLI",
4165
4222
  models: [
4166
- { id: "gpt-5.5", label: "GPT-5.5\uFF08\u9810\u8A2D\uFF09" },
4223
+ { id: "gpt-5.5", label: "GPT-5.5" },
4167
4224
  { id: "gpt-5.4", label: "GPT-5.4" },
4168
4225
  { id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
4169
4226
  ]
@@ -4535,8 +4592,8 @@ async function taskCommand(argv) {
4535
4592
  }
4536
4593
 
4537
4594
  // packages/cli/src/host.ts
4538
- import fs19 from "fs";
4539
- import path23 from "path";
4595
+ import fs20 from "fs";
4596
+ import path24 from "path";
4540
4597
  import { randomBytes as randomBytes2 } from "crypto";
4541
4598
 
4542
4599
  // packages/core/src/events.ts
@@ -5074,35 +5131,278 @@ async function syncBuiltinsAtStartup() {
5074
5131
  }
5075
5132
 
5076
5133
  // packages/core/src/service.ts
5077
- import fs17 from "fs";
5078
- import os6 from "os";
5079
- import path21 from "path";
5134
+ import fs18 from "fs";
5135
+ import os8 from "os";
5136
+ import path22 from "path";
5080
5137
  import { randomUUID as randomUUID4 } from "crypto";
5081
5138
 
5082
5139
  // packages/core/src/agent-models.ts
5083
- import { execFile as execFile7 } from "child_process";
5140
+ import fs13 from "fs";
5141
+ import os6 from "os";
5142
+ import path17 from "path";
5143
+
5144
+ // packages/core/src/claude-models.ts
5145
+ init_effort();
5084
5146
  import fs12 from "fs";
5085
5147
  import os4 from "os";
5086
5148
  import path16 from "path";
5087
- import { promisify as promisify4 } from "util";
5149
+
5150
+ // packages/core/src/json-lines-process.ts
5088
5151
  init_env();
5089
- var pexec4 = promisify4(execFile7);
5152
+ import { spawn as spawn4 } from "child_process";
5153
+ function spawnJsonLines(bin, args, opts) {
5154
+ const spawnOpts = { cwd: opts.cwd, env: opts.env ?? execEnv(), windowsHide: true };
5155
+ const proc = process.platform === "win32" ? spawn4([bin, ...args].join(" "), { ...spawnOpts, shell: true }) : spawn4(bin, args, spawnOpts);
5156
+ const inbox = [];
5157
+ const waiters = [];
5158
+ let failure = null;
5159
+ let stderr = "";
5160
+ let exited = false;
5161
+ const finished = () => {
5162
+ if (exited) return;
5163
+ exited = true;
5164
+ opts.onExit?.();
5165
+ };
5166
+ const fail2 = (err) => {
5167
+ if (failure) return;
5168
+ failure = err;
5169
+ for (const w of waiters.splice(0)) w.reject(err);
5170
+ };
5171
+ const deliver = (message) => {
5172
+ const i = waiters.findIndex((w) => w.match(message));
5173
+ if (i === -1) inbox.push(message);
5174
+ else waiters.splice(i, 1)[0].resolve(message);
5175
+ };
5176
+ let buffered = "";
5177
+ proc.stdout.setEncoding("utf8");
5178
+ proc.stdout.on("data", (chunk) => {
5179
+ buffered += chunk;
5180
+ let nl;
5181
+ while ((nl = buffered.indexOf("\n")) >= 0) {
5182
+ const line = buffered.slice(0, nl).trim();
5183
+ buffered = buffered.slice(nl + 1);
5184
+ if (!line) continue;
5185
+ let message;
5186
+ try {
5187
+ message = JSON.parse(line);
5188
+ } catch {
5189
+ continue;
5190
+ }
5191
+ deliver(message);
5192
+ }
5193
+ });
5194
+ proc.stderr.setEncoding("utf8");
5195
+ proc.stderr.on("data", (chunk) => {
5196
+ stderr = (stderr + chunk).slice(-2048);
5197
+ });
5198
+ proc.on("error", (err) => {
5199
+ fail2(err);
5200
+ if (proc.pid === void 0) {
5201
+ clearTimeout(timer);
5202
+ finished();
5203
+ }
5204
+ });
5205
+ proc.on("exit", (code2, signal) => {
5206
+ finished();
5207
+ clearTimeout(timer);
5208
+ clearTimeout(graceTimer);
5209
+ const reason = signal ? `was killed (${signal})` : `exited with code ${code2}`;
5210
+ fail2(new Error(`${bin} ${reason}${stderr.trim() ? `: ${stderr.trim()}` : ""}`));
5211
+ });
5212
+ proc.stdin.on("error", () => {
5213
+ });
5214
+ const kill = () => {
5215
+ if (exited) return;
5216
+ if (process.platform === "win32" && proc.pid !== void 0) {
5217
+ spawn4("taskkill", ["/pid", String(proc.pid), "/T", "/F"], { windowsHide: true }).on("error", () => proc.kill());
5218
+ return;
5219
+ }
5220
+ proc.kill();
5221
+ };
5222
+ const timer = setTimeout(() => {
5223
+ fail2(new Error(`${bin} did not answer within ${opts.timeoutMs}ms`));
5224
+ kill();
5225
+ }, opts.timeoutMs);
5226
+ let graceTimer;
5227
+ return {
5228
+ send(message) {
5229
+ if (!failure) proc.stdin.write(`${JSON.stringify(message)}
5230
+ `);
5231
+ },
5232
+ next(match) {
5233
+ const i = inbox.findIndex(match);
5234
+ if (i >= 0) return Promise.resolve(inbox.splice(i, 1)[0]);
5235
+ if (failure) return Promise.reject(failure);
5236
+ return new Promise((resolve, reject) => waiters.push({ match, resolve, reject }));
5237
+ },
5238
+ close() {
5239
+ clearTimeout(timer);
5240
+ fail2(new Error(`${bin} conversation closed`));
5241
+ proc.stdin.end();
5242
+ if (!exited && !graceTimer) graceTimer = setTimeout(kill, opts.exitGraceMs ?? 3e3);
5243
+ }
5244
+ };
5245
+ }
5246
+
5247
+ // packages/core/src/claude-models.ts
5248
+ var CLAUDE_MODELS_TIMEOUT_MS = 15e3;
5249
+ var REQUEST_ID = "vibeflow-models";
5250
+ var CLAUDE_ARGS = ["-p", "--input-format", "stream-json", "--output-format", "stream-json", "--verbose"];
5251
+ function openClaude() {
5252
+ const cwd = fs12.mkdtempSync(path16.join(os4.tmpdir(), "vf-claude-models-"));
5253
+ return spawnJsonLines("claude", CLAUDE_ARGS, {
5254
+ cwd,
5255
+ timeoutMs: CLAUDE_MODELS_TIMEOUT_MS,
5256
+ onExit: () => fs12.rmSync(cwd, { recursive: true, force: true })
5257
+ });
5258
+ }
5259
+ function isRecord(value) {
5260
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5261
+ }
5262
+ function isOurResponse(message) {
5263
+ return isRecord(message) && message.type === "control_response" && isRecord(message.response) && message.response.request_id === REQUEST_ID;
5264
+ }
5265
+ function parseClaudeInitialize(message) {
5266
+ const outer = isRecord(message) && isRecord(message.response) ? message.response : null;
5267
+ if (outer?.subtype === "error") throw new Error(`claude refused the handshake: ${String(outer.error ?? "unknown error")}`);
5268
+ const body = outer && isRecord(outer.response) ? outer.response : null;
5269
+ if (!body || !Array.isArray(body.models)) throw new Error("claude handshake has no models array");
5270
+ const defaultEntry = body.models.find((e) => isRecord(e) && e.value === "default");
5271
+ const defaultResolved = isRecord(defaultEntry) && typeof defaultEntry.resolvedModel === "string" ? defaultEntry.resolvedModel : void 0;
5272
+ let defaultMarked = false;
5273
+ const seen = /* @__PURE__ */ new Set();
5274
+ const models = [];
5275
+ for (const entry of body.models) {
5276
+ if (!isRecord(entry) || typeof entry.value !== "string") continue;
5277
+ const id = entry.value.trim();
5278
+ if (!id || id === "default" || seen.has(id)) continue;
5279
+ seen.add(id);
5280
+ const label = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : id;
5281
+ const description = typeof entry.description === "string" && entry.description.trim() ? entry.description.trim() : void 0;
5282
+ const levels = entry.supportsEffort === true && Array.isArray(entry.supportedEffortLevels) ? entry.supportedEffortLevels.filter(isAgentEffort) : [];
5283
+ const isDefault = !defaultMarked && defaultResolved !== void 0 && entry.resolvedModel === defaultResolved;
5284
+ if (isDefault) defaultMarked = true;
5285
+ models.push({ id, label, ...description ? { description } : {}, efforts: levels, ...isDefault ? { isDefault } : {} });
5286
+ }
5287
+ if (models.length === 0) throw new Error("claude handshake lists no selectable models");
5288
+ return models;
5289
+ }
5290
+ async function fetchClaudeModels(open = openClaude) {
5291
+ const channel = open();
5292
+ try {
5293
+ channel.send({ type: "control_request", request_id: REQUEST_ID, request: { subtype: "initialize" } });
5294
+ return parseClaudeInitialize(await channel.next(isOurResponse));
5295
+ } finally {
5296
+ channel.close();
5297
+ }
5298
+ }
5299
+
5300
+ // packages/core/src/codex-models.ts
5301
+ init_effort();
5302
+ init_env();
5303
+ import os5 from "os";
5090
5304
  var CODEX_MODELS_TIMEOUT_MS = 2e4;
5305
+ var MAX_PAGES = 20;
5306
+ var CodexLoginRequiredError = class extends Error {
5307
+ constructor() {
5308
+ super("Codex CLI is not signed in; run `codex login`");
5309
+ this.name = "CodexLoginRequiredError";
5310
+ }
5311
+ };
5312
+ function codexAppServerCommand() {
5313
+ return {
5314
+ bin: "codex",
5315
+ args: ["app-server", "--listen", "stdio://"],
5316
+ cwd: os5.homedir(),
5317
+ env: execEnv()
5318
+ };
5319
+ }
5320
+ function openCodex() {
5321
+ const { bin, args, cwd, env } = codexAppServerCommand();
5322
+ return spawnJsonLines(bin, args, { cwd, env, timeoutMs: CODEX_MODELS_TIMEOUT_MS });
5323
+ }
5324
+ function isRecord2(value) {
5325
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5326
+ }
5327
+ var responseTo = (id) => (message) => isRecord2(message) && message.id === id && ("result" in message || "error" in message);
5328
+ function resultOf(message, method) {
5329
+ if (!isRecord2(message)) throw new Error(`codex ${method}: malformed response`);
5330
+ if (isRecord2(message.error)) throw new Error(`codex ${method}: ${String(message.error.message ?? "request failed")}`);
5331
+ if (!isRecord2(message.result)) throw new Error(`codex ${method}: response has no result`);
5332
+ return message.result;
5333
+ }
5334
+ function isCodexSignedIn(result) {
5335
+ return result.account != null || result.requiresOpenaiAuth === false;
5336
+ }
5337
+ function parseCodexModelPage(result) {
5338
+ if (!Array.isArray(result.data)) throw new Error("codex model/list has no data array");
5339
+ const models = [];
5340
+ for (const entry of result.data) {
5341
+ if (!isRecord2(entry) || typeof entry.id !== "string" || !entry.id.trim() || entry.hidden === true) continue;
5342
+ const id = entry.id.trim();
5343
+ const label = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : id;
5344
+ const description = typeof entry.description === "string" && entry.description.trim() ? entry.description.trim() : void 0;
5345
+ const efforts = Array.isArray(entry.supportedReasoningEfforts) ? entry.supportedReasoningEfforts.map((e) => isRecord2(e) ? e.reasoningEffort : void 0).filter(isAgentEffort) : void 0;
5346
+ models.push({
5347
+ id,
5348
+ label,
5349
+ ...description ? { description } : {},
5350
+ ...efforts ? { efforts } : {},
5351
+ ...entry.isDefault === true ? { isDefault: true } : {}
5352
+ });
5353
+ }
5354
+ const nextCursor = typeof result.nextCursor === "string" && result.nextCursor ? result.nextCursor : null;
5355
+ return { models, nextCursor };
5356
+ }
5357
+ async function fetchCodexModels(clientVersion, open = openCodex) {
5358
+ const channel = open();
5359
+ try {
5360
+ channel.send({
5361
+ method: "initialize",
5362
+ id: 1,
5363
+ params: { clientInfo: { name: "vibeflow", title: "VibeFlow", version: clientVersion } }
5364
+ });
5365
+ resultOf(await channel.next(responseTo(1)), "initialize");
5366
+ channel.send({ method: "initialized", params: {} });
5367
+ channel.send({ method: "account/read", id: 2, params: { refreshToken: false } });
5368
+ if (!isCodexSignedIn(resultOf(await channel.next(responseTo(2)), "account/read"))) {
5369
+ throw new CodexLoginRequiredError();
5370
+ }
5371
+ const seen = /* @__PURE__ */ new Set();
5372
+ const models = [];
5373
+ let cursor = null;
5374
+ for (let page = 0; page < MAX_PAGES; page++) {
5375
+ const id = 3 + page;
5376
+ channel.send({
5377
+ method: "model/list",
5378
+ id,
5379
+ params: { limit: 100, includeHidden: false, ...cursor ? { cursor } : {} }
5380
+ });
5381
+ const parsed = parseCodexModelPage(resultOf(await channel.next(responseTo(id)), "model/list"));
5382
+ for (const model of parsed.models) {
5383
+ if (seen.has(model.id)) continue;
5384
+ seen.add(model.id);
5385
+ models.push(model);
5386
+ }
5387
+ cursor = parsed.nextCursor;
5388
+ if (!cursor) break;
5389
+ }
5390
+ if (cursor) throw new Error(`codex model/list did not finish within ${MAX_PAGES} pages`);
5391
+ if (models.length === 0) throw new Error("codex model/list lists no selectable models");
5392
+ return models;
5393
+ } finally {
5394
+ channel.close();
5395
+ }
5396
+ }
5397
+
5398
+ // packages/core/src/agent-models.ts
5399
+ init_effort();
5091
5400
  function builtinModels(id) {
5092
5401
  return (AGENT_CLIS.find((a) => a.id === id) ?? AGENT_CLIS[0]).models;
5093
5402
  }
5094
5403
  function defaultCodexHome() {
5095
5404
  const configured = process.env.CODEX_HOME?.trim();
5096
- return configured ? configured : path16.join(os4.homedir(), ".codex");
5097
- }
5098
- async function defaultRunCodexModels() {
5099
- const { stdout } = await pexec4("codex", ["debug", "models"], {
5100
- env: execEnv(),
5101
- timeout: CODEX_MODELS_TIMEOUT_MS,
5102
- maxBuffer: 16 * 1024 * 1024,
5103
- windowsHide: true
5104
- });
5105
- return stdout;
5405
+ return configured ? configured : path17.join(os6.homedir(), ".codex");
5106
5406
  }
5107
5407
  function parseCodexCatalog(raw) {
5108
5408
  const body = JSON.parse(raw);
@@ -5127,36 +5427,75 @@ function parseCodexCatalog(raw) {
5127
5427
  function errorMessage(err) {
5128
5428
  return err instanceof Error ? err.message : String(err);
5129
5429
  }
5130
- async function listAgentModels(id, opts = {}) {
5131
- if (id !== "codex") return { models: builtinModels(id), source: "builtin" };
5132
- if (opts.refresh) {
5133
- try {
5134
- const stdout = await (opts.runCodexModels ?? defaultRunCodexModels)();
5135
- return { models: parseCodexCatalog(stdout), source: "codex-cli" };
5136
- } catch (err) {
5137
- return { models: builtinModels(id), source: "builtin", error: errorMessage(err) };
5138
- }
5139
- }
5140
- const cachePath = path16.join(opts.codexHome ?? defaultCodexHome(), "models_cache.json");
5430
+ async function readCodexCache(codexHome) {
5431
+ const cachePath = path17.join(codexHome, "models_cache.json");
5141
5432
  try {
5142
- const raw = await fs12.promises.readFile(cachePath, "utf8");
5433
+ const raw = await fs13.promises.readFile(cachePath, "utf8");
5143
5434
  return { models: parseCodexCatalog(raw), source: "codex-cache" };
5144
5435
  } catch (err) {
5145
5436
  const missing = err.code === "ENOENT";
5146
5437
  return {
5147
- models: builtinModels(id),
5438
+ models: builtinModels("codex"),
5148
5439
  source: "builtin",
5149
5440
  ...missing ? {} : { error: errorMessage(err) }
5150
5441
  };
5151
5442
  }
5152
5443
  }
5444
+ function createModelCatalog(opts) {
5445
+ const fetchClaude = opts.fetchClaude ?? (() => fetchClaudeModels());
5446
+ const fetchCodex = opts.fetchCodex ?? (() => fetchCodexModels(opts.version));
5447
+ const inFlight = /* @__PURE__ */ new Map();
5448
+ const settled = /* @__PURE__ */ new Map();
5449
+ const fallback = (id) => id === "codex" ? readCodexCache(opts.codexHome ?? defaultCodexHome()) : Promise.resolve({ models: builtinModels(id), source: "builtin" });
5450
+ const ask = async (id) => {
5451
+ try {
5452
+ const models = await (id === "codex" ? fetchCodex() : fetchClaude());
5453
+ return { models, source: id === "codex" ? "codex-app-server" : "claude-cli" };
5454
+ } catch (err) {
5455
+ const list = await fallback(id);
5456
+ return {
5457
+ ...list,
5458
+ error: errorMessage(err),
5459
+ ...err instanceof CodexLoginRequiredError ? { loginRequired: true } : {}
5460
+ };
5461
+ }
5462
+ };
5463
+ const refresh = (id) => {
5464
+ const running = inFlight.get(id);
5465
+ if (running) return running;
5466
+ const pending = ask(id).then((list) => {
5467
+ settled.set(id, list);
5468
+ return list;
5469
+ }).finally(() => inFlight.delete(id));
5470
+ inFlight.set(id, pending);
5471
+ return pending;
5472
+ };
5473
+ return {
5474
+ async get(id) {
5475
+ const answer = settled.get(id);
5476
+ if (answer) return answer;
5477
+ const list = await fallback(id);
5478
+ return inFlight.has(id) ? { ...list, pending: true } : list;
5479
+ },
5480
+ refresh,
5481
+ warm() {
5482
+ for (const agent of AGENT_CLIS) void refresh(agent.id);
5483
+ },
5484
+ findModel(id, modelId) {
5485
+ return modelFor(settled.get(id)?.models, modelId);
5486
+ }
5487
+ };
5488
+ }
5489
+
5490
+ // packages/core/src/service.ts
5491
+ init_effort();
5153
5492
 
5154
5493
  // packages/core/src/github-auth.ts
5155
5494
  init_env();
5156
- import { execFile as execFile8 } from "child_process";
5157
- import { promisify as promisify5 } from "util";
5495
+ import { execFile as execFile7 } from "child_process";
5496
+ import { promisify as promisify4 } from "util";
5158
5497
  import * as nodePty3 from "node-pty";
5159
- var pexec5 = promisify5(execFile8);
5498
+ var pexec4 = promisify4(execFile7);
5160
5499
  var GITHUB_HOST = "github.com";
5161
5500
  var DEVICE_URL = "https://github.com/login/device";
5162
5501
  var activeLogin = null;
@@ -5191,7 +5530,7 @@ function errorMessage2(err) {
5191
5530
  }
5192
5531
  async function getGitHubCliAuthStatus() {
5193
5532
  try {
5194
- const { stdout } = await pexec5(
5533
+ const { stdout } = await pexec4(
5195
5534
  "gh",
5196
5535
  ["auth", "status", "--hostname", GITHUB_HOST, "--json", "hosts"],
5197
5536
  {
@@ -5223,7 +5562,7 @@ async function logoutGitHubCli() {
5223
5562
  const status = await getGitHubCliAuthStatus();
5224
5563
  if (!status.installed) throw new Error("\u627E\u4E0D\u5230 GitHub CLI\uFF08gh\uFF09\u3002");
5225
5564
  if (!status.login) return status;
5226
- await pexec5(
5565
+ await pexec4(
5227
5566
  "gh",
5228
5567
  ["auth", "logout", "--hostname", GITHUB_HOST, "--user", status.login],
5229
5568
  {
@@ -5306,45 +5645,45 @@ function startGitHubCliLogin(onEvent) {
5306
5645
  }
5307
5646
 
5308
5647
  // packages/core/src/board-cli.ts
5309
- import fs13 from "fs";
5310
- import path17 from "path";
5648
+ import fs14 from "fs";
5649
+ import path18 from "path";
5311
5650
  import { pathToFileURL } from "url";
5312
5651
  async function boardCliLaunchInfo() {
5313
- const storeDir = path17.dirname(getStorePath());
5652
+ const storeDir = path18.dirname(getStorePath());
5314
5653
  const entry = getPlatform().cliEntry?.();
5315
- if (entry && fs13.existsSync(entry)) return { storeDir, cliPath: entry };
5654
+ if (entry && fs14.existsSync(entry)) return { storeDir, cliPath: entry };
5316
5655
  const root = getPlatform().sourceRoot();
5317
5656
  if (!root) return { storeDir };
5318
- const cliPath = path17.join(root, "scripts", "vibeflow.mjs");
5319
- const loaderPath = path17.join(root, "test", "support", "register.mjs");
5320
- if (!fs13.existsSync(cliPath) || !fs13.existsSync(loaderPath)) return { storeDir };
5657
+ const cliPath = path18.join(root, "scripts", "vibeflow.mjs");
5658
+ const loaderPath = path18.join(root, "test", "support", "register.mjs");
5659
+ if (!fs14.existsSync(cliPath) || !fs14.existsSync(loaderPath)) return { storeDir };
5321
5660
  return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
5322
5661
  }
5323
5662
 
5324
5663
  // packages/core/src/decisions.ts
5325
- import fs14 from "fs";
5326
- import path18 from "path";
5664
+ import fs15 from "fs";
5665
+ import path19 from "path";
5327
5666
  var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
5328
5667
  function decisionsKey(worktreePath, branch) {
5329
- return worktreePath ? path18.basename(worktreePath) : branch.replace(/\//g, "-");
5668
+ return worktreePath ? path19.basename(worktreePath) : branch.replace(/\//g, "-");
5330
5669
  }
5331
5670
  function decisionsPath(workspacePath, key) {
5332
- return path18.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
5671
+ return path19.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
5333
5672
  }
5334
5673
  function deleteDecisions(workspacePath, key) {
5335
5674
  try {
5336
- fs14.rmSync(decisionsPath(workspacePath, key), { force: true });
5675
+ fs15.rmSync(decisionsPath(workspacePath, key), { force: true });
5337
5676
  } catch {
5338
5677
  }
5339
5678
  }
5340
5679
 
5341
5680
  // packages/core/src/specs.ts
5342
- import fs15 from "fs";
5343
- import path19 from "path";
5681
+ import fs16 from "fs";
5682
+ import path20 from "path";
5344
5683
  var SPEC_FILE_NAME = "spec.md";
5345
5684
  var MAX_SPEC_BYTES = 256 * 1024;
5346
5685
  function isSpecPath(p) {
5347
- return path19.posix.basename(p) === SPEC_FILE_NAME;
5686
+ return path20.posix.basename(p) === SPEC_FILE_NAME;
5348
5687
  }
5349
5688
  function clip2(buf) {
5350
5689
  return {
@@ -5359,10 +5698,10 @@ async function readWorktreeSpecs(worktreePath, baseBranch) {
5359
5698
  for (const entry of entries) {
5360
5699
  if (entry.status === "D" || !isSpecPath(entry.path)) continue;
5361
5700
  try {
5362
- const full = path19.join(worktreePath, entry.path);
5363
- const stat = fs15.statSync(full);
5701
+ const full = path20.join(worktreePath, entry.path);
5702
+ const stat = fs16.statSync(full);
5364
5703
  if (!stat.isFile()) continue;
5365
- specs.push({ path: entry.path, ...clip2(fs15.readFileSync(full)), updatedAt: stat.mtimeMs });
5704
+ specs.push({ path: entry.path, ...clip2(fs16.readFileSync(full)), updatedAt: stat.mtimeMs });
5366
5705
  } catch {
5367
5706
  }
5368
5707
  }
@@ -5427,7 +5766,7 @@ async function readSpecsAt(cwd, ref, paths) {
5427
5766
  // packages/core/src/chat-session.ts
5428
5767
  init_env();
5429
5768
  init_git_bash();
5430
- import { spawn as spawn5 } from "child_process";
5769
+ import { spawn as spawn6 } from "child_process";
5431
5770
  import { randomUUID as randomUUID3 } from "crypto";
5432
5771
 
5433
5772
  // packages/core/src/chat-store.ts
@@ -5583,7 +5922,7 @@ ${attachmentLines.join("\n")}` : text;
5583
5922
  `--append-system-prompt ${quote(systemPrompt)}`,
5584
5923
  quote(fullText)
5585
5924
  ].filter(Boolean).join(" ") : buildRawAgentCommand(agentCli, model, fullText, systemPrompt, worktreePath, quote, workspacePath);
5586
- const proc = spawn5(shell.command, shell.args(cmd), {
5925
+ const proc = spawn6(shell.command, shell.args(cmd), {
5587
5926
  cwd: worktreePath,
5588
5927
  env: execEnv()
5589
5928
  });
@@ -5709,20 +6048,22 @@ function startChatSend(opts, sender) {
5709
6048
 
5710
6049
  // packages/core/src/service.ts
5711
6050
  init_attachments();
6051
+ init_launch();
5712
6052
 
5713
6053
  // packages/core/src/progress-tracker.ts
5714
6054
  init_progress();
5715
- import fs16 from "fs";
5716
- import os5 from "os";
5717
- import path20 from "path";
6055
+ init_launch();
6056
+ import fs17 from "fs";
6057
+ import os7 from "os";
6058
+ import path21 from "path";
5718
6059
  var SINCE_SLACK_MS = 5e3;
5719
6060
  function claudeProjectDir(home, cwd) {
5720
- return path20.join(home, ".claude", "projects", cwd.replace(/[^a-zA-Z0-9]/g, "-"));
6061
+ return path21.join(home, ".claude", "projects", cwd.replace(/[^a-zA-Z0-9]/g, "-"));
5721
6062
  }
5722
6063
  function pathAliases(p) {
5723
- const resolved = path20.resolve(p);
6064
+ const resolved = path21.resolve(p);
5724
6065
  try {
5725
- const real = fs16.realpathSync(resolved);
6066
+ const real = fs17.realpathSync(resolved);
5726
6067
  return real === resolved ? [resolved] : [resolved, real];
5727
6068
  } catch {
5728
6069
  return [resolved];
@@ -5738,7 +6079,7 @@ function samePath(a, b) {
5738
6079
  }
5739
6080
  function listFiles(dir, test) {
5740
6081
  try {
5741
- return fs16.readdirSync(dir).filter(test).map((name) => path20.join(dir, name));
6082
+ return fs17.readdirSync(dir).filter(test).map((name) => path21.join(dir, name));
5742
6083
  } catch {
5743
6084
  return [];
5744
6085
  }
@@ -5746,22 +6087,22 @@ function listFiles(dir, test) {
5746
6087
  function firstLine(file) {
5747
6088
  let fd;
5748
6089
  try {
5749
- fd = fs16.openSync(file, "r");
6090
+ fd = fs17.openSync(file, "r");
5750
6091
  const buf = Buffer.alloc(64 * 1024);
5751
- const n = fs16.readSync(fd, buf, 0, buf.length, 0);
6092
+ const n = fs17.readSync(fd, buf, 0, buf.length, 0);
5752
6093
  const text = buf.subarray(0, n).toString("utf8");
5753
6094
  const nl = text.indexOf("\n");
5754
6095
  return nl === -1 ? null : text.slice(0, nl);
5755
6096
  } catch {
5756
6097
  return null;
5757
6098
  } finally {
5758
- if (fd !== void 0) fs16.closeSync(fd);
6099
+ if (fd !== void 0) fs17.closeSync(fd);
5759
6100
  }
5760
6101
  }
5761
6102
  function advance(file, cursor, reduce, initial) {
5762
6103
  let size;
5763
6104
  try {
5764
- size = fs16.statSync(file).size;
6105
+ size = fs17.statSync(file).size;
5765
6106
  } catch {
5766
6107
  return;
5767
6108
  }
@@ -5773,9 +6114,9 @@ function advance(file, cursor, reduce, initial) {
5773
6114
  if (size === cursor.offset) return;
5774
6115
  let fd;
5775
6116
  try {
5776
- fd = fs16.openSync(file, "r");
6117
+ fd = fs17.openSync(file, "r");
5777
6118
  const chunk = Buffer.alloc(size - cursor.offset);
5778
- const n = fs16.readSync(fd, chunk, 0, chunk.length, cursor.offset);
6119
+ const n = fs17.readSync(fd, chunk, 0, chunk.length, cursor.offset);
5779
6120
  cursor.offset += n;
5780
6121
  const data = Buffer.concat([cursor.rest, chunk.subarray(0, n)]);
5781
6122
  const nl = data.lastIndexOf(10);
@@ -5789,7 +6130,7 @@ function advance(file, cursor, reduce, initial) {
5789
6130
  }
5790
6131
  } catch {
5791
6132
  } finally {
5792
- if (fd !== void 0) fs16.closeSync(fd);
6133
+ if (fd !== void 0) fs17.closeSync(fd);
5793
6134
  }
5794
6135
  }
5795
6136
  function newCursor(state) {
@@ -5808,14 +6149,14 @@ function codexDayDirs(sessionsRoot, since) {
5808
6149
  const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
5809
6150
  while (cur <= end) {
5810
6151
  const pad = (n) => String(n).padStart(2, "0");
5811
- days.push(path20.join(sessionsRoot, String(cur.getFullYear()), pad(cur.getMonth() + 1), pad(cur.getDate())));
6152
+ days.push(path21.join(sessionsRoot, String(cur.getFullYear()), pad(cur.getMonth() + 1), pad(cur.getDate())));
5812
6153
  cur.setDate(cur.getDate() + 1);
5813
6154
  }
5814
6155
  return days;
5815
6156
  }
5816
6157
  function createProgressTracker(opts) {
5817
- const home = opts.homeDir ?? os5.homedir();
5818
- const codexHomes = opts.codexHomes ?? (() => [path20.join(home, ".codex")]);
6158
+ const home = opts.homeDir ?? os7.homedir();
6159
+ const codexHomes = opts.codexHomes ?? (() => [path21.join(home, ".codex")]);
5819
6160
  const pollMs = opts.pollMs ?? 2e3;
5820
6161
  const entries = /* @__PURE__ */ new Map();
5821
6162
  const belongsToRun = (target, startedAt) => target.since == null || startedAt != null && startedAt >= target.since - SINCE_SLACK_MS;
@@ -5824,7 +6165,7 @@ function createProgressTracker(opts) {
5824
6165
  const dirs = pathAliases(target.worktreePath).map((p) => claudeProjectDir(home, p));
5825
6166
  for (const file of dirs.flatMap((dir) => listFiles(dir, (n) => n.endsWith(".jsonl")))) {
5826
6167
  if (entry.claude.has(file) || entry.rejected.has(file)) continue;
5827
- const session = path20.basename(file, ".jsonl");
6168
+ const session = path21.basename(file, ".jsonl");
5828
6169
  if (session !== target.sessionId) {
5829
6170
  const head = firstLine(file);
5830
6171
  if (head == null) continue;
@@ -5837,8 +6178,8 @@ function createProgressTracker(opts) {
5837
6178
  entry.claude.set(file, newCursor(initialClaudeState()));
5838
6179
  }
5839
6180
  for (const file of Array.from(entry.claude.keys())) {
5840
- if (file.includes(`${path20.sep}subagents${path20.sep}`)) continue;
5841
- const sub = path20.join(file.slice(0, -".jsonl".length), "subagents");
6181
+ if (file.includes(`${path21.sep}subagents${path21.sep}`)) continue;
6182
+ const sub = path21.join(file.slice(0, -".jsonl".length), "subagents");
5842
6183
  for (const f of listFiles(sub, (n) => n.endsWith(".jsonl"))) {
5843
6184
  if (!entry.claude.has(f)) entry.claude.set(f, newCursor(initialClaudeState()));
5844
6185
  }
@@ -5847,7 +6188,7 @@ function createProgressTracker(opts) {
5847
6188
  function discoverCodex(entry) {
5848
6189
  const { target } = entry;
5849
6190
  for (const codexHome of codexHomes()) {
5850
- for (const day of codexDayDirs(path20.join(codexHome, "sessions"), target.since)) {
6191
+ for (const day of codexDayDirs(path21.join(codexHome, "sessions"), target.since)) {
5851
6192
  for (const file of listFiles(day, (n) => n.startsWith("rollout-") && n.endsWith(".jsonl"))) {
5852
6193
  if (entry.codex.has(file) || entry.rejected.has(file)) continue;
5853
6194
  const head = firstLine(file);
@@ -5864,11 +6205,11 @@ function createProgressTracker(opts) {
5864
6205
  }
5865
6206
  }
5866
6207
  function drainHooks(entry) {
5867
- const dir = path20.join(entry.target.worktreePath, PROGRESS_EVENTS_DIR);
6208
+ const dir = path21.join(entry.target.worktreePath, PROGRESS_EVENTS_DIR);
5868
6209
  for (const file of listFiles(dir, (n) => n.endsWith(".json")).sort()) {
5869
6210
  try {
5870
- const at = fs16.statSync(file).mtimeMs;
5871
- const event = JSON.parse(fs16.readFileSync(file, "utf8"));
6211
+ const at = fs17.statSync(file).mtimeMs;
6212
+ const event = JSON.parse(fs17.readFileSync(file, "utf8"));
5872
6213
  const foreign = entry.target.sessionId && typeof event.session_id === "string" && event.session_id !== entry.target.sessionId;
5873
6214
  const activity = foreign ? null : activityFromHook(event, at);
5874
6215
  if (activity) entry.hookActivity = latestActivity(entry.hookActivity ?? activity, activity);
@@ -5876,7 +6217,7 @@ function createProgressTracker(opts) {
5876
6217
  continue;
5877
6218
  }
5878
6219
  try {
5879
- fs16.unlinkSync(file);
6220
+ fs17.unlinkSync(file);
5880
6221
  } catch {
5881
6222
  }
5882
6223
  }
@@ -5888,8 +6229,8 @@ function createProgressTracker(opts) {
5888
6229
  if (target.agent === "claude") {
5889
6230
  for (const [file, cursor] of entry.claude) {
5890
6231
  runUsage = addUsage(runUsage, claudeUsageTotal(cursor.state));
5891
- if (file.includes(`${path20.sep}subagents${path20.sep}`)) continue;
5892
- const isExecutor = path20.basename(file, ".jsonl") === target.sessionId;
6232
+ if (file.includes(`${path21.sep}subagents${path21.sep}`)) continue;
6233
+ const isExecutor = path21.basename(file, ".jsonl") === target.sessionId;
5893
6234
  const s = cursor.state;
5894
6235
  if (isExecutor || !target.sessionId && (!primary || (s.startedAt ?? 0) > (primary.startedAt ?? 0))) {
5895
6236
  primary = { todos: s.todos, contextTokens: s.contextTokens, activity: s.activity, startedAt: s.startedAt };
@@ -5968,10 +6309,10 @@ function createProgressTracker(opts) {
5968
6309
  };
5969
6310
  entry.timer.unref?.();
5970
6311
  entries.set(target.taskId, entry);
5971
- const eventsDir = path20.join(target.worktreePath, PROGRESS_EVENTS_DIR);
6312
+ const eventsDir = path21.join(target.worktreePath, PROGRESS_EVENTS_DIR);
5972
6313
  try {
5973
- fs16.mkdirSync(eventsDir, { recursive: true });
5974
- entry.watcher = fs16.watch(eventsDir, () => {
6314
+ fs17.mkdirSync(eventsDir, { recursive: true });
6315
+ entry.watcher = fs17.watch(eventsDir, () => {
5975
6316
  if (entry.pending) return;
5976
6317
  entry.pending = setTimeout(() => {
5977
6318
  entry.pending = null;
@@ -6056,17 +6397,18 @@ function validBoard(value) {
6056
6397
  }
6057
6398
  return next;
6058
6399
  }
6059
- function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatus }) {
6400
+ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatus, modelCatalog }) {
6060
6401
  const sink = bus.sink();
6402
+ const models = modelCatalog ?? createModelCatalog({ version });
6061
6403
  let libraryCodexHome = null;
6062
6404
  void libraryRoot().then((root) => {
6063
6405
  libraryCodexHome = libraryPaths(root).codexHome;
6064
6406
  }).catch(() => {
6065
6407
  });
6066
- const home = homeDir ?? os6.homedir();
6408
+ const home = homeDir ?? os8.homedir();
6067
6409
  const progress = createProgressTracker({
6068
6410
  homeDir: home,
6069
- codexHomes: () => [path21.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
6411
+ codexHomes: () => [path22.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
6070
6412
  onUpdate: (taskId, value) => bus.emit("progress:update", { taskId, progress: value }),
6071
6413
  onNotify: (taskId, list) => {
6072
6414
  const prefs = resolveNotificationSettings(getSettings());
@@ -6130,12 +6472,16 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6130
6472
  requireTask(key.split(":")[0]);
6131
6473
  return key;
6132
6474
  }
6475
+ function launchEffort(task) {
6476
+ const model = models.findModel(taskAgent(task), taskModel(task));
6477
+ return model?.efforts ? clampEffort(task.effort, model.efforts) : task.effort;
6478
+ }
6133
6479
  async function launchCommand(task, intent) {
6134
6480
  const settings = getSettings();
6135
6481
  const library = await libraryLaunchInfo(task.worktreePath);
6136
6482
  const boardCli = await boardCliLaunchInfo();
6137
6483
  return buildAgentCommand(
6138
- task,
6484
+ { ...task, effort: launchEffort(task) },
6139
6485
  settings.systemPrompt ?? "",
6140
6486
  {
6141
6487
  resume: intent.resume === true,
@@ -6149,13 +6495,13 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6149
6495
  }
6150
6496
  function claudeSessionFile(cwd, sessionId) {
6151
6497
  const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
6152
- return path21.join(os6.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
6498
+ return path22.join(os8.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
6153
6499
  }
6154
6500
  async function removeTaskWorktree(task) {
6155
6501
  if (!task.projectPath || !task.worktreePath) return;
6156
6502
  const branch = task.branch || fallbackBranchName(task.id);
6157
6503
  await removeWorktree(task.projectPath, task.worktreePath);
6158
- deleteArtifacts(task.workspacePath ?? path21.dirname(task.worktreePath), task.worktreePath);
6504
+ deleteArtifacts(task.workspacePath ?? path22.dirname(task.worktreePath), task.worktreePath);
6159
6505
  await deleteBranch(task.projectPath, branch);
6160
6506
  }
6161
6507
  function columnOf(taskId) {
@@ -6246,7 +6592,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6246
6592
  "env:detectAgents": () => detectAgents(),
6247
6593
  "agents:listModels": (payload) => {
6248
6594
  const { agentId: rawId, refresh } = obj(payload, "payload");
6249
- return listAgentModels(agentId(rawId), { refresh: refresh === true });
6595
+ const id = agentId(rawId);
6596
+ return refresh === true ? models.refresh(id) : models.get(id);
6250
6597
  },
6251
6598
  // A task does not exist yet when a project is picked, so these two are the
6252
6599
  // ones that must take a path. They only inspect or `git init` it.
@@ -6260,8 +6607,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6260
6607
  "projects:listRecent": () => listRecentProjects(getStore()),
6261
6608
  // `vibeflow open <path>` while a host runs: only the host writes the store.
6262
6609
  "projects:record": (projectPath) => {
6263
- const p = path21.resolve(str(projectPath, "projectPath"));
6264
- if (!fs17.existsSync(p) || !fs17.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
6610
+ const p = path22.resolve(str(projectPath, "projectPath"));
6611
+ if (!fs18.existsSync(p) || !fs18.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
6265
6612
  recordRecentProject(getStore(), p);
6266
6613
  return listRecentProjects(getStore());
6267
6614
  },
@@ -6312,7 +6659,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6312
6659
  if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
6313
6660
  await teardownTask(task.id);
6314
6661
  await resetWorktreeToBase(task.worktreePath, task.baseBranch);
6315
- const workspacePath = task.workspacePath ?? path21.dirname(task.worktreePath);
6662
+ const workspacePath = task.workspacePath ?? path22.dirname(task.worktreePath);
6316
6663
  deleteArtifacts(workspacePath, task.worktreePath);
6317
6664
  deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
6318
6665
  resetSubAgents(task.worktreePath);
@@ -6336,6 +6683,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6336
6683
  if (columnOf(existing.id) !== "backlog") {
6337
6684
  throw new Error("\u53EA\u6709 Backlog \u7684\u4EFB\u52D9\u53EF\u4EE5\u7DE8\u8F2F");
6338
6685
  }
6686
+ if (p.effort !== void 0 && !AGENT_EFFORTS.includes(p.effort)) invalid("unknown effort");
6339
6687
  let gitPatch = {};
6340
6688
  const nextProject = p.projectPath || existing.projectPath;
6341
6689
  const nextBranch = p.branch === void 0 ? "" : str(p.branch, "branch").trim();
@@ -6355,7 +6703,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6355
6703
  }
6356
6704
  const info = await getGitInfo(nextProject);
6357
6705
  if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
6358
- const projectName = path21.basename(nextProject);
6706
+ const projectName = path22.basename(nextProject);
6359
6707
  const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
6360
6708
  await teardownTask(existing.id);
6361
6709
  await removeTaskWorktree(existing);
@@ -6449,7 +6797,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6449
6797
  "claude:sessionExists": (taskId) => {
6450
6798
  const task = requireTask(taskId);
6451
6799
  if (!task.worktreePath) return false;
6452
- return fs17.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
6800
+ return fs18.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
6453
6801
  },
6454
6802
  "sessions:list": async () => {
6455
6803
  const keys = await sessions.list();
@@ -6496,7 +6844,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6496
6844
  const task = requireTask(taskId);
6497
6845
  if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
6498
6846
  const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
6499
- if (!fs17.existsSync(dir)) return "artifacts directory does not exist yet";
6847
+ if (!fs18.existsSync(dir)) return "artifacts directory does not exist yet";
6500
6848
  return getPlatform().openPath(dir);
6501
6849
  },
6502
6850
  "task:getSpecs": async (taskId) => {
@@ -6630,7 +6978,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6630
6978
  }
6631
6979
  await removeTaskWorktree(task);
6632
6980
  if (task.workspacePath) {
6633
- fs17.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
6981
+ fs18.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
6634
6982
  }
6635
6983
  clearConversation(task.id);
6636
6984
  removeTask(task.id);
@@ -6683,9 +7031,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6683
7031
  // Watch the containing directory, not the file: JsonStore renames a temp
6684
7032
  // file over the target and so replaces the inode on every save.
6685
7033
  watchStore() {
6686
- const file = path21.basename(getStorePath());
7034
+ const file = path22.basename(getStorePath());
6687
7035
  let debounce = null;
6688
- storeWatcher = fs17.watch(path21.dirname(getStorePath()), (_event, filename) => {
7036
+ storeWatcher = fs18.watch(path22.dirname(getStorePath()), (_event, filename) => {
6689
7037
  if (filename !== file) return;
6690
7038
  if (debounce) clearTimeout(debounce);
6691
7039
  debounce = setTimeout(() => {
@@ -6697,6 +7045,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6697
7045
  });
6698
7046
  return () => storeWatcher?.close();
6699
7047
  },
7048
+ warmModelCatalog() {
7049
+ models.warm();
7050
+ },
6700
7051
  shutdown() {
6701
7052
  sessions.shutdown();
6702
7053
  cancelAllChatSends();
@@ -6718,9 +7069,9 @@ function createSessionBackend(sink, choice) {
6718
7069
  }
6719
7070
 
6720
7071
  // packages/core/src/web-server.ts
6721
- import fs18 from "fs";
7072
+ import fs19 from "fs";
6722
7073
  import http from "http";
6723
- import path22 from "path";
7074
+ import path23 from "path";
6724
7075
  import { timingSafeEqual } from "crypto";
6725
7076
  import { WebSocketServer } from "ws";
6726
7077
  var TOKEN_COOKIE = "vf_token";
@@ -6773,14 +7124,14 @@ function resolveStatic(root, urlPath) {
6773
7124
  return null;
6774
7125
  }
6775
7126
  if (decoded.includes("\0")) return null;
6776
- const rel = path22.posix.normalize(decoded).replace(/^\/+/, "");
6777
- const base2 = path22.resolve(root);
6778
- const candidates = [rel, path22.posix.join(rel, "index.html"), `${rel}.html`];
7127
+ const rel = path23.posix.normalize(decoded).replace(/^\/+/, "");
7128
+ const base2 = path23.resolve(root);
7129
+ const candidates = [rel, path23.posix.join(rel, "index.html"), `${rel}.html`];
6779
7130
  for (const c of candidates) {
6780
- const full = path22.resolve(base2, c);
6781
- if (full !== base2 && !full.startsWith(base2 + path22.sep)) return null;
7131
+ const full = path23.resolve(base2, c);
7132
+ if (full !== base2 && !full.startsWith(base2 + path23.sep)) return null;
6782
7133
  try {
6783
- if (fs18.statSync(full).isFile()) return full;
7134
+ if (fs19.statSync(full).isFile()) return full;
6784
7135
  } catch {
6785
7136
  }
6786
7137
  }
@@ -6841,7 +7192,7 @@ async function startWebServer(options) {
6841
7192
  send(res, 404, "Not found");
6842
7193
  return;
6843
7194
  }
6844
- const ext = path22.extname(file).toLowerCase();
7195
+ const ext = path23.extname(file).toLowerCase();
6845
7196
  const immutable = url.pathname.startsWith("/_next/static/");
6846
7197
  res.writeHead(200, {
6847
7198
  "Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
@@ -6854,7 +7205,7 @@ async function startWebServer(options) {
6854
7205
  res.end();
6855
7206
  return;
6856
7207
  }
6857
- fs18.createReadStream(file).pipe(res);
7208
+ fs19.createReadStream(file).pipe(res);
6858
7209
  }
6859
7210
  const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
6860
7211
  function reject(socket, status, reason) {
@@ -7018,6 +7369,7 @@ async function startHost(options) {
7018
7369
  lock.port = server.port;
7019
7370
  updateLock(options.userDataDir, lock);
7020
7371
  const stopWatch = core.watchStore();
7372
+ core.warmModelCatalog();
7021
7373
  const stop = () => stopping ??= (async () => {
7022
7374
  stopWatch();
7023
7375
  core.shutdown();
@@ -7034,7 +7386,7 @@ function loginUrl(lock, pathname = "/home/") {
7034
7386
  return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
7035
7387
  }
7036
7388
  function isWebDir(dir) {
7037
- return fs19.existsSync(path23.join(dir, "home", "index.html"));
7389
+ return fs20.existsSync(path24.join(dir, "home", "index.html"));
7038
7390
  }
7039
7391
 
7040
7392
  // packages/cli/src/remote.ts
@@ -7151,14 +7503,14 @@ function connectToHost(lock) {
7151
7503
  }
7152
7504
 
7153
7505
  // packages/cli/src/doctor.ts
7154
- import fs20 from "fs";
7155
- import path24 from "path";
7156
- import { execFile as execFile9 } from "child_process";
7157
- import { promisify as promisify6 } from "util";
7506
+ import fs21 from "fs";
7507
+ import path25 from "path";
7508
+ import { execFile as execFile8 } from "child_process";
7509
+ import { promisify as promisify5 } from "util";
7158
7510
  init_env();
7159
7511
  init_git_bash();
7160
7512
  init_tmux_backend();
7161
- var pexec6 = promisify6(execFile9);
7513
+ var pexec5 = promisify5(execFile8);
7162
7514
  function parseVersion(text) {
7163
7515
  const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
7164
7516
  return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : [];
@@ -7172,7 +7524,7 @@ function versionAtLeast(have, want) {
7172
7524
  }
7173
7525
  async function run2(cmd, args) {
7174
7526
  try {
7175
- const { stdout } = await pexec6(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
7527
+ const { stdout } = await pexec5(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
7176
7528
  return String(stdout).trim();
7177
7529
  } catch {
7178
7530
  return null;
@@ -7292,10 +7644,10 @@ async function runDoctor(options) {
7292
7644
  });
7293
7645
  }
7294
7646
  try {
7295
- fs20.mkdirSync(options.userDataDir, { recursive: true });
7296
- const probe = path24.join(options.userDataDir, `.doctor-${process.pid}`);
7297
- fs20.writeFileSync(probe, "");
7298
- fs20.rmSync(probe);
7647
+ fs21.mkdirSync(options.userDataDir, { recursive: true });
7648
+ const probe = path25.join(options.userDataDir, `.doctor-${process.pid}`);
7649
+ fs21.writeFileSync(probe, "");
7650
+ fs21.rmSync(probe);
7299
7651
  checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
7300
7652
  } catch (err) {
7301
7653
  checks.push({
@@ -7349,24 +7701,24 @@ Options:
7349
7701
  `.trim();
7350
7702
  function detectLayout() {
7351
7703
  const file = fileURLToPath(import.meta.url);
7352
- const here = path27.dirname(file);
7353
- const bundledWeb = path27.join(here, "web");
7354
- if (isWebDir(bundledWeb) || path27.basename(here) === "dist") {
7704
+ const here = path28.dirname(file);
7705
+ const bundledWeb = path28.join(here, "web");
7706
+ if (isWebDir(bundledWeb) || path28.basename(here) === "dist") {
7355
7707
  return {
7356
- version: readVersion(path27.join(here, "..", "package.json")),
7708
+ version: readVersion(path28.join(here, "..", "package.json")),
7357
7709
  webDir: isWebDir(bundledWeb) ? bundledWeb : null,
7358
7710
  sourceRoot: null,
7359
7711
  cliEntry: file
7360
7712
  };
7361
7713
  }
7362
- const root = path27.resolve(here, "..", "..", "..");
7714
+ const root = path28.resolve(here, "..", "..", "..");
7363
7715
  const override = process.env.VIBEFLOW_WEB_DIR;
7364
- const webDir = override && isWebDir(override) ? override : isWebDir(path27.join(root, "app")) ? path27.join(root, "app") : null;
7365
- return { version: readVersion(path27.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
7716
+ const webDir = override && isWebDir(override) ? override : isWebDir(path28.join(root, "app")) ? path28.join(root, "app") : null;
7717
+ return { version: readVersion(path28.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
7366
7718
  }
7367
7719
  function readVersion(pkgPath) {
7368
7720
  try {
7369
- return JSON.parse(fs22.readFileSync(pkgPath, "utf8")).version;
7721
+ return JSON.parse(fs23.readFileSync(pkgPath, "utf8")).version;
7370
7722
  } catch {
7371
7723
  return "0.0.0";
7372
7724
  }
@@ -7572,8 +7924,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
7572
7924
  console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
7573
7925
  return 1;
7574
7926
  }
7575
- const dir = path27.resolve(target);
7576
- if (!fs22.existsSync(dir) || !fs22.statSync(dir).isDirectory()) {
7927
+ const dir = path28.resolve(target);
7928
+ if (!fs23.existsSync(dir) || !fs23.statSync(dir).isDirectory()) {
7577
7929
  console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
7578
7930
  return 1;
7579
7931
  }