@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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +3 -0
- package/dist/build-stamp.json +3 -3
- package/dist/executors/dag-pi-executor.js +74 -13
- package/dist/task/source-prepare/fragment-inventory.js +49 -16
- package/dist/task/source-prepare/prepare.js +37 -20
- package/dist/worker/console/chat/model-resolver.js +7 -4
- package/dist/worker/console/chat/pi-runtime.js +40 -1
- package/dist/worker/console/chat/routes.js +25 -24
- package/dist/worker/console/chat/sdd-data-alignment.js +6 -3
- package/dist/worker/console/pi-readiness.js +3 -2
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BJpsGgAv.js → abnfDiagram-N423BO3Z-BQBHX31n.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DjMFPsEd.js → arc-MSJ199eR.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DqdZlr1O.js → architectureDiagram-T3A2C74G-DiRq2qMX.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-BmZnfFEO.js → blockDiagram-VBNYF7ZC-rlaxFOvi.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-3cKberpL.js → c4Diagram-5PPSVZJV-BSpi9ump.js} +1 -1
- package/dist/worker/console/static/assets/channel-BdTaMnYP.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-q-0yjQ1h.js → chunk-2GRJ4B5K-BN8VNeh5.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DnONBaDW.js → chunk-2Q5K7J3B-CDa1OVRA.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-v3a6PCOS.js → chunk-5RXB4S5H-zxMQ5b8E.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-D1kLrlU_.js → chunk-5VM5RSS4-XTqNlzH4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BPelzihi.js → chunk-6Q2QTUOP-DpZQfFTq.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-DauNE69H.js → chunk-GF5L2VYU-DPv743zu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-RIg5_bFJ.js → chunk-JWPE2WC7-CFsCFoF4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BuILuoxs.js → chunk-KBJHAD2P-CV__aJTt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-VHORV5oW.js → chunk-RYQCIY6F-BshKlNir.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BJYr4xHC.js → chunk-XXDRQBXY-B-A3ErBP.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cid7CSRK.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cid7CSRK.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BS6nuqQn.js → cose-bilkent-JH36ORCC-25xj8928.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BBhF0NAT.js → cynefin-VYW2F7L2-DFmv38cl.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D1135Ifg.js → cynefinDiagram-MW4NZA55-hReuWDQP.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-vAOYPUWJ.js → dagre-VZM6K2ZE-C9X3YPUI.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DuaSPkfa.js → diagram-7IWD3JNH-CAVmqjg9.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-mMvXvGU6.js → diagram-B4RE2ZJO-DYNlLsIm.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CDIEJtlL.js → diagram-LBJQPF4R-BP5mlbfb.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Bi_6JvCW.js → diagram-Q27KOJAE-KWOHK5ZM.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-DAqj57yN.js → diagram-UB23O5K3-D25Ro5y-.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DfWaxNEo.js → ebnfDiagram-BXEA7PRR-kLaahtbh.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bt7QqkNG.js → erDiagram-JOGREHBK-1tgKuLez.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CRRhQwT2.js → flowDiagram-UKHOOZJN-DARr-Cvn.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DAV1O5B6.js → ganttDiagram-PKOTCBZU-BgwjBv_V.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-0X2Qo4Lp.js → gitGraphDiagram-DS77QQ5N-DneIlYSI.js} +1 -1
- package/dist/worker/console/static/assets/{index-CVdddQsA.js → index-Dkv0ezuE.js} +75 -77
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-C5W5eILC.js → infoDiagram-6WML65LV-DA7ATqY6.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-5SfqU-Tb.js → ishikawaDiagram-WSZJBQD7-DnOS-E1Z.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CKDRcaKf.js → journeyDiagram-NVQOT4AX-C8JnwJ_s.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Cc7_TuSb.js → kanban-definition-27J2QSJJ-Cw0_OjEC.js} +1 -1
- package/dist/worker/console/static/assets/{linear-BcQYYmeT.js → linear-mrxq3fyn.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-CwLZPtQo.js → mermaid.core-Bagus6zF.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D29M025z.js → mindmap-definition-FAOFIHXS-Bf95bLuW.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CV-ZAWYe.js → pegDiagram-VL7TDLO6-BvtUW6w2.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-C6iPS_o5.js → pieDiagram-7S7Q4E2Y-CbWtT-Ry.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-BFCD1sTg.js → quadrantDiagram-CIZ2JOQS-ctnxxIdD.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CztNj4O9.js → railroadDiagram-AXF67PYL-BZ7Z3X0-.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BWIELSNd.js → requirementDiagram-LRYGKXZP-BMX1xnIy.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CPtxrjJQ.js → sankeyDiagram-W5VNT64P-MShOQznz.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BEWVSFsu.js → sequenceDiagram-SI44F4Z6-CTGOazaB.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DyhzoX_J.js → sizeCapture-X5ZJPWSS-zGbfM4nV.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-xLox0nu7.js → stateDiagram-OKZ733FA-Brf8Wwzr.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CIkoculx.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CLC_ow9A.js → swimlanes-SLNWSIFB-CvNw3ZqF.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-pJ-V5EdT.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DmJZ4uyY.js → timeline-definition-Z64GVDOM-z2LyZpDt.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DD7GGi9Z.js → vennDiagram-T6HMQDX7-Bc_coprH.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-Bi8PQoGt.js → wardleyDiagram-T6FBY63Y-M1NSmWq-.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-uKdAY8Dp.js → xychartDiagram-ELKLHX3M-Ct0rjGXW.js} +1 -1
- package/dist/worker/console/static/index.html +1 -1
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
- package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
- package/dist/worker/console/static-src/shell/console-update-reload.js +36 -9
- package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
- package/dist/worker/observe/static/dag-helpers.js +4 -2
- package/dist/worker/observe/static/inspect-workspace.js +23 -0
- package/dist/worker/observe/static/kpi.js +2 -2
- package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
- package/dist/worker/observe/static/operator-chrome.js +37 -23
- package/dist/worker/observe/static/relations.js +2 -2
- package/dist/worker/observe/static/router.d.ts +12 -1
- package/dist/worker/observe/static/router.js +48 -6
- package/dist/worker/observe/static/run-processing.js +4 -2
- package/dist/worker/observe/static/shell-chrome.js +2 -2
- package/dist/worker/observe/static/state.js +5 -0
- package/dist/worker/observe/static/styles.css +95 -0
- package/dist/worker/observe/static/views/dag-inspector.js +14 -7
- package/dist/worker/observe/static/views/dag.js +10 -2
- package/dist/worker/observe/static/views/dags.js +3 -1
- package/dist/worker/observe/static/views/dashboard.js +14 -6
- package/dist/worker/observe/static/views/pool.js +7 -2
- package/dist/worker/observe/static/views/run.js +12 -3
- package/dist/worker/observe/static/views/session-timeline.js +69 -10
- package/dist/worker/observe/static/views/task.js +7 -2
- package/dist/workflows/dag/frontend-shadow-dual-write.js +33 -6
- package/dist/workflows/dag/init-hybrid.js +152 -24
- package/dist/workflows/dag/node-execution.js +1 -1
- package/dist/workflows/dag/types.js +1 -1
- package/package.json +3 -2
- package/dist/worker/console/static/assets/channel-DWYwciiv.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D6jOf_l4.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D6jOf_l4.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DS5ZRvJG.js +0 -1
- 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 可以在新窗口中直接完整加载,不再受源码摘要长度影响。
|
package/dist/build-stamp.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.42.0-next.
|
|
4
|
-
"gitSha": "
|
|
5
|
-
"builtAt": "2026-09-
|
|
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
|
-
|
|
4095
|
-
|
|
4096
|
-
//
|
|
4097
|
-
//
|
|
4098
|
-
|
|
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
|
-
/** 显式
|
|
20
|
-
* 与 checklist
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*
|
|
27
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
:
|
|
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
|
-
|
|
800
|
-
sourceIntegrity
|
|
801
|
-
|
|
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
|
|
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
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
path
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
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 {
|
|
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
|
-
//
|
|
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
|
-
|
|
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: {
|
|
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
|
|
207
|
-
if (!
|
|
206
|
+
const modelRegistry = options.context.modelRegistry;
|
|
207
|
+
if (!modelRegistry?.getApiKeyAndHeaders)
|
|
208
208
|
return;
|
|
209
|
-
|
|
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 =
|
|
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-
|
|
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-
|
|
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};
|