@tea-agent/loop-agent 0.42.0-next.6 → 0.42.0-next.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +3 -0
  3. package/dist/build-stamp.json +3 -3
  4. package/dist/executors/dag-pi-executor.js +74 -13
  5. package/dist/task/source-prepare/fragment-inventory.js +49 -16
  6. package/dist/task/source-prepare/prepare.js +37 -20
  7. package/dist/worker/console/chat/model-resolver.js +7 -4
  8. package/dist/worker/console/chat/pi-runtime.js +40 -1
  9. package/dist/worker/console/chat/routes.js +25 -24
  10. package/dist/worker/console/chat/sdd-data-alignment.js +6 -3
  11. package/dist/worker/console/pi-readiness.js +3 -2
  12. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BJpsGgAv.js → abnfDiagram-N423BO3Z-BQBHX31n.js} +1 -1
  13. package/dist/worker/console/static/assets/{arc-DjMFPsEd.js → arc-MSJ199eR.js} +1 -1
  14. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DqdZlr1O.js → architectureDiagram-T3A2C74G-DiRq2qMX.js} +1 -1
  15. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-BmZnfFEO.js → blockDiagram-VBNYF7ZC-rlaxFOvi.js} +1 -1
  16. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-3cKberpL.js → c4Diagram-5PPSVZJV-BSpi9ump.js} +1 -1
  17. package/dist/worker/console/static/assets/channel-BdTaMnYP.js +1 -0
  18. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-q-0yjQ1h.js → chunk-2GRJ4B5K-BN8VNeh5.js} +1 -1
  19. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DnONBaDW.js → chunk-2Q5K7J3B-CDa1OVRA.js} +1 -1
  20. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-v3a6PCOS.js → chunk-5RXB4S5H-zxMQ5b8E.js} +1 -1
  21. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-D1kLrlU_.js → chunk-5VM5RSS4-XTqNlzH4.js} +1 -1
  22. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BPelzihi.js → chunk-6Q2QTUOP-DpZQfFTq.js} +1 -1
  23. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-DauNE69H.js → chunk-GF5L2VYU-DPv743zu.js} +1 -1
  24. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-RIg5_bFJ.js → chunk-JWPE2WC7-CFsCFoF4.js} +1 -1
  25. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BuILuoxs.js → chunk-KBJHAD2P-CV__aJTt.js} +1 -1
  26. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-VHORV5oW.js → chunk-RYQCIY6F-BshKlNir.js} +1 -1
  27. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BJYr4xHC.js → chunk-XXDRQBXY-B-A3ErBP.js} +1 -1
  28. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cid7CSRK.js +1 -0
  29. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cid7CSRK.js +1 -0
  30. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BS6nuqQn.js → cose-bilkent-JH36ORCC-25xj8928.js} +1 -1
  31. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BBhF0NAT.js → cynefin-VYW2F7L2-DFmv38cl.js} +1 -1
  32. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D1135Ifg.js → cynefinDiagram-MW4NZA55-hReuWDQP.js} +1 -1
  33. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-vAOYPUWJ.js → dagre-VZM6K2ZE-C9X3YPUI.js} +1 -1
  34. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DuaSPkfa.js → diagram-7IWD3JNH-CAVmqjg9.js} +1 -1
  35. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-mMvXvGU6.js → diagram-B4RE2ZJO-DYNlLsIm.js} +1 -1
  36. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CDIEJtlL.js → diagram-LBJQPF4R-BP5mlbfb.js} +1 -1
  37. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Bi_6JvCW.js → diagram-Q27KOJAE-KWOHK5ZM.js} +1 -1
  38. package/dist/worker/console/static/assets/{diagram-UB23O5K3-DAqj57yN.js → diagram-UB23O5K3-D25Ro5y-.js} +1 -1
  39. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DfWaxNEo.js → ebnfDiagram-BXEA7PRR-kLaahtbh.js} +1 -1
  40. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bt7QqkNG.js → erDiagram-JOGREHBK-1tgKuLez.js} +1 -1
  41. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CRRhQwT2.js → flowDiagram-UKHOOZJN-DARr-Cvn.js} +1 -1
  42. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DAV1O5B6.js → ganttDiagram-PKOTCBZU-BgwjBv_V.js} +1 -1
  43. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-0X2Qo4Lp.js → gitGraphDiagram-DS77QQ5N-DneIlYSI.js} +1 -1
  44. package/dist/worker/console/static/assets/{index-CVdddQsA.js → index-Dkv0ezuE.js} +75 -77
  45. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-C5W5eILC.js → infoDiagram-6WML65LV-DA7ATqY6.js} +1 -1
  46. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-5SfqU-Tb.js → ishikawaDiagram-WSZJBQD7-DnOS-E1Z.js} +1 -1
  47. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CKDRcaKf.js → journeyDiagram-NVQOT4AX-C8JnwJ_s.js} +1 -1
  48. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Cc7_TuSb.js → kanban-definition-27J2QSJJ-Cw0_OjEC.js} +1 -1
  49. package/dist/worker/console/static/assets/{linear-BcQYYmeT.js → linear-mrxq3fyn.js} +1 -1
  50. package/dist/worker/console/static/assets/{mermaid.core-CwLZPtQo.js → mermaid.core-Bagus6zF.js} +5 -5
  51. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D29M025z.js → mindmap-definition-FAOFIHXS-Bf95bLuW.js} +1 -1
  52. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CV-ZAWYe.js → pegDiagram-VL7TDLO6-BvtUW6w2.js} +1 -1
  53. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-C6iPS_o5.js → pieDiagram-7S7Q4E2Y-CbWtT-Ry.js} +1 -1
  54. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-BFCD1sTg.js → quadrantDiagram-CIZ2JOQS-ctnxxIdD.js} +1 -1
  55. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CztNj4O9.js → railroadDiagram-AXF67PYL-BZ7Z3X0-.js} +1 -1
  56. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BWIELSNd.js → requirementDiagram-LRYGKXZP-BMX1xnIy.js} +1 -1
  57. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CPtxrjJQ.js → sankeyDiagram-W5VNT64P-MShOQznz.js} +1 -1
  58. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BEWVSFsu.js → sequenceDiagram-SI44F4Z6-CTGOazaB.js} +1 -1
  59. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DyhzoX_J.js → sizeCapture-X5ZJPWSS-zGbfM4nV.js} +1 -1
  60. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-xLox0nu7.js → stateDiagram-OKZ733FA-Brf8Wwzr.js} +1 -1
  61. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CIkoculx.js +1 -0
  62. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CLC_ow9A.js → swimlanes-SLNWSIFB-CvNw3ZqF.js} +2 -2
  63. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-pJ-V5EdT.js +8 -0
  64. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DmJZ4uyY.js → timeline-definition-Z64GVDOM-z2LyZpDt.js} +1 -1
  65. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DD7GGi9Z.js → vennDiagram-T6HMQDX7-Bc_coprH.js} +1 -1
  66. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-Bi8PQoGt.js → wardleyDiagram-T6FBY63Y-M1NSmWq-.js} +1 -1
  67. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-uKdAY8Dp.js → xychartDiagram-ELKLHX3M-Ct0rjGXW.js} +1 -1
  68. package/dist/worker/console/static/index.html +1 -1
  69. package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
  70. package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
  71. package/dist/worker/console/static-src/shell/console-update-reload.js +36 -9
  72. package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
  73. package/dist/worker/observe/static/dag-helpers.js +4 -2
  74. package/dist/worker/observe/static/inspect-workspace.js +23 -0
  75. package/dist/worker/observe/static/kpi.js +2 -2
  76. package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
  77. package/dist/worker/observe/static/operator-chrome.js +37 -23
  78. package/dist/worker/observe/static/relations.js +2 -2
  79. package/dist/worker/observe/static/router.d.ts +12 -1
  80. package/dist/worker/observe/static/router.js +48 -6
  81. package/dist/worker/observe/static/run-processing.js +4 -2
  82. package/dist/worker/observe/static/shell-chrome.js +2 -2
  83. package/dist/worker/observe/static/state.js +5 -0
  84. package/dist/worker/observe/static/styles.css +95 -0
  85. package/dist/worker/observe/static/views/dag-inspector.js +14 -7
  86. package/dist/worker/observe/static/views/dag.js +10 -2
  87. package/dist/worker/observe/static/views/dags.js +3 -1
  88. package/dist/worker/observe/static/views/dashboard.js +14 -6
  89. package/dist/worker/observe/static/views/pool.js +7 -2
  90. package/dist/worker/observe/static/views/run.js +12 -3
  91. package/dist/worker/observe/static/views/session-timeline.js +69 -10
  92. package/dist/worker/observe/static/views/task.js +7 -2
  93. package/dist/workflows/dag/frontend-shadow-dual-write.js +33 -6
  94. package/dist/workflows/dag/init-hybrid.js +152 -24
  95. package/dist/workflows/dag/node-execution.js +1 -1
  96. package/dist/workflows/dag/types.js +1 -1
  97. package/package.json +3 -2
  98. package/dist/worker/console/static/assets/channel-DWYwciiv.js +0 -1
  99. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D6jOf_l4.js +0 -1
  100. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D6jOf_l4.js +0 -1
  101. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DS5ZRvJG.js +0 -1
  102. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Dvpxn-bf.js +0 -8
package/AGENTS.md CHANGED
@@ -81,7 +81,7 @@ DAG 执行期间,主 agent 不得修改 writeSet 外的任何工作区文件
81
81
 
82
82
  权威源:`docs/governance/verification-matrix.md`。
83
83
 
84
- 默认节奏:编辑中只跑矩阵「最低验证」(typecheck + 定向 Vitest);`git commit` 的 `pre-commit` 只做 `check-repo`;`git push` 的 `pre-push` 经 `node scripts/pre-push-verify.mjs` 在 receipt 命中时复用、miss 时跑 full `bash scripts/ci.sh`(全量 typecheck + `npm test`)。每个 clone 安装一次 hooks:`bash scripts/install-git-hooks.sh`。
84
+ 默认节奏:编辑中只跑矩阵「最低验证」(typecheck + 定向 Vitest);常规 `npm test` 跳过约 6 分钟的 I/O-heavy 池(fast + integration);`git commit` 的 `pre-commit` 只做 `check-repo`;`git push` 的 `pre-push` 经 `node scripts/pre-push-verify.mjs` 在 receipt 命中时复用、miss 时跑 full `bash scripts/ci.sh`(全量 typecheck + 含 I/O-heavy 的 `npm run test:full`)。每个 clone 安装一次 hooks:`bash scripts/install-git-hooks.sh`。
85
85
 
86
86
  连续 source→target 交付遵循**单一最终树全量验证**:同一 Git tree、相同环境与命令合同在 receipt TTL 内只选一个本地 full authority(有效 receipt / target pre-push / 可选 `npm run verify:tree`),不按分支名策略化,也不机械双跑 full CI。细节见 verification-matrix 与 branch-merge-guideline。
87
87
 
package/CHANGELOG.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ - 修复前端 `--from-text` 任务缺少 source-fidelity ledger、DAG 生成后降级为 v1 并在 `frontend-contract-pi` 0ms 失败的问题:文本入口现在持久化可校验的 v2 绑定输入,init-hybrid 复用任务自有需求源;Console 默认模型与 Pi readiness 同时排除无凭据 provider,避免把不可执行模型误报为可用。
6
+ - 常规回归 `npm test` 默认跳过约 6 分钟的 I/O-heavy 测试池(仍跑 fast + integration);完整三池改为 `npm run test:full` / `LOOP_AGENT_FULL_TEST=1`。本地 `ci.sh`、GitHub PR 与 release/next 发布门禁仍跑全量,不降低交付验证。
5
7
  - 修复 backend-test correspondence 把前面函数的 `pytest.param(..., id="TP-...")` 叠进后续 Case `parameterIds` 的问题:decorator 区域遇到上一 `def`/`class` 或非 decorator 续行即停,不再把整组打成 `TEST_POINT_BINDING_EXTRA`。eligibility 门禁不放宽。
6
8
  - 修复 backend-test scenario facts 把内部 max/min/oversize helper 哨兵 `-1/-2/-3` 写成 `observedLength`、导致 N10 在 schema `min(0)` 上 `invalid-output` 的问题:这些 helper 仍按 Markdown bound MATCH,持久化长度只保留非负值。
7
9
  - 修复 backend-test 把 GET `params=`/`query=` 过滤字段、Python `True`/`False` 布尔字面量和混合大小写 `pytest.param` 列表漏出 correspondence/payload/scenario 观察的问题:这些 variant 现在可以按 item 授权,而不再把整 Case 打成 `PAYLOAD_SHAPE_MISMATCH` / `VARIANT_PARAMETER_MISSING`。eligibility 门禁不放宽。
@@ -14,6 +16,7 @@
14
16
  - 新建对话会优先复用当前工作区中真正的空白会话,避免重复创建;有历史消息、工具调用、待发送内容或正在运行的会话不会被复用。
15
17
  - 会话列表刷新现在会复用未变化的行和列表引用,标题、运行状态或队列变化时只更新对应会话,侧栏切换和刷新更平滑。
16
18
  - Operator Chat 新增仅对具备 Aimax 凭证的用户可见的 `/sdd-bingo` 数据对齐命令;`wizard-local` 模型请求会自动携带与上报一致的 OpenCode 风格会话身份、当前模型和最新 API key,请求头与资格文件变更均在下一次操作自动生效。
19
+ - DAG 节点检查器的「执行记录」默认自动跟随最新内容;右下角新增纯图标悬浮开关,可显式暂停并保留阅读位置,恢复后立即回到底部,悬停时显示当前可执行动作。
17
20
  - 修复前端任务产物派生续跑无法识别当前节点链的问题;从实现节点继续时,设计约束、写入准入、验证和收尾步骤会按现行流程安全重放。
18
21
  - 统一工作区、Todo 和其他滚动区域的滚动条样式,默认更轻巧,鼠标悬浮时也更容易辨认。
19
22
  - HTML 文件预览现在支持页面内的标签切换等交互;较大的自包含 HTML 可以在新窗口中直接完整加载,不再受源码摘要长度影响。
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.42.0-next.6",
4
- "gitSha": "6919b06fcb268f3a61890e85b6bc4dcbf11b62e1",
5
- "builtAt": "2026-09-02T06:01:45.078Z"
3
+ "version": "0.42.0-next.7",
4
+ "gitSha": "dc38c285736865703929d788b0f1929513ebe24c",
5
+ "builtAt": "2026-09-02T17:21:58.319Z"
6
6
  }
@@ -2019,6 +2019,41 @@ export async function createFrontendContractTools(input) {
2019
2019
  },
2020
2020
  };
2021
2021
  }
2022
+ async function resolveFrontendScoutSourceDeclaredPaths(input) {
2023
+ const binding = input.spec.sourceBinding;
2024
+ if (!binding?.sources?.length)
2025
+ return undefined;
2026
+ const declared = new Set();
2027
+ const pathToken = /(?:\.?\.?\/)?[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+\/?/g;
2028
+ const writerPaths = input.spec.tasks
2029
+ .filter((task) => task.executor === "pi" && task.toolProfile === "write")
2030
+ .flatMap((task) => task.allowedPaths ?? []);
2031
+ const prefixes = writerPaths
2032
+ .map((pattern) => pattern.replaceAll("\\", "/").replace(/^\.\//, ""))
2033
+ .map((pattern) => pattern.slice(0, pattern.search(/[!*?[{]/) >= 0 ? pattern.search(/[!*?[{]/) : pattern.length))
2034
+ .map((prefix) => prefix.slice(0, prefix.lastIndexOf("/") + 1))
2035
+ .filter(Boolean);
2036
+ for (const source of binding.sources) {
2037
+ try {
2038
+ const text = await readFile(path.resolve(input.cwd, source.path), "utf8");
2039
+ for (const match of text.matchAll(pathToken)) {
2040
+ const token = match[0].replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/$/, "");
2041
+ if (!token || token.startsWith("http") || token.startsWith("api/"))
2042
+ continue;
2043
+ declared.add(token);
2044
+ for (const prefix of prefixes) {
2045
+ if (!token.startsWith(prefix) && !token.startsWith(".harness/"))
2046
+ declared.add(`${prefix}${token}`);
2047
+ }
2048
+ }
2049
+ }
2050
+ catch {
2051
+ // The source binding itself remains the authority; an unreadable source
2052
+ // produces an empty declaration set, so future test paths fail closed.
2053
+ }
2054
+ }
2055
+ return [...declared].sort();
2056
+ }
2022
2057
  /**
2023
2058
  * A+B: `frontend-scout-pi` incremental evidence tools (origin=scout). Runtime
2024
2059
  * enriches committed target-surface / design-evidence facts with hash/section/
@@ -2043,6 +2078,12 @@ export async function createFrontendScoutEvidenceTools(input) {
2043
2078
  content: [{ type: "text", text: JSON.stringify(details) }],
2044
2079
  details,
2045
2080
  });
2081
+ const sourceDeclaredPaths = (input.sourceDeclaredPaths ?? []).map((value) => value.replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/$/, ""));
2082
+ const hasSourceDeclarations = input.sourceDeclaredPaths !== undefined;
2083
+ const isSourceDeclared = (candidate) => {
2084
+ const normalized = candidate.replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/$/, "");
2085
+ return sourceDeclaredPaths.some((declared) => declared === normalized || declared.startsWith(`${normalized}/`));
2086
+ };
2046
2087
  // A+B (AC-003): runtime enriches declared scout paths with hash/freshness
2047
2088
  // from the real filesystem. The model's self-reported path list is never
2048
2089
  // trusted for content identity; a missing file fails closed to fresh=false
@@ -2056,7 +2097,7 @@ export async function createFrontendScoutEvidenceTools(input) {
2056
2097
  const absolute = path.resolve(workspaceRoot, relative);
2057
2098
  if (absolute !== workspaceRoot &&
2058
2099
  !absolute.startsWith(`${workspaceRoot}${path.sep}`)) {
2059
- evidence.push({ path: relative, sha256: "0".repeat(64), fresh: false });
2100
+ evidence.push({ path: relative, sha256: "0".repeat(64), fresh: false, sourceDeclared: isSourceDeclared(relative) });
2060
2101
  continue;
2061
2102
  }
2062
2103
  try {
@@ -2073,6 +2114,7 @@ export async function createFrontendScoutEvidenceTools(input) {
2073
2114
  path: relative,
2074
2115
  sha256: createHash("sha256").update(`directory:${relative}`).digest("hex"),
2075
2116
  fresh: true,
2117
+ sourceDeclared: isSourceDeclared(relative),
2076
2118
  });
2077
2119
  continue;
2078
2120
  }
@@ -2081,10 +2123,11 @@ export async function createFrontendScoutEvidenceTools(input) {
2081
2123
  path: relative,
2082
2124
  sha256: createHash("sha256").update(bytes).digest("hex"),
2083
2125
  fresh: true,
2126
+ sourceDeclared: isSourceDeclared(relative),
2084
2127
  });
2085
2128
  }
2086
2129
  catch {
2087
- evidence.push({ path: relative, sha256: "0".repeat(64), fresh: false });
2130
+ evidence.push({ path: relative, sha256: "0".repeat(64), fresh: false, sourceDeclared: isSourceDeclared(relative) });
2088
2131
  }
2089
2132
  }
2090
2133
  return evidence;
@@ -2157,6 +2200,7 @@ export async function createFrontendScoutEvidenceTools(input) {
2157
2200
  dataSource: params?.dataSource ?? "",
2158
2201
  allowedPathConflicts: params?.allowedPathConflicts ?? [],
2159
2202
  unresolvedPaths: params?.unresolvedPaths ?? [],
2203
+ ...(hasSourceDeclarations ? { sourceDeclaredPaths } : {}),
2160
2204
  ...(pathEvidence.length > 0 ? { pathEvidence } : {}),
2161
2205
  });
2162
2206
  return receipt(result);
@@ -3798,6 +3842,10 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
3798
3842
  runDir: meta.runDir,
3799
3843
  nodeId: input.task.id,
3800
3844
  workspaceRoot: input.cwd,
3845
+ sourceDeclaredPaths: await resolveFrontendScoutSourceDeclaredPaths({
3846
+ cwd: input.cwd,
3847
+ spec: meta.spec,
3848
+ }),
3801
3849
  });
3802
3850
  writerToolPolicy = {
3803
3851
  requireSdk: true,
@@ -4091,24 +4139,37 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
4091
4139
  })),
4092
4140
  ];
4093
4141
  if (readBudgetIssues.length > 0) {
4094
- // Read-budget telemetry is diagnostic only when the provider/executor has
4095
- // already failed. In particular, keep context-overflow so node-execution
4096
- // selects its compact retry envelope instead of treating the attempt as a
4097
- // generic read-burst retry.
4098
- if (!mapped.ok) {
4142
+ const hasTypedFrontendTerminal = isFrontendReviewTypedTerminalNode(input.task) ||
4143
+ isFrontendDesignTypedTerminalNode(input.task);
4144
+ // A stale/generated DAG may still carry the legacy read budget. Once a
4145
+ // design/review node has a successful typed terminal, telemetry is
4146
+ // diagnostic only; the terminal shadow below still fails closed when the
4147
+ // fact is missing or conflicting.
4148
+ if (mapped.ok && hasTypedFrontendTerminal) {
4149
+ // Continue to the typed terminal shadow validation below.
4150
+ }
4151
+ else {
4152
+ // Read-budget telemetry is diagnostic only when the provider/executor
4153
+ // has already failed. In particular, keep context-overflow so
4154
+ // node-execution selects its compact retry envelope instead of treating
4155
+ // the attempt as a generic read-burst retry.
4156
+ if (!mapped.ok) {
4157
+ return {
4158
+ ...mapped,
4159
+ stderr: [mapped.stderr, ...readBudgetIssues]
4160
+ .filter(Boolean)
4161
+ .join("\n\n"),
4162
+ };
4163
+ }
4099
4164
  return {
4100
4165
  ...mapped,
4166
+ ok: false,
4167
+ failureCategory: "read-burst",
4101
4168
  stderr: [mapped.stderr, ...readBudgetIssues]
4102
4169
  .filter(Boolean)
4103
4170
  .join("\n\n"),
4104
4171
  };
4105
4172
  }
4106
- return {
4107
- ...mapped,
4108
- ok: false,
4109
- failureCategory: "read-burst",
4110
- stderr: [mapped.stderr, ...readBudgetIssues].filter(Boolean).join("\n\n"),
4111
- };
4112
4173
  }
4113
4174
  if (isFrontendReviewTypedTerminalNode(input.task)) {
4114
4175
  return await runFrontendReviewTerminalShadow({
@@ -16,17 +16,15 @@ const NON_GOAL_HEADING_RE = /(?:^|\s)(?:非目标|out of scope|non-goals|non goa
16
16
  const OBJECTIVE_HEADING_RE = /(?:^|\s)(?:目标|objective|问题与目标|业务目标|需求概述)(?:\s|$)/iu;
17
17
  /** 可执行片段启发式(AC 风格/祈使语气):命中则不可按 context-only 降级。 */
18
18
  const EXECUTABLE_HINT_RE = /(?:必须|应当|应支持|需要|需|不得|should|must|shall|will|require|support|implement|provide|ensure|verify|当.*?时|when.*?then)/iu;
19
- /** 显式 AC 式 ID 前缀:AC-001 / AC-FE-001 / AC-FE-INS-001 等(允许 bullet 标记后空格
20
- * 与 checklist 勾选框)。收紧解析(AC-001 / AC-HARD-001):AC 后必须跟分隔符
21
- * (-/_)+ 大写字母/数字 id 形态(含可选 FE/BE/UI/API 作用域前缀与 V4 `#### AC-*`
22
- * 标题归属);`## Acceptance Criteria` / `## Acceptance` / Ac 开头普通单词永不产生
23
- * 伪造 id(ACCEPTANCE 类 id 全量消失)。捕获组 1 = AC id,捕获组 2 = 余下文本。 */
24
- const EXPLICIT_AC_ID_RE = /^\s*(?:[-*+]|\d+[.)])\s+(?:\[[ xX]\]\s+)?(AC(?:[-_](?:FE|BE|UI|API))?[-_][A-Z0-9]+(?:[-_][A-Z0-9]+)*)\s*[::]?\s*(.*)$/iu;
25
- /** 显式 AC 标题式:`#### AC-FE-001 Save nickname` / `### AC-001:正文`。
26
- * AC 后必须跟分隔符(-/_)+ id 形态;普通单词(Acceptance Criteria / Acceptance)
27
- * 永不匹配,按 ACCEPTANCE_HEADING_RE 章节分类生成稳定 REQ-SRC-* 片段。
28
- * 捕获组 1 = AC id,捕获组 2 = 标题余下文本。 */
29
- const EXPLICIT_AC_HEADING_RE = /^\s*(AC(?:[-_](?:FE|BE|UI|API))?[-_][A-Z0-9]+(?:[-_][A-Z0-9]+)*)\s*[::]?\s*(.*)$/iu;
19
+ /** 显式 requirement ID:AC-001 / BR-001 / REQ-SRC-001 等(允许 bullet 标记后空格
20
+ * 与 checklist 勾选框)。AC/BR/REQ 都是 DAG 的合法 canonical requirement 身份;
21
+ * 之前这里只识别 AC,导致已进入 canonicalRequirements 的 BR 片段无法绑定来源。
22
+ * 捕获组 1 = requirement id,捕获组 2 = 余下文本。 */
23
+ const EXPLICIT_REQUIREMENT_ID_RE = /^\s*(?:(?:[-*+]|\d+[.)])\s+(?:\[[ xX]\]\s+)?)?((?:REQ|BR|AC)(?:[-_][A-Z0-9]+)+)\s*[::]?\s*(.*)$/iu;
24
+ /** 显式 requirement 标题式:`#### AC-FE-001 Save nickname` / `### BR-001:正文`。
25
+ * 普通单词(Acceptance Criteria / Acceptance)永不匹配,按章节分类生成稳定
26
+ * REQ-SRC-* 片段。捕获组 1 = requirement id,捕获组 2 = 标题余下文本。 */
27
+ const EXPLICIT_REQUIREMENT_HEADING_RE = /^\s*((?:REQ|BR|AC)(?:[-_][A-Z0-9]+)+)\s*[::]?\s*(.*)$/iu;
30
28
  /** Markdown 表格数据行(非分隔行)。 */
31
29
  const TABLE_DATA_ROW_RE = /^\s*\|.*\|\s*$/;
32
30
  const TABLE_SEPARATOR_ROW_RE = /^\s*\|[\s:|-]+\|\s*$/;
@@ -35,11 +33,11 @@ function isTableHeaderRow(line, nextLine) {
35
33
  return isTableDataRow(line) && TABLE_SEPARATOR_ROW_RE.test(nextLine.trim());
36
34
  }
37
35
  /** 表格行首格显式 AC ID:`| AC-001 | 订单列表加载完成 |` → id=AC-001, rest=订单列表加载完成。 */
38
- function matchTableRowAcId(line) {
36
+ function matchTableRowRequirementId(line) {
39
37
  const trimmed = line.trim().replace(/^\|/, "");
40
38
  const cells = trimmed.split("|");
41
39
  const first = cells[0]?.trim() ?? "";
42
- const idMatch = EXPLICIT_AC_HEADING_RE.exec(first);
40
+ const idMatch = EXPLICIT_REQUIREMENT_HEADING_RE.exec(first);
43
41
  if (!idMatch)
44
42
  return null;
45
43
  const id = (idMatch[1] ?? "").trim().toUpperCase().replace(/_/g, "-");
@@ -52,6 +50,14 @@ function matchTableRowAcId(line) {
52
50
  .trim();
53
51
  return { id, rest: rest || id };
54
52
  }
53
+ /** The splitter may use only AC ids as fragment identities; BR/REQ ids are
54
+ * mapped later while keeping the generated REQ-SRC fragment identity. */
55
+ function matchTableRowAcId(line) {
56
+ const match = matchTableRowRequirementId(line);
57
+ if (!match || !/^AC(?:-[A-Z0-9]+)+$/iu.test(match.id))
58
+ return null;
59
+ return match;
60
+ }
55
61
  export function sha256Text(text) {
56
62
  return createHash("sha256").update(text, "utf8").digest("hex");
57
63
  }
@@ -66,7 +72,20 @@ export function normalizeFragmentText(text) {
66
72
  .trim();
67
73
  }
68
74
  export function matchExplicitAcId(line) {
69
- const match = EXPLICIT_AC_ID_RE.exec(line.trim());
75
+ const match = EXPLICIT_REQUIREMENT_ID_RE.exec(line.trim());
76
+ if (!match)
77
+ return null;
78
+ const rawId = match[1] ?? "";
79
+ if (!/^AC(?:[-_][A-Z0-9]+)+$/iu.test(rawId))
80
+ return null;
81
+ const id = rawId.trim().toUpperCase().replace(/_/g, "-");
82
+ if (!id)
83
+ return null;
84
+ return { id, rest: (match[2] ?? "").trim() };
85
+ }
86
+ /** Match an explicit AC/BR/REQ id in a list item or a paragraph fragment. */
87
+ export function matchExplicitRequirementId(line) {
88
+ const match = EXPLICIT_REQUIREMENT_ID_RE.exec(line.trim());
70
89
  if (!match)
71
90
  return null;
72
91
  const rawId = match[1] ?? "";
@@ -77,7 +96,20 @@ export function matchExplicitAcId(line) {
77
96
  }
78
97
  /** V4 显式 AC 标题式匹配:`#### AC-FE-001 Save nickname` → id=AC-FE-001, rest=Save nickname。 */
79
98
  export function matchExplicitAcHeading(headingText) {
80
- const match = EXPLICIT_AC_HEADING_RE.exec(headingText.trim());
99
+ const match = EXPLICIT_REQUIREMENT_HEADING_RE.exec(headingText.trim());
100
+ if (!match)
101
+ return null;
102
+ const rawId = match[1] ?? "";
103
+ if (!/^AC(?:[-_][A-Z0-9]+)+$/iu.test(rawId))
104
+ return null;
105
+ const id = rawId.trim().toUpperCase().replace(/_/g, "-");
106
+ if (!id)
107
+ return null;
108
+ return { id, rest: (match[2] ?? "").trim() };
109
+ }
110
+ /** Match an explicit AC/BR/REQ id in a heading. */
111
+ export function matchExplicitRequirementHeading(headingText) {
112
+ const match = EXPLICIT_REQUIREMENT_HEADING_RE.exec(headingText.trim());
81
113
  if (!match)
82
114
  return null;
83
115
  const rawId = match[1] ?? "";
@@ -389,7 +421,8 @@ export function buildFragmentInventory(input) {
389
421
  : null;
390
422
  const explicit = explicitIdFromId
391
423
  ? { id: explicitIdFromId, rest: fragment.text }
392
- : matchExplicitAcId(fragment.text);
424
+ : matchExplicitRequirementId(fragment.text) ??
425
+ matchTableRowRequirementId(fragment.text);
393
426
  // V4 标题/条目下正文继承的显式 AC ID(不生成取代它的 REQ-SRC-*)。
394
427
  const inherited = !explicit && fragment.explicitAcId
395
428
  ? { id: fragment.explicitAcId, rest: fragment.text }
@@ -796,29 +796,46 @@ export async function prepareTaskSource(input) {
796
796
  // -------------------------------------------------------------------
797
797
  let ledger;
798
798
  let ledgerSummary;
799
- if (hasImportedPrd &&
800
- sourceIntegrity &&
801
- sourceIntegrity.parseableDocuments.some((doc) => doc.role === "requirement" || doc.role === "acceptance")) {
799
+ const hasAuthoritativeImportedDocs = Boolean(hasImportedPrd &&
800
+ sourceIntegrity?.parseableDocuments.some((doc) => doc.role === "requirement" || doc.role === "acceptance"));
801
+ // Frontend tasks created from text still need the same source-fidelity
802
+ // contract as imported-PRD tasks. In that case the projected requirement
803
+ // document is the canonical, task-owned source that the DAG can bind to;
804
+ // without it init-hybrid would silently emit a v1 binding and the first
805
+ // frontend-contract node would fail before making a model call.
806
+ if (built.draft.taskKind === "frontend-implementation" &&
807
+ (hasAuthoritativeImportedDocs ||
808
+ (input.intent.kind === "facts" && Boolean(input.intent.text)))) {
802
809
  try {
803
- const authoritativeDocs = sourceIntegrity.parseableDocuments.filter((doc) => (doc.role === "requirement" || doc.role === "acceptance") &&
810
+ const authoritativeDocs = sourceIntegrity?.parseableDocuments.filter((doc) => (doc.role === "requirement" || doc.role === "acceptance") &&
804
811
  typeof doc.content === "string" &&
805
- doc.content.trim().length > 0);
806
- const sourceDocuments = authoritativeDocs.map((doc) => {
807
- // Canonical source identity 单一边界(AC-001 / AC-FIDELITY-REPAIR-001):
808
- // ledger sourceDocuments[].path 与 DAG 侧 serializeDagTaskSourcePath /
809
- // resolveDagTaskSourcePath 一致(repo-root relative POSIX),路径转换只
810
- // 发生在该边界,不再二次转换进入 digest 计算。
811
- const absolute = path.resolve(contractPaths.sourceDir, doc.materializedPath);
812
- return {
813
- path: path
814
- .relative(repoRoot, absolute)
815
- .split(path.sep)
816
- .join("/"),
817
- content: doc.content,
818
- };
819
- });
812
+ doc.content.trim().length > 0) ?? [];
813
+ const requirementProjection = projectRequirementMarkdown(built.draft);
814
+ const sourceDocuments = authoritativeDocs.length > 0
815
+ ? authoritativeDocs.map((doc) => {
816
+ // Canonical source identity 单一边界(AC-001 / AC-FIDELITY-REPAIR-001):
817
+ // ledger sourceDocuments[].path 与 DAG 侧 serializeDagTaskSourcePath /
818
+ // resolveDagTaskSourcePath 一致(repo-root relative POSIX),路径转换只
819
+ // 发生在该边界,不再二次转换进入 digest 计算。
820
+ const absolute = path.resolve(contractPaths.sourceDir, doc.materializedPath);
821
+ return {
822
+ path: path
823
+ .relative(repoRoot, absolute)
824
+ .split(path.sep)
825
+ .join("/"),
826
+ content: doc.content,
827
+ };
828
+ })
829
+ : [
830
+ {
831
+ path: path
832
+ .relative(repoRoot, contractPaths.requirementPath)
833
+ .split(path.sep)
834
+ .join("/"),
835
+ content: requirementProjection,
836
+ },
837
+ ];
820
838
  if (sourceDocuments.length > 0) {
821
- const requirementProjection = projectRequirementMarkdown(built.draft);
822
839
  ledger = await buildRequirementLedger({
823
840
  taskId,
824
841
  requirementSha256: sha256Utf8(requirementProjection),
@@ -125,11 +125,14 @@ function resolveTierDescriptor(selection, available) {
125
125
  });
126
126
  return {
127
127
  provider: selection.provider,
128
- resolved: Boolean(entry),
128
+ resolved: Boolean(entry && entry.hasCredentials !== false),
129
129
  };
130
130
  }
131
131
  const entry = findProviderForModelId(selection.modelId, available);
132
- return { provider: entry?.provider, resolved: Boolean(entry) };
132
+ return {
133
+ provider: entry?.provider,
134
+ resolved: Boolean(entry && entry.hasCredentials !== false),
135
+ };
133
136
  }
134
137
  /**
135
138
  * Resolve harness executors.pi tiers (LOW/MED/HIGH) to provider descriptors.
@@ -196,7 +199,7 @@ export async function resolveDefaultChatModel(repoRoot, available, options) {
196
199
  const entry = findProviderForModelId(selection.modelId, available, {
197
200
  preferredProvider: selection.provider,
198
201
  });
199
- if (!entry)
202
+ if (!entry || entry.hasCredentials === false)
200
203
  return undefined;
201
204
  return {
202
205
  provider: selection.provider,
@@ -205,7 +208,7 @@ export async function resolveDefaultChatModel(repoRoot, available, options) {
205
208
  };
206
209
  }
207
210
  const entry = findProviderForModelId(selection.modelId, available);
208
- if (!entry)
211
+ if (!entry || entry.hasCredentials === false)
209
212
  return undefined;
210
213
  return {
211
214
  provider: entry.provider,
@@ -26,7 +26,7 @@
26
26
  * `createDefaultPiSdkBindings()`.
27
27
  */
28
28
  import { createHash, randomBytes } from "node:crypto";
29
- import { access, stat } from "node:fs/promises";
29
+ import { access, mkdir, rm, stat, writeFile } from "node:fs/promises";
30
30
  import path from "node:path";
31
31
  import { extractAssistantContentBlocks, extractAssistantText, isAssistantSdkMessage, } from "./assistant-content.js";
32
32
  import { parseProviderError, } from "./provider-error.js";
@@ -1331,6 +1331,15 @@ export class ConsolePiRuntime {
1331
1331
  if (!sessionFile) {
1332
1332
  throw new Error(`chat session shell missing sessionFile for ${sessionId}`);
1333
1333
  }
1334
+ // Pi's SessionManager.create deliberately postpones writing a JSONL until
1335
+ // an assistant response. Materialize a valid header now, otherwise Console
1336
+ // could persist a path that disappears on a restart before first reply.
1337
+ await this.bindings.materializeSessionShell({
1338
+ sessionManager,
1339
+ sessionId,
1340
+ sessionFile,
1341
+ cwd: this.options.cwd,
1342
+ });
1334
1343
  this.sessionManagers.set(sessionId, sessionManager);
1335
1344
  this.thinkingSelections.set(sessionId, "auto");
1336
1345
  // Detached single-flight materialization; dependent routes await the same promise.
@@ -3173,6 +3182,36 @@ export function createDefaultPiSdkBindings() {
3173
3182
  id: opts.sessionId,
3174
3183
  });
3175
3184
  },
3185
+ materializeSessionShell: async (opts) => {
3186
+ const sdk = (await loadSdk());
3187
+ const manager = opts.sessionManager;
3188
+ if (typeof manager.setSessionFile !== "function") {
3189
+ throw new Error("Pi SessionManager cannot materialize a session file");
3190
+ }
3191
+ let created = false;
3192
+ try {
3193
+ await mkdir(path.dirname(opts.sessionFile), { recursive: true });
3194
+ // Match Pi's documented session header. Rebinding through the public
3195
+ // SessionManager API marks it flushed, so Pi appends the first user
3196
+ // entry instead of trying to create the already-present file later.
3197
+ await writeFile(opts.sessionFile, `${JSON.stringify({
3198
+ type: "session",
3199
+ version: typeof sdk.CURRENT_SESSION_VERSION === "number"
3200
+ ? sdk.CURRENT_SESSION_VERSION
3201
+ : 3,
3202
+ id: opts.sessionId,
3203
+ timestamp: new Date().toISOString(),
3204
+ cwd: opts.cwd,
3205
+ })}\n`, { encoding: "utf8", flag: "wx", mode: 0o600 });
3206
+ created = true;
3207
+ manager.setSessionFile(opts.sessionFile);
3208
+ }
3209
+ catch (error) {
3210
+ if (created)
3211
+ await rm(opts.sessionFile, { force: true }).catch(() => undefined);
3212
+ throw error;
3213
+ }
3214
+ },
3176
3215
  createInMemorySessionManager: async (opts) => {
3177
3216
  const sdk = (await loadSdk());
3178
3217
  return sdk.SessionManager.inMemory(opts.cwd);
@@ -561,9 +561,15 @@ workspaceId) {
561
561
  });
562
562
  return;
563
563
  }
564
+ let createdSessionId;
565
+ let persisted = false;
564
566
  try {
565
567
  const snapshot = snapshotInput === undefined ? undefined : projectCompactSnapshot(snapshotInput);
566
- // Fast path: durable shell + Console record before full AgentSession ready.
568
+ // A Console record promises that its Pi JSONL can be reopened after a
569
+ // Console restart. Do not make that promise until Pi has finished
570
+ // materializing the session: the old fast path wrote a record for a
571
+ // future JSONL path, leaving an unrecoverable orphan when init failed or
572
+ // the process restarted in that window.
567
573
  const handle = await deps.runtime.createSession({
568
574
  ...(provider && modelId ? { model: { provider, modelId } } : {}),
569
575
  ...(snapshot
@@ -583,40 +589,26 @@ workspaceId) {
583
589
  });
584
590
  return;
585
591
  }
592
+ createdSessionId = handle.sessionId;
593
+ await deps.runtime.ensureSessionReady(handle.sessionId);
594
+ const selection = deps.runtime.getRuntimeSelection(handle.sessionId);
586
595
  const session = await deps.store.create({
587
596
  sessionId: handle.sessionId,
588
597
  sessionFile: handle.sessionFile,
589
- modelProvider: handle.model?.provider,
590
- modelId: handle.model?.modelId,
598
+ modelProvider: selection.model?.provider ?? handle.model?.provider,
599
+ modelId: selection.model?.modelId ?? handle.model?.modelId,
591
600
  // AC-R7-002: explicit parameter from the dispatcher — the runtime
592
601
  // store.create wrapper is gone; body workspaceId (if any) only acts as
593
602
  // a fallback for direct (non-dispatcher) handler tests.
594
603
  workspaceId: workspaceId ?? (typeof body.workspaceId === "string" && body.workspaceId ? body.workspaceId : undefined),
595
604
  });
605
+ persisted = true;
596
606
  await deps.attachSession?.(handle.sessionId).catch(() => undefined);
597
607
  if (snapshot)
598
608
  deps.events.append(handle.sessionId, `${handle.sessionId}:snapshot`, {
599
609
  kind: "compact",
600
610
  data: snapshot,
601
611
  });
602
- // Best-effort: after materialization, patch resolved default model onto the
603
- // durable record. Failures stay on the init fence (PI_SESSION_INIT_FAILED).
604
- if (typeof deps.runtime.ensureSessionReady === "function") {
605
- void deps.runtime
606
- .ensureSessionReady(handle.sessionId)
607
- .then(async () => {
608
- if (typeof deps.runtime.getRuntimeSelection !== "function")
609
- return;
610
- const selection = deps.runtime.getRuntimeSelection(handle.sessionId);
611
- if (!selection?.model)
612
- return;
613
- await deps.store.setRuntimeSelection(handle.sessionId, {
614
- modelProvider: selection.model.provider,
615
- modelId: selection.model.modelId,
616
- });
617
- })
618
- .catch(() => undefined);
619
- }
620
612
  sendJson(res, 201, {
621
613
  ok: true,
622
614
  sessionId: handle.sessionId,
@@ -625,14 +617,23 @@ workspaceId) {
625
617
  session: { ...session, isBlank: isBlankChatSession(session) },
626
618
  activeTools: handle.activeTools,
627
619
  hasBash: true,
628
- ...(handle.initializing ? { initializing: true } : {}),
629
620
  });
630
621
  }
631
622
  catch (error) {
623
+ // Before store.create succeeds, this handle has no durable owner. Dispose
624
+ // it so a failed create cannot survive in-memory until the next restart.
625
+ if (createdSessionId && !persisted)
626
+ deps.runtime.dispose(createdSessionId);
632
627
  const message = error instanceof Error ? error.message : String(error);
633
- sendJson(res, 500, {
628
+ const code = error && typeof error === "object" && "code" in error
629
+ ? String(error.code ?? "")
630
+ : "";
631
+ sendJson(res, code === "PI_SESSION_INIT_FAILED" ? 503 : 500, {
634
632
  ok: false,
635
- error: { code: "CHAT_SESSION_CREATE_FAILED", message },
633
+ error: {
634
+ code: code === "PI_SESSION_INIT_FAILED" ? code : "CHAT_SESSION_CREATE_FAILED",
635
+ message,
636
+ },
636
637
  });
637
638
  }
638
639
  }
@@ -203,10 +203,13 @@ export async function injectWizardLocalHeaders(options) {
203
203
  return;
204
204
  if (!(await options.service.isEligible()))
205
205
  return;
206
- const getAuth = options.context.modelRegistry?.getApiKeyAndHeaders;
207
- if (!getAuth)
206
+ const modelRegistry = options.context.modelRegistry;
207
+ if (!modelRegistry?.getApiKeyAndHeaders)
208
208
  return;
209
- const auth = await getAuth(model);
209
+ // Keep the method call attached to ModelRegistry. Its implementation reads
210
+ // the private runtime through `this`; extracting the function would make the
211
+ // SDK path fail closed and silently drop all custom headers.
212
+ const auth = await modelRegistry.getApiKeyAndHeaders(model);
210
213
  if (!auth.ok || typeof auth.apiKey !== "string" || !auth.apiKey)
211
214
  return;
212
215
  setStaticHeader(options.headers, auth.headers, "client-code", "OpenCode");
@@ -170,11 +170,12 @@ export async function discoverPiReadinessProbe(options) {
170
170
  const availableCount = countAvailableModels(snapshot);
171
171
  const providerConfigured = hasProviderConfig(snapshot);
172
172
  const availableModels = mapSnapshotAvailableModels(snapshot);
173
+ const credentialedAvailableCount = availableModels.filter((model) => model.hasCredentials !== false).length;
173
174
  const harnessMatrix = await resolveHarnessPiExecutorMatrix(repoRoot, availableModels);
174
175
  const resolvedHarnessModels = [];
175
176
  if (typeof runtime.getModel === "function") {
176
177
  for (const row of harnessMatrix) {
177
- if (!row.provider)
178
+ if (!row.provider || !row.resolved)
178
179
  continue;
179
180
  try {
180
181
  if (runtime.getModel(row.provider, row.modelId)) {
@@ -195,7 +196,7 @@ export async function discoverPiReadinessProbe(options) {
195
196
  .join(",")
196
197
  : "none";
197
198
  const authReady = availableCount > 0 || providerConfigured;
198
- const modelsAvailable = availableCount > 0 || resolvedHarnessModels.length > 0;
199
+ const modelsAvailable = credentialedAvailableCount > 0 || resolvedHarnessModels.length > 0;
199
200
  let interviewSessionOk = false;
200
201
  let sessionMessage;
201
202
  if (!authReady || !modelsAvailable) {
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-BPelzihi.js";import{p as f}from"./chunk-JWPE2WC7-RIg5_bFJ.js";import{_ as n,l as o}from"./mermaid.core-CwLZPtQo.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-BBhF0NAT.js";import"./index-CVdddQsA.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
1
+ import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-DpZQfFTq.js";import{p as f}from"./chunk-JWPE2WC7-CFsCFoF4.js";import{_ as n,l as o}from"./mermaid.core-Bagus6zF.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-DFmv38cl.js";import"./index-Dkv0ezuE.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
@@ -1 +1 @@
1
- import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-CwLZPtQo.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-Bagus6zF.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};