@tea-agent/loop-agent 0.39.0-beta.12 → 0.39.0-beta.14
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/CHANGELOG.md +27 -0
- package/dist/application/dag/generate-task-dag.js +6 -2
- package/dist/application/task-lifecycle/advance.js +20 -4
- package/dist/application/task-lifecycle/observe.js +171 -17
- package/dist/application/task-lifecycle/plan-transitions.js +42 -7
- package/dist/build-stamp.json +3 -3
- package/dist/commands/client-recovery.js +8 -36
- package/dist/executors/dag-pi-executor.js +636 -109
- package/dist/executors/pi-executor.js +8 -4
- package/dist/executors/pi-sdk-executor.js +33 -5
- package/dist/executors/shell-executor.js +102 -32
- package/dist/governance/checks.js +1 -0
- package/dist/shared/pi-context-pressure/checkpoint.js +116 -0
- package/dist/shared/pi-context-pressure/compaction-policy.js +151 -0
- package/dist/shared/pi-context-pressure/env.js +58 -0
- package/dist/shared/pi-context-pressure/extension.js +100 -0
- package/dist/shared/pi-context-pressure/index.js +7 -0
- package/dist/shared/pi-context-pressure/overflow.js +252 -0
- package/dist/shared/pi-context-pressure/sift-bridge.js +386 -0
- package/dist/shared/pi-context-pressure/telemetry.js +51 -0
- package/dist/task/frontend-project-capability.js +3 -1
- package/dist/task/source-prepare/fragment-inventory.js +4 -1
- package/dist/worker/console/chat/pi-runtime.js +146 -5
- package/dist/worker/console/chat/provider-error.js +2 -1
- package/dist/worker/console/chat/routes.js +3 -0
- package/dist/worker/console/chat/sift-bridge.js +1 -0
- package/dist/worker/console/dag-execution-receipt.js +20 -2
- package/dist/worker/console/operator-actions.js +4 -3
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CXj_GnSb.js → abnfDiagram-N423BO3Z-DC863mud.js} +1 -1
- package/dist/worker/console/static/assets/{arc-BZp6JAp7.js → arc-CftC38G9.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DfpcEuYU.js → architectureDiagram-T3A2C74G-B3PAPQCw.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-_Gf0xadb.js → blockDiagram-VBNYF7ZC-C9UDJNRv.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Ct2QPCmv.js → c4Diagram-5PPSVZJV--BJ76fv_.js} +1 -1
- package/dist/worker/console/static/assets/channel-CUz-Bg86.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-BfklkzKl.js → chunk-2GRJ4B5K--hiIqoGp.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-Dy25vZJV.js → chunk-2Q5K7J3B-DgTzpIa3.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BFlCRZep.js → chunk-5RXB4S5H-DIwpJziP.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-op3oVxIE.js → chunk-5VM5RSS4-BJzbdUi0.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-C0R7rzF2.js → chunk-6Q2QTUOP-BbGouI1Z.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bt44TCGy.js → chunk-GF5L2VYU-B9247w69.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-_uEE_XFx.js → chunk-JWPE2WC7-CXob_wYy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-C3TOYGZ9.js → chunk-KBJHAD2P-C6FUel1g.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-Cz60oBPV.js → chunk-RYQCIY6F-DGKRYKHu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-8Bik0qis.js → chunk-XXDRQBXY-kNBqNPfx.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BhlUacmD.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BhlUacmD.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-C2QIOA4a.js → cose-bilkent-JH36ORCC--xmkDwfD.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CSH_yUUd.js → cynefin-VYW2F7L2-Cw5FMMuG.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BDLw2XFM.js → cynefinDiagram-MW4NZA55-CuLslt2t.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CcD9ZtF4.js → dagre-VZM6K2ZE-D9ngqrs2.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DqwtkBsI.js → diagram-7IWD3JNH-RDRSbHQp.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BWVEcqsC.js → diagram-B4RE2ZJO-BgQ1P9aV.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-M-WAbtQi.js → diagram-LBJQPF4R-D3WWyPtn.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DKW7SixP.js → diagram-Q27KOJAE-L2k28wTR.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-PHHPPtrj.js → diagram-UB23O5K3-DDNrA5Qw.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BQD-B4RX.js → ebnfDiagram-BXEA7PRR-BaRFCOdM.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BFvULb51.js → erDiagram-JOGREHBK-CM33DVVM.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Bw-aXTCb.js → flowDiagram-UKHOOZJN-CVBSjOxe.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BGKw04Qx.js → ganttDiagram-PKOTCBZU-BdscewE_.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-RqKaPR-I.js → gitGraphDiagram-DS77QQ5N-HRuSZb7g.js} +1 -1
- package/dist/worker/console/static/assets/{index-B_D8rbWc.js → index-B9JJQsVK.js} +102 -72
- package/dist/worker/console/static/assets/index-rWaGv4jz.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-YO5dnzrJ.js → infoDiagram-6WML65LV-cYfHvfAR.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-Bi1VZHMq.js → ishikawaDiagram-WSZJBQD7-CZmaoOuy.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Bszls1DD.js → journeyDiagram-NVQOT4AX-ntYijh7g.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-PhzeaZ09.js → kanban-definition-27J2QSJJ-Cz3b0DeD.js} +1 -1
- package/dist/worker/console/static/assets/{linear-BsjbDoXi.js → linear-DvGonpsP.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-0B7NnWKk.js → mermaid.core-B-3vjfyW.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BJr4Fj-q.js → mindmap-definition-FAOFIHXS-5iKxlsX3.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-moC4fpGB.js → pegDiagram-VL7TDLO6-C8BUwapW.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BEw37-2c.js → pieDiagram-7S7Q4E2Y-B2rPoQQG.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Cq6LyasU.js → quadrantDiagram-CIZ2JOQS-jDeVUAy4.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DUCMcK0D.js → railroadDiagram-AXF67PYL-DV140Dwm.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C3upTZm7.js → requirementDiagram-LRYGKXZP-CnYgHtYC.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BI_gMsCW.js → sankeyDiagram-W5VNT64P-SIK3CdWw.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-YFOIRzfN.js → sequenceDiagram-SI44F4Z6-DW_vZix7.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-dOnB7UDD.js → sizeCapture-X5ZJPWSS-NBAtkg0C.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BXUniaIh.js → stateDiagram-OKZ733FA-Bi1bQxpi.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DjWKYjQ1.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-2tA4wTNu.js → swimlanes-SLNWSIFB-8PT_uP_i.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B4cdm7bH.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DO0HkJXC.js → timeline-definition-Z64GVDOM-CD0ZZPk0.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DvIOzixv.js → vennDiagram-T6HMQDX7-BbEgWdhK.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DXDZ0cTj.js → wardleyDiagram-T6FBY63Y-DCwPKdLl.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-B32Ark0D.js → xychartDiagram-ELKLHX3M-sfC5PcNg.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/observe/static/styles.css +9 -0
- package/dist/worker/observe/static/views/dag-inspector.js +40 -0
- package/dist/worker/observe/static/views/session-timeline.js +135 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +157 -7
- package/dist/workflows/dag/backend-test-pytest-collection.js +70 -2
- package/dist/workflows/dag/backend-test-result-contract.js +4 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +339 -53
- package/dist/workflows/dag/backend-test-writer-completeness.js +11 -0
- package/dist/workflows/dag/dag-retry-schema.js +138 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +174 -31
- package/dist/workflows/dag/frontend-review-context.js +12 -1
- package/dist/workflows/dag/frontend-shadow-dual-write.js +59 -13
- package/dist/workflows/dag/frontend-writer-admission.js +13 -0
- package/dist/workflows/dag/init-hybrid.js +8 -3
- package/dist/workflows/dag/node-execution.js +277 -3
- package/dist/workflows/dag/rerun-feedback.js +135 -3
- package/dist/workflows/dag/retry-policy.js +13 -122
- package/dist/workflows/dag/types.js +11 -4
- package/docs/architecture/runtime-boundaries.md +2 -1
- package/docs/templates/backend-test-dag.json +4 -3
- package/harness.json +3 -3
- package/package.json +4 -3
- package/dist/worker/console/static/assets/channel-3TxJgYaH.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BHIkXpp3.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BHIkXpp3.js +0 -1
- package/dist/worker/console/static/assets/index-BdNx6fj0.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cdi6UhLa.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D-RJBbb0.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
|
+
- 0.39.0-beta.14(frontend-node dogfood):plan-pi 三段分段会话 + coverage 需求批次切片(4/批、零进度减半)、finalize 收据修复环(聚合诊断+修复 JSON)、record_* 边界校验左移(VT 重复/writeSet/交互 VT 引用/规范需求 id/可疑 symbol)、编译装配 last-wins(组件按 purpose 去重修正)、review-context 授权面扩展至 VT 测试文件、设计评审修订降级为 admission advisory。
|
|
5
|
+
- 前端 DAG 极端环境加固(16KB 输出 / 120KB 上下文真实冒烟,r2-r5 验证):`frontend-contract-pi` 改为消费编译后的 ledger 输入块(`<frontend_contract_input>`,12KB 上限、cap ladder、requirement id 永不丢)并移除 read 工具,以增量 `record_*` 提交为唯一输出模式(OUTPUT BUDGET DISCIPLINE),修复极端小输出窗口下模型反复 read 源文件空转 1h54m 超时的问题(r3 起 contract 3m54s 正常完成、37 次 record_* / 0 次 read);plan/contract 的 `record_*` 工具支持每 message 最多 5 条批量提交并加 1s 调用间隔,rate-limit 退避上限提高到 60s,缓解密集小调用触发第三方网关限流;修复 fragment-inventory 对 V4 标题式 AC(`#### AC-NNN`)的双重 emit 缺陷(同一标题行被 section 入口与正文 heading 分支各切一次,30 个 AC 结构性产生 30 个 duplicate)。
|
|
6
|
+
|
|
7
|
+
- 修复连续 `dag rerun-task` 丢失前代评审意见的问题:直接父 run 未走到 review/design review 时,`deriveDagRerunFeedback` 沿 run-owned lineage 有界追溯最近 5 层并继承最近的 committed typed findings;较近的 `approve_review` / `approve_design` 作为对应 resolution barrier,防止已解决 finding 被复活。lineage run ID、生命周期目录与 `state.json.runId` 必须一致,损坏、越界、循环或超深链路 fail closed。
|
|
8
|
+
|
|
9
|
+
- 修复前端 review 修订无法稳定收敛的问题:`decision=new` 的组件选型除关联 `sourceRequirementIds` 外,必须显式选择该需求绑定的冻结 `sourceFragmentId`;runtime 校验隶属关系后物化 `specReference`,并覆盖模型提交的任意路径/章节/行号,避免同一需求含多个来源片段时引用错误章节。同步明确 `purpose` 是与 interaction/UI state 对齐的稳定覆盖键,组件职责由 `expectedBehavior` 与 rationale 表达,review 不再仅因标识符相同而误判。
|
|
4
10
|
|
|
5
11
|
- 收敛前端 `frontend-plan-pi` 的职责与上下文:Plan 只把 Contract/Scout 的冻结事实映射为 typed decision patch(需求→实现/验证、组件/状态/Mock/依赖与真实证据缺口),不再重复任务源、完整 JSON Schema、受保护 skeleton、逐 AC 回显模板或 bad/good 教学示例。Schema、字段别名、路径与命令校验继续由 typed tools 和 deterministic runtime 持有;Plan 的同名 `record_target_surface` 改为只接受 routes 的 `record_route_selection`,文件归属仍只来自 Scout/runtime。初始 prompt 不再教学别名,validator 失败重试才附加对应 canonical 字段指引;保留冻结验证命令、Mock 约束、字段摘要和组件选型约束,并新增回归断言防止大段 Schema 回注。
|
|
6
12
|
|
|
@@ -26,10 +32,19 @@
|
|
|
26
32
|
- 前端 review findings PRD 草稿携带修复范围路由信号(方向 A 补充):`deriveFrontendReviewFixScope` 从 committed findings 确定性推导建议 shape(单文件无远程 API→micro、同目录集中多文件→small、跨目录/API 信号→standard)与建议 complexity、目标文件、建议 allowedPaths;PRD 标题改为「修复 review 发现(<shape>):<目标文件>」,正文新增「修复范围(供下一轮任务生成路由)」段(含 small 信号词,如「单文件局部修复」「无远程接口」),下一轮任务以该 PRD 生成 DAG 时,`resolveFrontendTaskShape` 能把小修复路由到 micro=7/small=10 节点拓扑,而非默认 11 节点 standard 全流程。
|
|
27
33
|
- 前端 shape 分类 API 否定词覆盖中文空格形式(冒烟暴露边界):`无 API/网络调用`、`无 API` 等表述不再被误判为远程 API 信号(原 `\b(...)\b` 对 CJK 开头无词边界),`resolveFrontendTaskShape` 与 `deriveFrontendReviewFixScope` 的 hasApiSignal 判定同步修正——带「无 API/无接口」声明的单文件小修复正确路由 micro/small,而非误升级 standard。
|
|
28
34
|
- 深度合入 origin/main(source e68dfbe1):以 frontend-node typed-tools 拓扑为基底移植 main 硬化——canonical artifact 消费、fail-closed 无验证 marker、风险分级细化(DEPENDENCY_INSTALL_LANGUAGE)、`HARNESS_DAG_RERUN_PROMPT_OVERRIDE` prompt override、Pi SDK 运行时依赖 0.83.0 与 `engines node>=22.19.0`、next-release scripts 入包;废弃节点(verify-assess/reverify/repair-pi/plan-revision/final-design-review/lint-baseline shell)能力确认被当前流程覆盖后裁剪对应实现(repair-gate 机制、lint-baseline shell 类型),plan retry 文案对齐 committed ledger;affected 392 + M2-M9 303 测试全绿,fast 3909/3910、integration 397/398(各 1 个 fn 既有失败)。
|
|
35
|
+
- 修复 Console 轨迹视图引用未声明 CSS 自定义属性导致仓库发布门禁失败的问题,补齐品牌色、警告色与字体别名并保持现有视觉回退值。
|
|
29
36
|
|
|
30
37
|
### 重点更新
|
|
31
38
|
|
|
39
|
+
- Console 对话页「轨迹」视图响应式优化:轨迹列宽跟随面板实际宽度收缩(`.oc-trajectory` 加 `width:100%`、`.oc-trajectory-turn/.step` 加 `min-width:0` 修 flex 收缩链),窄窗口(≤720px)自动隐藏耗时列,避免工具行被固定项挤爆。
|
|
40
|
+
- Console 对话页新增「轨迹」视图(详情侧栏 tab):仿 DSH ui-trajectory,用本仓库 turnGroups 数据把对话按「用户 → 过程 → AI 回复」渲染成统一单元格列表,每行带 kind 标签 + 图标(用户/ AI / 思考 / 工具 / 活动 / 错误 / 压缩);用户与 AI 用标签颜色 + 图标区分,AI 中间文本也标注;思考、工具、以及超长用户/AI 消息默认单行截断、可展开/收起看全文(含工具参数与结果、状态与耗时)。支持一键「导出 HTML」自包含文件(长内容用 `<details>` 折叠,短内容内联)。主会话思考块标签由英文 Think 改为「思考」。
|
|
41
|
+
- DAG 检查器节点「执行过程」的「导出 HTML」改为复用页面真实渲染与样式,导出的独立 HTML 排版与页面一致;Pi 会话节点导出会带上原始协议事件,左上角提供「展开/隐藏原始协议事件」按钮(默认隐藏,每项仍可单独展开),shell/静态节点导出同样与页面一致。
|
|
32
42
|
- backend-test 的 pytest repair 现在继续使用 run-owned plan,不再搜索测试目录 README;correspondence finding 会直接指向 docstring、参数 ID、符号、脚本或 payload 的修复位置,collection/scenario repair 与 generated-local Python 写边界保持一致,并在单次 Pi 会话达到 24 轮或 80K 最大 usage snapshot 前受控返回 partial failure。
|
|
43
|
+
- Operator Chat 现在可以在同一个页面管理多个工作区;每个会话始终使用创建它的工作区,切换对话时地址栏保持简洁稳定。
|
|
44
|
+
- 对话消息流整体调整为更紧凑、清晰的布局,思考过程、工具执行、阶段性结果和最终回答会按真实发生顺序展示。
|
|
45
|
+
- 上下文过大时会自动压缩并继续原请求,消息流会依次展示触发原因、压缩进度和压缩结果;手动压缩也使用同一套展示方式。
|
|
46
|
+
- Operator Chat 与 DAG Pi 节点默认开启上下文压力处理:发送前接近窗口时压缩一次、识别网关「请求上下文过大」后压缩续跑,并对 OpenAI 格式的大工具输出做线路压缩;可用环境变量关闭,不必改会话记录。
|
|
47
|
+
- 对话进行中可以连续提交多条消息,并支持编辑、删除或立即插入当前回复;刷新页面或重启 Console 后,未发送内容仍会保留。
|
|
33
48
|
|
|
34
49
|
- Operator Chat 多次输入排队对齐 DSH Web:运行中的连续输入改为持久化 FIFO 队列,不再用后一条覆盖前一条;单条直接展示,多条默认折叠为数量栏,展开后逐行提供内联编辑、删除和插话发送。队列 Dock 使用与 DSH 一致的外层定位与内层面板结构,标题固定、仅消息列表滚动,并在编辑或请求期间锁定折叠操作。编辑/删除以消息 ID + revision 做并发保护,插话复用 Pi `steer` 注入当前回合,其余消息继续按顺序在回合结束后自动发送;队列通过会话快照与 SSE 权威数组恢复,刷新或 Console 重启不丢失。
|
|
35
50
|
- Operator Chat 右侧辅助栏统一上下文与调用记录:输入框的上下文环、调用记录入口现在复用文件/DAG 侧栏;「文件」常驻,「上下文 / 调用记录」按入口临时添加并支持独立 `×` 关闭,关闭后回到文件 Tab;点击已选入口可收起侧栏,切换到上下文时同步开启注入状态,收起时仅关闭注入而保留上下文数据。上下文与工具调用内容改为内嵌布局,不再叠加独立弹窗、遮罩、焦点陷阱或重复关闭按钮,并补充 Tab 切换与侧栏开合动画;修复折叠态右侧展开按钮无法恢复侧栏的问题。
|
|
@@ -164,6 +179,9 @@
|
|
|
164
179
|
- Operator Chat 的命令说明和参数提示与实际能力保持一致,减少选错恢复入口或误用占位参数。
|
|
165
180
|
- 测试套件按 CPU、内存、平台和当前 DAG 压力动态分配资源,并减少重复启动进程,完整验证耗时更稳定。
|
|
166
181
|
|
|
182
|
+
- Operator Chat 发送前会在接近模型窗口时主动压缩,压缩失败仍发送当前输入,不会把消息丢掉。DAG 节点同步启用 overflow 识别与线路压缩,但不在 streaming 中主动 compact。
|
|
183
|
+
- 网关「请求上下文过大」及常见包装/嵌套 JSON 由共享分类器识别;目标项目 `loop-agent init` 生成的 Pi overflow extension 与控制器使用同一套短语表。
|
|
184
|
+
- `@agent-context/sift@0.0.1-alpha.6` 作为可选依赖接入 Console 与 DAG:默认开启,`HARNESS_CONSOLE_SIFT` / `HARNESS_DAG_SIFT` 设为 `0`、`false` 或 `off` 即可关闭。Anthropic 缺省 prompt cache 形态保持 no-op,避免破坏缓存。
|
|
167
185
|
- 后端测试的计划与生成阶段只读取任务明确绑定的需求、环境报告和本轮产物,减少无关仓库内容干扰结果,并让后续步骤继续使用同一份已确认计划。
|
|
168
186
|
- Operator Chat 的任务清单工具描述补齐多步骤与 DAG 使用纪律:开始前按具体步骤规划、保持进行中约束、完成后立即更新完整列表,并用面向用户的阶段呈现 DAG 进度,简单任务自动跳过。
|
|
169
187
|
- 正式发布会自动同步更新版本日志和文档站摘要;发布说明优先生成简洁自然的中文,生成服务不可用时也能安全完成发布。
|
|
@@ -186,6 +204,15 @@
|
|
|
186
204
|
- Windows 上执行后端测试场景参数的 Python 语法检查时,会跳过不可用的 Microsoft Store 占位程序,避免错误回滚合法改动。
|
|
187
205
|
- 旧版前端 DAG 也能识别增量计划修订,不再因契约格式差异反复重试;无法合并的修订会提示重新生成 DAG。
|
|
188
206
|
|
|
207
|
+
- 修复 backend-test 的 Scenario Partition 计划表只提示 `each-value`/`omitted`、却由下游确定性门禁强制要求 `not-in-set` 的合同漂移;计划、Markdown Case 与 pytest 现在统一明确唯一补集槽位,避免 PI 因表格指令遗漏 `TP-<Partition ID>-NOT-IN-SET` 后触发 `MISSING_COMPLEMENT_SLOT`。
|
|
208
|
+
- 修复文档审计扫描本地 `.scratch/` 临时证据与打包目录、导致仓库 preflight 和发布前验证被非治理文件阻断的问题。
|
|
209
|
+
- 修复 backend-test 把 Markdown 明确声明的 enum-invalid、wrong-type、null、empty 和精确非法 custom literal 当作 payload enum 契约漂移、错误消耗唯一一次 pytest repair 的问题;豁免现在按 pytest parameter ID、目标字段和值逐 TP 绑定,positive/nominal drift 与 path 缺陷仍严格阻断。
|
|
210
|
+
- 修复 backend-test 无法从同一生成模块的安全 payload helper 中绑定 field-target positional/keyword/default 参数,以及无法识别精确 `_without(payload, field)` 删除语义的问题;computed、动态、二层、跨模块和危险 helper 仍保持 `UNDETERMINED`。
|
|
211
|
+
- 修复 backend-test 无法静态证明同一生成模块内安全 payload helper、导致 request-level nominal 场景长期停留在 `UNDETERMINED` 的问题;超长边界 observed 现在持久化为有界摘要并保留真实长度,空白修复建议也明确使用真实空格而非字面 escape 文本。
|
|
212
|
+
- 修复 backend-test 把 Markdown 中 `\\u0020`/`\\x20` 空白表示与 pytest 真实空白字符串误判为 scenario-param 不一致的问题;readiness 的逐项排除现在携带有界的 TP、字段、expected/observed raw/normalized 和源码位置诊断,同时不对普通业务字符做 trim、大小写折叠或宽松解码。
|
|
213
|
+
- 修复 Console 把 backend-test writer 的局部保护文件误判为整个 writeSet 越界的问题;任务级禁止路径仍保持硬阻断,合法 DAG 可继续取得 autonomous execution receipt。
|
|
214
|
+
- 修复 backend-test 对 `resource_client.call(..., payload=...)` 一类本地 transport 的 payload AST 假阴性;readiness 新增固定分类/Case 排除汇总和 pytest 启动事实,payload repair finding 同时携带 expected/observed/missing path 清单。
|
|
215
|
+
- 修复后端测试在 payload、fixture 与 Markdown→pytest 对应关系同时异常时只修复其中一类问题的问题;pytest 侧缺陷现在通过结构化 `repairFindings` 合并进入唯一一次有界 repair,Markdown Test Point 未分类或重复分类则在生成阶段定向重试并禁止下游猜修。
|
|
189
216
|
- 修复重命名会话时输入框没有回显当前标题的问题。
|
|
190
217
|
- 修复停止回复后输入区恢复迟缓、排队消息偶发丢失,以及切换会话后旧状态残留的问题。
|
|
191
218
|
- 修复宽表格、文件预览、滚动条和窄屏布局中的多处显示异常。
|
|
@@ -109,8 +109,12 @@ function findForbiddenOverlapsForPacket(task) {
|
|
|
109
109
|
const overlaps = [];
|
|
110
110
|
for (const writeSetEntry of task.writeSet ?? []) {
|
|
111
111
|
for (const forbiddenPath of task.forbiddenPaths ?? []) {
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
// A narrower forbidden child may intentionally carve a protected
|
|
113
|
+
// file out of a broader node writeSet. The runtime write guard gives
|
|
114
|
+
// forbiddenPaths precedence; report a packet conflict only when the
|
|
115
|
+
// writeSet entry itself is inside the forbidden boundary. Console G2
|
|
116
|
+
// separately rechecks task-global forbidden paths with live scope.
|
|
117
|
+
if (pathMatchesPattern(writeSetEntry, forbiddenPath)) {
|
|
114
118
|
overlaps.push({ writeSetEntry, forbiddenPath });
|
|
115
119
|
}
|
|
116
120
|
}
|
|
@@ -260,6 +260,10 @@ async function monitorRun(input) {
|
|
|
260
260
|
failureCategory: "monitor-timeout",
|
|
261
261
|
});
|
|
262
262
|
}
|
|
263
|
+
function isStartupGateEligible(snapshot) {
|
|
264
|
+
const status = snapshot.source?.sourceFidelityStartup?.status;
|
|
265
|
+
return status === undefined || status === "complete" || status === "not-applicable";
|
|
266
|
+
}
|
|
263
267
|
function nextMutationAction(actions) {
|
|
264
268
|
for (const action of actions) {
|
|
265
269
|
if (action.type !== "stop")
|
|
@@ -379,7 +383,10 @@ export async function advanceTaskLifecycle(input) {
|
|
|
379
383
|
};
|
|
380
384
|
}
|
|
381
385
|
const token = parseGateToken(input.rejectGateToken);
|
|
382
|
-
const currentGate = snapshot.gate ??
|
|
386
|
+
const currentGate = snapshot.gate ??
|
|
387
|
+
(isStartupGateEligible(snapshot)
|
|
388
|
+
? await buildCurrentGate(input.repoRoot, input.taskId)
|
|
389
|
+
: null);
|
|
383
390
|
if (!currentGate || !gateMatches(currentGate, token)) {
|
|
384
391
|
return {
|
|
385
392
|
taskId: input.taskId,
|
|
@@ -443,11 +450,12 @@ export async function advanceTaskLifecycle(input) {
|
|
|
443
450
|
dryRun,
|
|
444
451
|
skipFinalize: Boolean(input.skipFinalize),
|
|
445
452
|
forcePrepareContract: Boolean(input.fromDraftPath) && !preparedContractThisCall,
|
|
453
|
+
startupRepairAttempted: preparedContractThisCall,
|
|
446
454
|
});
|
|
447
455
|
if (dryRun) {
|
|
448
456
|
const plan = planOnce(snapshot);
|
|
449
457
|
const plannedGate = snapshot.gate ??
|
|
450
|
-
(snapshot.dagDraft?.exists
|
|
458
|
+
(isStartupGateEligible(snapshot) && snapshot.dagDraft?.exists
|
|
451
459
|
? await buildCurrentGate(input.repoRoot, input.taskId)
|
|
452
460
|
: null);
|
|
453
461
|
return {
|
|
@@ -586,6 +594,10 @@ export async function advanceTaskLifecycle(input) {
|
|
|
586
594
|
continue;
|
|
587
595
|
}
|
|
588
596
|
if (action.type === "prepare-contract") {
|
|
597
|
+
const startupStatus = snapshot.source?.sourceFidelityStartup?.status;
|
|
598
|
+
const startupRepairAction = startupStatus === "missing" ||
|
|
599
|
+
startupStatus === "invalid" ||
|
|
600
|
+
startupStatus === "coverage-incomplete";
|
|
589
601
|
// 生产 source fidelity Pi 接线(AC-PROD-001/005):仅 frontend-implementation
|
|
590
602
|
// 任务注入 reconcile executor + 独立 reviewer;ineligible 返回 undefined 不接线。
|
|
591
603
|
// 主路径与 semantic-intake retry 路径共用同一接线,保证任意一条生产路径都实际
|
|
@@ -731,7 +743,9 @@ export async function advanceTaskLifecycle(input) {
|
|
|
731
743
|
message: "structured requirement draft via semantic intake; projected managed source",
|
|
732
744
|
});
|
|
733
745
|
preparedContractThisCall = true;
|
|
734
|
-
if (
|
|
746
|
+
if (startupRepairAction ||
|
|
747
|
+
refreshManagedContractThisCall ||
|
|
748
|
+
input.fromDraftPath) {
|
|
735
749
|
refreshManagedContractThisCall = false;
|
|
736
750
|
regenerateDagThisCall = true;
|
|
737
751
|
}
|
|
@@ -825,7 +839,9 @@ export async function advanceTaskLifecycle(input) {
|
|
|
825
839
|
break;
|
|
826
840
|
}
|
|
827
841
|
preparedContractThisCall = true;
|
|
828
|
-
if (
|
|
842
|
+
if (startupRepairAction ||
|
|
843
|
+
refreshManagedContractThisCall ||
|
|
844
|
+
input.fromDraftPath) {
|
|
829
845
|
// The old DAG and gate were bound to the pre-mutation contract.
|
|
830
846
|
refreshManagedContractThisCall = false;
|
|
831
847
|
regenerateDagThisCall = true;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { access, readFile } from "node:fs/promises";
|
|
1
|
+
import { access, readFile, readdir } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { getTaskDir, getTaskPaths, getTaskStatus, loadTaskConfig, } from "../../task/runtime.js";
|
|
4
4
|
import { logicalRevisionForState, observeTaskContractState, } from "../../task/contract/index.js";
|
|
5
5
|
import { assessDagRunLiveness, listAllDagRunEntries, readDagRunSpec, readDagRunState, } from "../../workflows/dag/lifecycle.js";
|
|
6
6
|
import { resolveRunningControllerIdentity } from "../../shared/package-metadata.js";
|
|
7
|
+
import { parseLedgerJson, validateRequirementLedger, } from "../../task/source-prepare/ledger.js";
|
|
8
|
+
import { REQUIREMENT_LEDGER_FILE_NAME } from "../../task/source-prepare/types.js";
|
|
7
9
|
import { loadLifecycleRecord } from "./record.js";
|
|
8
10
|
import { buildGateBinding, buildWriteSetGate, buildWriteSetGateDelta, controllerIdentityAnchor, hashCanonicalPayload, normalizeWriteSet, sha256Hex, } from "./gates.js";
|
|
9
11
|
async function exists(filePath) {
|
|
@@ -47,6 +49,140 @@ async function taskExists(repoRoot, taskId) {
|
|
|
47
49
|
const paths = getTaskPaths(repoRoot, taskId);
|
|
48
50
|
return exists(paths.taskConfigPath);
|
|
49
51
|
}
|
|
52
|
+
async function assessReferenceEvidence(referencesDir) {
|
|
53
|
+
try {
|
|
54
|
+
const entries = await readdir(referencesDir);
|
|
55
|
+
const importedPrdCount = entries.filter((name) => name.endsWith(".md")).length;
|
|
56
|
+
return importedPrdCount > 0
|
|
57
|
+
? { status: "present", importedPrdCount }
|
|
58
|
+
: { status: "absent-or-empty", importedPrdCount: 0 };
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (error &&
|
|
62
|
+
typeof error === "object" &&
|
|
63
|
+
"code" in error &&
|
|
64
|
+
error.code === "ENOENT") {
|
|
65
|
+
return { status: "absent-or-empty", importedPrdCount: 0 };
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
status: "unknown",
|
|
69
|
+
importedPrdCount: 0,
|
|
70
|
+
error: error instanceof Error ? error.message : String(error),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function hasLedgerStructure(value) {
|
|
75
|
+
if (!value || typeof value !== "object")
|
|
76
|
+
return false;
|
|
77
|
+
const ledger = value;
|
|
78
|
+
return (typeof ledger.taskId === "string" &&
|
|
79
|
+
typeof ledger.inputDigest === "string" &&
|
|
80
|
+
typeof ledger.requirementSha256 === "string" &&
|
|
81
|
+
Array.isArray(ledger.sourcePaths) &&
|
|
82
|
+
Array.isArray(ledger.fragments) &&
|
|
83
|
+
Array.isArray(ledger.canonicalRequirements) &&
|
|
84
|
+
Boolean(ledger.stats && typeof ledger.stats === "object") &&
|
|
85
|
+
Array.isArray(ledger.unresolved) &&
|
|
86
|
+
Array.isArray(ledger.conflicts) &&
|
|
87
|
+
Array.isArray(ledger.sourceResolutions) &&
|
|
88
|
+
typeof ledger.schemaReconcilerVersion === "string" &&
|
|
89
|
+
typeof ledger.builtAt === "string");
|
|
90
|
+
}
|
|
91
|
+
async function assessSourceFidelityStartup(input) {
|
|
92
|
+
if (!input.applicable) {
|
|
93
|
+
return {
|
|
94
|
+
status: "not-applicable",
|
|
95
|
+
ledgerPath: input.ledgerPath,
|
|
96
|
+
diagnostics: [],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (input.referenceEvidenceError) {
|
|
100
|
+
return {
|
|
101
|
+
status: "invalid",
|
|
102
|
+
ledgerPath: input.ledgerPath,
|
|
103
|
+
diagnostics: [
|
|
104
|
+
{
|
|
105
|
+
code: "SOURCE_FIDELITY_LEDGER_STALE",
|
|
106
|
+
message: `imported PRD reference evidence is unreadable: ${input.referenceEvidenceError}`,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
let raw;
|
|
112
|
+
try {
|
|
113
|
+
raw = await readFile(input.ledgerPath, "utf-8");
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
const missing = !(await exists(input.ledgerPath));
|
|
117
|
+
return {
|
|
118
|
+
status: missing ? "missing" : "invalid",
|
|
119
|
+
ledgerPath: input.ledgerPath,
|
|
120
|
+
diagnostics: [
|
|
121
|
+
{
|
|
122
|
+
code: missing
|
|
123
|
+
? "SOURCE_FIDELITY_LEDGER_MISSING"
|
|
124
|
+
: "SOURCE_FIDELITY_LEDGER_STALE",
|
|
125
|
+
message: missing
|
|
126
|
+
? "managed frontend task has no requirement-ledger.json"
|
|
127
|
+
: `requirement ledger is unreadable: ${error instanceof Error ? error.message : String(error)}`,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const ledger = parseLedgerJson(raw);
|
|
134
|
+
if (!hasLedgerStructure(ledger)) {
|
|
135
|
+
return {
|
|
136
|
+
status: "invalid",
|
|
137
|
+
ledgerPath: input.ledgerPath,
|
|
138
|
+
diagnostics: [
|
|
139
|
+
{
|
|
140
|
+
code: "SOURCE_FIDELITY_LEDGER_STALE",
|
|
141
|
+
message: "requirement ledger is missing required structural fields",
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const nonDecisionErrors = validateRequirementLedger(ledger, {
|
|
147
|
+
allowedUnresolved: true,
|
|
148
|
+
allowedConflicts: true,
|
|
149
|
+
});
|
|
150
|
+
if (nonDecisionErrors.length > 0) {
|
|
151
|
+
const coverageCodes = new Set([
|
|
152
|
+
"SOURCE_FIDELITY_HIGH_RISK_UNCOVERED",
|
|
153
|
+
"SOURCE_FIDELITY_REQUIREMENT_NOT_COVERED",
|
|
154
|
+
]);
|
|
155
|
+
return {
|
|
156
|
+
status: nonDecisionErrors.every((error) => coverageCodes.has(error.code))
|
|
157
|
+
? "coverage-incomplete"
|
|
158
|
+
: "invalid",
|
|
159
|
+
ledgerPath: input.ledgerPath,
|
|
160
|
+
diagnostics: nonDecisionErrors,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const decisionErrors = validateRequirementLedger(ledger);
|
|
164
|
+
if (decisionErrors.length > 0) {
|
|
165
|
+
return {
|
|
166
|
+
status: "human-decision-blocked",
|
|
167
|
+
ledgerPath: input.ledgerPath,
|
|
168
|
+
diagnostics: decisionErrors,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return { status: "complete", ledgerPath: input.ledgerPath, diagnostics: [] };
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
return {
|
|
175
|
+
status: "invalid",
|
|
176
|
+
ledgerPath: input.ledgerPath,
|
|
177
|
+
diagnostics: [
|
|
178
|
+
{
|
|
179
|
+
code: "SOURCE_FIDELITY_LEDGER_STALE",
|
|
180
|
+
message: error instanceof Error ? error.message : String(error),
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
50
186
|
/**
|
|
51
187
|
* Explicit Task↔DAG association only:
|
|
52
188
|
* - lifecycle.json recorded runAssociations
|
|
@@ -182,10 +318,12 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
182
318
|
const record = await loadLifecycleRecord(repoRoot, taskId);
|
|
183
319
|
const contractState = await observeTaskContractState(repoRoot, taskId);
|
|
184
320
|
let title;
|
|
321
|
+
let taskKind;
|
|
185
322
|
let taskConfigAllowed = [];
|
|
186
323
|
try {
|
|
187
324
|
const config = await loadTaskConfig(repoRoot, taskId);
|
|
188
325
|
title = config.title;
|
|
326
|
+
taskKind = config.taskKind;
|
|
189
327
|
taskConfigAllowed = config.allowedPaths ?? [];
|
|
190
328
|
}
|
|
191
329
|
catch {
|
|
@@ -199,19 +337,22 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
199
337
|
...(requirementReady ? [] : [requirementPath]),
|
|
200
338
|
...(constraintsReady ? [] : [constraintsPath]),
|
|
201
339
|
];
|
|
202
|
-
|
|
340
|
+
const contractManaged = contractState.effectiveStatus === "managed" || Boolean(contractState.ref);
|
|
203
341
|
const referencesDir = path.join(paths.sourceDir, "references");
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
342
|
+
const referenceEvidence = await assessReferenceEvidence(referencesDir);
|
|
343
|
+
const importedPrdCount = referenceEvidence.importedPrdCount;
|
|
344
|
+
const ledgerPath = path.join(paths.sourceDir, REQUIREMENT_LEDGER_FILE_NAME);
|
|
345
|
+
const sourceFidelityStartup = await assessSourceFidelityStartup({
|
|
346
|
+
ledgerPath,
|
|
347
|
+
applicable: contractManaged &&
|
|
348
|
+
taskKind === "frontend-implementation" &&
|
|
349
|
+
referenceEvidence.status !== "absent-or-empty",
|
|
350
|
+
referenceEvidenceError: referenceEvidence.status === "unknown"
|
|
351
|
+
? referenceEvidence.error
|
|
352
|
+
: undefined,
|
|
353
|
+
});
|
|
354
|
+
const sourceFidelityReady = sourceFidelityStartup.status === "complete" ||
|
|
355
|
+
sourceFidelityStartup.status === "not-applicable";
|
|
215
356
|
const dagDraftPath = paths.dagDraftPath;
|
|
216
357
|
const dagDraftExists = await exists(dagDraftPath);
|
|
217
358
|
let specHash = null;
|
|
@@ -285,7 +426,11 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
285
426
|
// Open gate only when draft is strict-validated and the current binding is
|
|
286
427
|
// not already approved/rejected. Rejected same-digest gates stay closed
|
|
287
428
|
// until contract/source/DAG/writeSet/controller binding changes.
|
|
288
|
-
const gate = gateBound &&
|
|
429
|
+
const gate = gateBound &&
|
|
430
|
+
strictValidated &&
|
|
431
|
+
sourceFidelityReady &&
|
|
432
|
+
!approvedCurrent &&
|
|
433
|
+
!rejectedCurrent
|
|
289
434
|
? buildWriteSetGate({ taskId, bound: gateBound, delta: gateDelta })
|
|
290
435
|
: null;
|
|
291
436
|
const gateOpen = Boolean(gate);
|
|
@@ -310,11 +455,10 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
310
455
|
activeRun.failureCategory === "needs-attention" ||
|
|
311
456
|
activeRun.failureCategory === "suspected-stall" ||
|
|
312
457
|
activeRun.failureCategory === "monitor-timeout"));
|
|
313
|
-
const contractManaged = contractState.effectiveStatus === "managed" || Boolean(contractState.ref);
|
|
314
458
|
const lifecycleState = deriveLifecycleState({
|
|
315
459
|
exists: true,
|
|
316
460
|
contractManaged,
|
|
317
|
-
sourceReady: requirementReady,
|
|
461
|
+
sourceReady: requirementReady && sourceFidelityReady,
|
|
318
462
|
dagValidated: strictValidated,
|
|
319
463
|
gateOpen: Boolean(gate),
|
|
320
464
|
activeRun,
|
|
@@ -352,6 +496,7 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
352
496
|
constraintsReady,
|
|
353
497
|
importedPrdCount,
|
|
354
498
|
missing,
|
|
499
|
+
sourceFidelityStartup,
|
|
355
500
|
},
|
|
356
501
|
dagDraft: {
|
|
357
502
|
path: dagDraftPath,
|
|
@@ -375,7 +520,16 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
375
520
|
exists: closeoutExists || Boolean(record?.closeout),
|
|
376
521
|
},
|
|
377
522
|
completedTransitions: record?.completedTransitions ?? [],
|
|
378
|
-
blockers:
|
|
523
|
+
blockers: sourceFidelityReady
|
|
524
|
+
? []
|
|
525
|
+
: sourceFidelityStartup.diagnostics.map((diagnostic) => ({
|
|
526
|
+
code: diagnostic.code,
|
|
527
|
+
message: diagnostic.message,
|
|
528
|
+
details: {
|
|
529
|
+
startupStatus: sourceFidelityStartup.status,
|
|
530
|
+
ledgerPath: sourceFidelityStartup.ledgerPath,
|
|
531
|
+
},
|
|
532
|
+
})),
|
|
379
533
|
warnings: [],
|
|
380
534
|
next,
|
|
381
535
|
};
|
|
@@ -46,6 +46,37 @@ export function planTransitions(input) {
|
|
|
46
46
|
actions.push({ type: "recover-contract" });
|
|
47
47
|
expectedTransitions.push("contract-recovered");
|
|
48
48
|
}
|
|
49
|
+
const startupStatus = snapshot.source?.sourceFidelityStartup?.status;
|
|
50
|
+
const startupRepairDetected = startupStatus === "missing" ||
|
|
51
|
+
startupStatus === "invalid" ||
|
|
52
|
+
startupStatus === "coverage-incomplete";
|
|
53
|
+
if (startupRepairDetected && input.startupRepairAttempted) {
|
|
54
|
+
actions.push({ type: "stop", reason: "blocker" });
|
|
55
|
+
return {
|
|
56
|
+
actions,
|
|
57
|
+
gate: null,
|
|
58
|
+
blockers,
|
|
59
|
+
next: {
|
|
60
|
+
kind: "advance",
|
|
61
|
+
command: `loop-agent task advance ${snapshot.taskId} --json`,
|
|
62
|
+
},
|
|
63
|
+
expectedTransitions,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const startupRepairRequired = startupRepairDetected;
|
|
67
|
+
if (startupStatus === "human-decision-blocked") {
|
|
68
|
+
actions.push({ type: "stop", reason: "blocker" });
|
|
69
|
+
return {
|
|
70
|
+
actions,
|
|
71
|
+
gate: null,
|
|
72
|
+
blockers,
|
|
73
|
+
next: {
|
|
74
|
+
kind: "external-action",
|
|
75
|
+
description: "Requirement ledger requires an existing human source decision; preserve sourceResolutions, resolve the blocker, then advance again.",
|
|
76
|
+
},
|
|
77
|
+
expectedTransitions,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
49
80
|
const sourceReady = snapshot.source?.requirementReady === true;
|
|
50
81
|
const contractManaged = snapshot.contract?.effectiveStatus === "managed" ||
|
|
51
82
|
Boolean(snapshot.contract?.revision && snapshot.contract.revision > 0);
|
|
@@ -53,7 +84,9 @@ export function planTransitions(input) {
|
|
|
53
84
|
// Prefer prepare once PRDs are on disk; re-import only when caller still
|
|
54
85
|
// signals hasPrdInput (advance.ts latches this after one import per call).
|
|
55
86
|
const shouldImportPrd = input.hasPrdInput;
|
|
56
|
-
if (input.forcePrepareContract ||
|
|
87
|
+
if (input.forcePrepareContract ||
|
|
88
|
+
input.refreshManagedContract ||
|
|
89
|
+
startupRepairRequired) {
|
|
57
90
|
if (shouldImportPrd) {
|
|
58
91
|
actions.push({ type: "import-prd" });
|
|
59
92
|
expectedTransitions.push("prd-imported");
|
|
@@ -124,7 +157,8 @@ export function planTransitions(input) {
|
|
|
124
157
|
const strictValidated = snapshot.dagDraft?.strictValidated === true;
|
|
125
158
|
const dagRefreshRequired = Boolean(input.forceDagRefresh ||
|
|
126
159
|
input.refreshManagedContract ||
|
|
127
|
-
input.forcePrepareContract
|
|
160
|
+
input.forcePrepareContract ||
|
|
161
|
+
startupRepairRequired);
|
|
128
162
|
if (!dagExists || dagRefreshRequired) {
|
|
129
163
|
actions.push({ type: "generate-dag" });
|
|
130
164
|
expectedTransitions.push("dag-generated");
|
|
@@ -145,10 +179,11 @@ export function planTransitions(input) {
|
|
|
145
179
|
snapshot.lifecycleState === "evidence-closed" ||
|
|
146
180
|
snapshot.lifecycleState === "needs-attention" ||
|
|
147
181
|
snapshot.lifecycleState === "awaiting-decision";
|
|
148
|
-
const
|
|
182
|
+
const approveCurrentGate = input.approveGate && !dagRefreshRequired;
|
|
183
|
+
const approvedForCurrent = postRunState || (approveCurrentGate && gate !== null);
|
|
149
184
|
if (!approvedForCurrent && (strictValidated || !dagExists || dagRefreshRequired)) {
|
|
150
185
|
// Gate opens after generate+validate in the advance loop; plan includes open.
|
|
151
|
-
if (!
|
|
186
|
+
if (!approveCurrentGate) {
|
|
152
187
|
actions.push({ type: "open-write-set-gate" });
|
|
153
188
|
expectedTransitions.push("write-set-gate-opened");
|
|
154
189
|
if (input.dryRun) {
|
|
@@ -177,14 +212,14 @@ export function planTransitions(input) {
|
|
|
177
212
|
(snapshot.lifecycleState === "run-failed" && !failedRecoverable) ||
|
|
178
213
|
snapshot.lifecycleState === "evidence-closed";
|
|
179
214
|
if (!alreadyTerminalRun) {
|
|
180
|
-
if (
|
|
215
|
+
if (approveCurrentGate && !postRunState) {
|
|
181
216
|
expectedTransitions.push("write-set-approved");
|
|
182
217
|
actions.push({ type: "start-or-monitor-run" });
|
|
183
218
|
expectedTransitions.push("dag-run-started", "dag-run-monitored");
|
|
184
219
|
}
|
|
185
220
|
else if (snapshot.lifecycleState === "running" ||
|
|
186
221
|
snapshot.activeRun ||
|
|
187
|
-
(
|
|
222
|
+
(approveCurrentGate && Boolean(snapshot.activeRun))) {
|
|
188
223
|
actions.push({ type: "start-or-monitor-run" });
|
|
189
224
|
expectedTransitions.push("dag-run-monitored");
|
|
190
225
|
}
|
|
@@ -218,7 +253,7 @@ export function planTransitions(input) {
|
|
|
218
253
|
actions,
|
|
219
254
|
gate,
|
|
220
255
|
blockers,
|
|
221
|
-
next: deriveNext(snapshot, gate,
|
|
256
|
+
next: deriveNext(snapshot, gate, approveCurrentGate),
|
|
222
257
|
expectedTransitions,
|
|
223
258
|
};
|
|
224
259
|
}
|
package/dist/build-stamp.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.39.0-beta.
|
|
4
|
-
"gitSha": "
|
|
5
|
-
"builtAt": "2026-08-
|
|
3
|
+
"version": "0.39.0-beta.14",
|
|
4
|
+
"gitSha": "9d8bebca76a2608d9b38a1af1f379fd5fe5b04b0",
|
|
5
|
+
"builtAt": "2026-08-30T09:13:51.707Z"
|
|
6
6
|
}
|
|
@@ -19,36 +19,11 @@ export const MAX_SESSION_RETRIES = 5;
|
|
|
19
19
|
/** Hard cap for OpenCode overflow compact+resume recoveries (≤ 2). */
|
|
20
20
|
export const MAX_OVERFLOW_RECOVERIES = 1;
|
|
21
21
|
import { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY, } from "../shared/pi-retry-settings.js";
|
|
22
|
+
import { CONTEXT_OVERFLOW_PHRASES, buildGeneratedOverflowClassifierSource, isContextOverflow as classifySharedOverflow, } from "../shared/pi-context-pressure/overflow.js";
|
|
22
23
|
export { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY };
|
|
23
|
-
|
|
24
|
-
* Shared overflow phrase sources used by isContextOverflow and generated plugins.
|
|
25
|
-
* Keep English + Chinese intranet gateway messages in one place to avoid drift.
|
|
26
|
-
*/
|
|
27
|
-
export const CONTEXT_OVERFLOW_PHRASES = [
|
|
28
|
-
"请求上下文过大",
|
|
29
|
-
"context_length_exceeded",
|
|
30
|
-
"context overflow",
|
|
31
|
-
"context length overflow",
|
|
32
|
-
"too many tokens",
|
|
33
|
-
"maximum context",
|
|
34
|
-
"prompt is too long",
|
|
35
|
-
"request_too_large",
|
|
36
|
-
"context window",
|
|
37
|
-
];
|
|
38
|
-
function escapeRegExpLiteral(value) {
|
|
39
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
40
|
-
}
|
|
41
|
-
const CONTEXT_OVERFLOW_PATTERN = new RegExp(CONTEXT_OVERFLOW_PHRASES.map(escapeRegExpLiteral).join("|"), "i");
|
|
24
|
+
export { CONTEXT_OVERFLOW_PHRASES };
|
|
42
25
|
function buildGeneratedContextOverflowPatternSource() {
|
|
43
|
-
return
|
|
44
|
-
...CONTEXT_OVERFLOW_PHRASES,
|
|
45
|
-
])};
|
|
46
|
-
const OVERFLOW_PATTERN = new RegExp(
|
|
47
|
-
CONTEXT_OVERFLOW_PHRASES.map((phrase) =>
|
|
48
|
-
phrase.replace(/[.*+?^\${}()|[\\]\\\\]/g, "\\\\$&"),
|
|
49
|
-
).join("|"),
|
|
50
|
-
"i",
|
|
51
|
-
);`;
|
|
26
|
+
return buildGeneratedOverflowClassifierSource();
|
|
52
27
|
}
|
|
53
28
|
const PERMANENT_MESSAGE_PATTERNS = [
|
|
54
29
|
{
|
|
@@ -65,7 +40,7 @@ const PERMANENT_MESSAGE_PATTERNS = [
|
|
|
65
40
|
},
|
|
66
41
|
{
|
|
67
42
|
reason: "context-overflow",
|
|
68
|
-
pattern:
|
|
43
|
+
pattern: /context_length_exceeded|请求上下文过大/i,
|
|
69
44
|
},
|
|
70
45
|
{
|
|
71
46
|
reason: "cancelled",
|
|
@@ -180,6 +155,8 @@ function permanentReason(error) {
|
|
|
180
155
|
return "api-error";
|
|
181
156
|
}
|
|
182
157
|
const text = collectErrorText(error);
|
|
158
|
+
if (isContextOverflow(error))
|
|
159
|
+
return "context-overflow";
|
|
183
160
|
for (const entry of PERMANENT_MESSAGE_PATTERNS) {
|
|
184
161
|
if (entry.pattern.test(text))
|
|
185
162
|
return entry.reason;
|
|
@@ -192,7 +169,7 @@ function permanentReason(error) {
|
|
|
192
169
|
* business validation, or standard 502/LLMRequestError as overflow.
|
|
193
170
|
*/
|
|
194
171
|
export function isContextOverflow(error) {
|
|
195
|
-
return
|
|
172
|
+
return classifySharedOverflow(error);
|
|
196
173
|
}
|
|
197
174
|
function looksTransient(error) {
|
|
198
175
|
const text = collectErrorText(error);
|
|
@@ -887,7 +864,7 @@ function textOf(error) {
|
|
|
887
864
|
}
|
|
888
865
|
|
|
889
866
|
function isContextOverflow(error) {
|
|
890
|
-
return
|
|
867
|
+
return isOverflowMessage(textOf(error));
|
|
891
868
|
}
|
|
892
869
|
|
|
893
870
|
function buildOverflowResumePrompt(sessionId, error, attempt) {
|
|
@@ -1163,11 +1140,6 @@ const EXTENSION_PATH = ${JSON.stringify(PI_CONTEXT_OVERFLOW_EXTENSION_PATH)};
|
|
|
1163
1140
|
${buildGeneratedContextOverflowPatternSource()}
|
|
1164
1141
|
const NORMALIZED_PREFIX = "context_length_exceeded:";
|
|
1165
1142
|
|
|
1166
|
-
function isOverflowMessage(text) {
|
|
1167
|
-
if (typeof text !== "string" || !text) return false;
|
|
1168
|
-
return OVERFLOW_PATTERN.test(text);
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
1143
|
export default function loopAgentContextOverflowExtension(pi) {
|
|
1172
1144
|
void EXTENSION_PATH;
|
|
1173
1145
|
pi.on("message_end", (event) => {
|