@tea-agent/loop-agent 0.38.2 → 0.39.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +2 -0
  3. package/dist/application/task-lifecycle/advance.js +4 -0
  4. package/dist/build-stamp.json +3 -3
  5. package/dist/commands/task-advance.js +31 -0
  6. package/dist/executors/pi-event-serializer.js +42 -1
  7. package/dist/executors/pi-executor.js +51 -0
  8. package/dist/executors/pi-sdk-executor.js +7 -26
  9. package/dist/executors/shell-executor.js +2 -2
  10. package/dist/task/contract/constants.js +1 -0
  11. package/dist/task/contract/diff.js +26 -0
  12. package/dist/task/contract/project.js +5 -0
  13. package/dist/task/contract/schema.js +6 -1
  14. package/dist/task/source-prepare/build-draft.js +15 -0
  15. package/dist/worker/console/pi-readiness.js +4 -4
  16. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4bmo3iY.js → abnfDiagram-N423BO3Z-C4kVYweA.js} +1 -1
  17. package/dist/worker/console/static/assets/{arc-SAD7McOS.js → arc-Bj2M8iO5.js} +1 -1
  18. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DAVLUdK1.js → architectureDiagram-T3A2C74G-CLlXe4-6.js} +1 -1
  19. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CPPdv4vj.js → blockDiagram-VBNYF7ZC-psEp5xH0.js} +1 -1
  20. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-D1GRGZax.js → c4Diagram-5PPSVZJV-DeKcOCGJ.js} +1 -1
  21. package/dist/worker/console/static/assets/channel-CPF4N7pf.js +1 -0
  22. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D_Bq2ZqK.js → chunk-2GRJ4B5K-K0C744p1.js} +1 -1
  23. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CPLVtamK.js → chunk-2Q5K7J3B-xbNw4J9-.js} +1 -1
  24. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DnaZWcL3.js → chunk-5RXB4S5H-DjLaSDUO.js} +1 -1
  25. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BRTmFgnq.js → chunk-5VM5RSS4-CtiPlKel.js} +1 -1
  26. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BRlfYZoH.js → chunk-6Q2QTUOP-DZtolir7.js} +1 -1
  27. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CjNybD4u.js → chunk-GF5L2VYU-CPcdNeew.js} +1 -1
  28. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-D3e-74fG.js → chunk-JWPE2WC7-NgHc6V37.js} +1 -1
  29. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Br3ir6f2.js → chunk-KBJHAD2P-DQ_T-hg8.js} +1 -1
  30. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-1NgVuRbh.js → chunk-RYQCIY6F-DgLsxcVP.js} +1 -1
  31. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BTYuvuWN.js → chunk-XXDRQBXY-UrVoM9z1.js} +1 -1
  32. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +1 -0
  33. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +1 -0
  34. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BC6Bgosf.js → cose-bilkent-JH36ORCC-CLkYvTb3.js} +1 -1
  35. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DV-HypfL.js → cynefin-VYW2F7L2-BwT_xKrE.js} +1 -1
  36. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DhTaddO-.js → cynefinDiagram-MW4NZA55-Bi9tuzif.js} +1 -1
  37. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BnEcOmiI.js → dagre-VZM6K2ZE-BCIqkWBV.js} +1 -1
  38. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-xphGRVKp.js → diagram-7IWD3JNH-Dip6l9_Z.js} +1 -1
  39. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CrdD1mRG.js → diagram-B4RE2ZJO-B-xkh_wK.js} +1 -1
  40. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-D4YdLsaF.js → diagram-LBJQPF4R-DZO0kTt_.js} +1 -1
  41. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-LCKyGZ5U.js → diagram-Q27KOJAE-D6ZplNbZ.js} +1 -1
  42. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BeiTe7t0.js → diagram-UB23O5K3-BPekbhoS.js} +1 -1
  43. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BO2cq8GS.js → ebnfDiagram-BXEA7PRR-BtmaJpK5.js} +1 -1
  44. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CmWww3Nt.js → erDiagram-JOGREHBK-6DS0Xc44.js} +1 -1
  45. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-C2kkUYg5.js → flowDiagram-UKHOOZJN-CihnROSm.js} +1 -1
  46. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DY_JZ2iQ.js → ganttDiagram-PKOTCBZU-CZC4zOE2.js} +1 -1
  47. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-BizhCSw9.js → gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js} +1 -1
  48. package/dist/worker/console/static/assets/{index-BXN89VPX.js → index-DTZOKgAn.js} +142 -63
  49. package/dist/worker/console/static/assets/index-Ya5FE7cD.css +1 -0
  50. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Dlo2eK_d.js → infoDiagram-6WML65LV-DlSl4BGx.js} +1 -1
  51. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-3EIlIIuZ.js → ishikawaDiagram-WSZJBQD7-snnzFl_U.js} +1 -1
  52. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DiMDe5Yh.js → journeyDiagram-NVQOT4AX-BDE7qpMx.js} +1 -1
  53. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-sN553oIN.js → kanban-definition-27J2QSJJ-CD2Ci04G.js} +1 -1
  54. package/dist/worker/console/static/assets/{linear-D9Py31Ld.js → linear-DdnapKIH.js} +1 -1
  55. package/dist/worker/console/static/assets/{mermaid.core-D7Occzk_.js → mermaid.core-BVjAT9b8.js} +5 -5
  56. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DjDRboiR.js → mindmap-definition-FAOFIHXS-D0yJk-zA.js} +1 -1
  57. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Cgc4qlzp.js → pegDiagram-VL7TDLO6-BVo9tFP-.js} +1 -1
  58. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DxZj8pKd.js → pieDiagram-7S7Q4E2Y-DnOV-mZ_.js} +1 -1
  59. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CJ50MngR.js → quadrantDiagram-CIZ2JOQS-CzUJo58i.js} +1 -1
  60. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BY-OD8Tr.js → railroadDiagram-AXF67PYL-Bvikrolh.js} +1 -1
  61. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CQFButfK.js → requirementDiagram-LRYGKXZP-DtaSVCap.js} +1 -1
  62. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BB_QUK_b.js → sankeyDiagram-W5VNT64P-C2c9A0wW.js} +1 -1
  63. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CqhXZjyx.js → sequenceDiagram-SI44F4Z6-DmXJcU7r.js} +1 -1
  64. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-2l1U4rgJ.js → sizeCapture-X5ZJPWSS-B4GUFW92.js} +1 -1
  65. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BX1FbrhJ.js → stateDiagram-OKZ733FA-Dsad2MXf.js} +1 -1
  66. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +1 -0
  67. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DOSnT7hb.js → swimlanes-SLNWSIFB-DGq48Fbi.js} +2 -2
  68. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +8 -0
  69. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DVJQA7AN.js → timeline-definition-Z64GVDOM-CSSi6OFf.js} +1 -1
  70. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-74uxl9gx.js → vennDiagram-T6HMQDX7-BqyzPrWv.js} +1 -1
  71. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DsBxlcg_.js → wardleyDiagram-T6FBY63Y-DlznVb6S.js} +1 -1
  72. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-snuW9Mjw.js → xychartDiagram-ELKLHX3M-CfBcgJ_K.js} +1 -1
  73. package/dist/worker/console/static/index.html +2 -2
  74. package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +328 -0
  75. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +56 -4
  76. package/dist/workflows/dag/backend-test-scenario-partitions.js +73 -1
  77. package/dist/workflows/dag/frontend-implementation-contract.js +92 -14
  78. package/dist/workflows/dag/frontend-prewrite-gate.js +106 -11
  79. package/dist/workflows/dag/init-hybrid.js +44 -11
  80. package/dist/workflows/dag/node-execution.js +26 -0
  81. package/dist/workflows/dag/prompt.js +23 -1
  82. package/dist/workflows/dag/types.js +6 -0
  83. package/dist/workflows/dag/upstream-artifacts.js +4 -0
  84. package/docs/operations/local-development-environment.md +1 -5
  85. package/docs/skills/vetted-skill-registry.md +14 -0
  86. package/docs/templates/README.md +1 -1
  87. package/docs/templates/backend-test-dag.json +2 -2
  88. package/package.json +8 -4
  89. package/skills/analyze-product-requirements/SKILL.md +8 -5
  90. package/skills/analyze-product-requirements/references/acceptance-criteria.md +1 -1
  91. package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +10 -6
  92. package/skills/analyze-product-requirements/references/example.md +50 -0
  93. package/skills/analyze-product-requirements/references/forward-test-cases.md +11 -7
  94. package/skills/analyze-product-requirements/references/kb-integration.md +5 -5
  95. package/skills/analyze-product-requirements/references/product-analysis-schema.md +8 -4
  96. package/skills/analyze-product-requirements/references/product-requirement-schema.md +2 -2
  97. package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +1 -1
  98. package/skills/analyze-product-requirements/scripts/test-validators.mjs +11 -1
  99. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +20 -1
  100. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +9 -0
  101. package/skills/improve-codebase-architecture/SKILL.md +81 -0
  102. package/skills/improve-codebase-architecture/deepening.md +37 -0
  103. package/skills/improve-codebase-architecture/html-report.md +123 -0
  104. package/skills/improve-codebase-architecture/interface-design.md +44 -0
  105. package/skills/improve-codebase-architecture/language.md +53 -0
  106. package/dist/worker/console/static/assets/channel-R6oQIDkw.js +0 -1
  107. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BPXlmPWI.js +0 -1
  108. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BPXlmPWI.js +0 -1
  109. package/dist/worker/console/static/assets/index-CuH5CNFu.css +0 -1
  110. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DJKi0Qel.js +0 -1
  111. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-YYe7EEm5.js +0 -8
package/CHANGELOG.md CHANGED
@@ -4,10 +4,15 @@
4
4
 
5
5
  ### 变更
6
6
 
7
+ - 将 `@earendil-works/pi-ai` 与 `@earendil-works/pi-coding-agent` 从可选依赖提升为精确 pin 的运行时依赖 `0.83.0`,将运行时直接导入的 `typebox` 精确声明为 `1.3.7`,并在根包声明 Node.js `>=22.19.0`。安装 `@tea-agent/loop-agent` 会默认装上 Pi SDK(含 `--omit=optional` 场景);`@cursor/sdk` 仍保持 optional。availability / readiness 探针、隔离 tarball canary、发布包契约测试与安装/本地环境说明同步对齐。
8
+ - backend-test Scenario Partition 仅接受绑定源有限 enum/分类集合的 GET/list query/path 轴;非法 Domain(空/重复/保留 token、无有限域、与 OpenAPI enum 不一致)报 `INVALID_PARTITION_DOMAIN` / `NO_FINITE_DOMAIN` / `PARTITION_SOURCE_MISMATCH` 根因,不再生成空后缀 `TP-SP-...-`、重复 slot,也不再级联 `MISSING_*_SLOT`。N6 对这些根因码 fail-closed;合法源分区缺槽仍阻断。N2 禁止为自由串/主键/required-only/boundary-only 建分区;N5 允许删除无源非法 Partition 及其派生槽位。
7
9
  - npm `next` 渠道发布改为提交数节流:main push 仅当距上次 next 发布的 source commit ≥ 5 个 commit 时才自动发布(新增 `scripts/next-publish-gate.mjs`,读已发布 tarball build-stamp 对比 HEAD);不足阈值时 workflow 绿色跳过,手动 `workflow_dispatch` 不受限(切 release 线前强制出包 / 紧急补发)。发布文档真源 §4.1/§5.1/§7/§12 同步。
8
10
 
9
11
  ### 新增
10
12
 
13
+ - Pi SDK 终态 session-events JSONL 顶层增加可选 `finish_reason`:仅当 `message_end` / `turn_end` / `assistant_message` 明确携带非空终止原因时由序列化层提升,不伪造默认值,也不回流 `stopReason` 诊断。
14
+ - `backendTest` 布局配置纳入 Task Contract managed 注入面:`task advance` 新增 `--backend-test <json>`,可在公共任务生命周期内把 `testRoot`/`markdownRel`/`scriptRel`/`sharedSetupDoc`/`mode`/`gapDoc`/`executeScope` 写进 `task.json`,不再需要手工编辑(手工编辑会让任务漂移成 `externally-modified` 并拒绝 `--approve-gate`)。给出即整体替换、省略即保留既有配置(历史手写配置因此自愈)、`'{}'` 为显式清空回默认布局;重复 advance 幂等,契约保持 `managed`。`task contract diff` 把值变更标 routine `BACKEND_TEST_CONFIG_CHANGED`、丢键/清空额外标 elevated `BACKEND_TEST_CONFIG_WEAKENED`。
15
+ - Operator Chat 文件预览抽屉新增图标「新窗口打开」:用当前已加载内容在独立浏览器窗口打开全页阅读视图,窗口内可切换「预览 / 查看原文」(带宽高的 `window.open` popup,Markdown 排版、HTML 铺满视口的空 sandbox iframe、JSON pretty-print / 其它文本等宽滚动);打开成功后断开 `opener` 且不立刻 revoke Blob,避免空白页;弹窗被拦截时 header 附近报错且不关闭抽屉
11
16
  - backend-test 适配 A–D:可配置 `backendTest` 布局(`testRoot`/`markdownRel`/`scriptRel`)、用户公共前置文档引用与模块顶部 `user_shared_setup`(不写 conftest)、查询轴 Scenario Partitions 确定性展开与 fail-closed 门禁、`mode=gap-fill` 13 节点增量 DAG。Slot ID 以声明的 Partition ID 为真源(`TP-<Partition ID>-...`)。
12
17
  - 前端 plan 唯一机器契约化(JSON-only):`frontend-plan-pi` / `frontend-plan-revision-pi` 输出收敛为「一行导语 + 唯一 fenced `json` 契约 + 唯一 `openspec-citations` 引用块」,彻底移除「可选 Markdown 说明」;plan.md(`contracts/frontend-plan.md`)改由 prewrite gate 用纯函数 `renderFrontendPlanMarkdown` 确定性渲染(不新增 DAG 节点/不新增 Pi 调用)
13
18
  - 契约 schema 新增 5 个可选字段(`implementationSteps` / `stylingStrategy` / `dependencyPolicy` / `residualRisks` / `realIntegrationGap`),`schemaVersion` 保持 1;`docs/templates/frontend-implementation-contract.schema.json` 同步
@@ -49,6 +54,7 @@
49
54
 
50
55
  ### 修复
51
56
 
57
+ - 前端实现 DAG 的 plan/revision 改为 artifact-first:成功节点以 SHA-256 绑定的 canonical contract 或 merge-patch artifact 供 prewrite 与评审消费,citation/raw 仅保留引用;prewrite 对新记录先校验节点状态、schema、run 内路径和哈希,失败不再回退解析模型 prose。plan/revision 输出限制为 14 KiB、canonical contract 限制为 16 KiB,citation JSON 与主契约隔离解析;下游不再内联或截断完整契约,provider `stopReason=length` 改用紧凑恢复提示。
52
58
  - Console 对话中的 Markdown 表格只约束横向滚动,不再截断纵向滚动链;鼠标或触控板停在表格上时仍可继续上下滚动对话。
53
59
  - scenario-param 确定性 rewrite 的 Python 语法校验:Windows 优先 `python`,并把 Microsoft Store `python3` 空壳(非 0 且无语法诊断)视为解释器不可用后回退,避免误回滚合法 rewrite。
54
60
  - `frontend-plan-revision-pi` 节点级校验与 merge-patch 输出协议矛盾修复:新增 schemaId `frontend-implementation-contract-revision-patch-v1` 与格式级宽松校验 `validateFrontendRevisionPatchNodeOutput`(至少一个 `openspec-citations` 块 + 剥离引用块后恰好一个 JSON 对象,不做完整契约 schema 校验),revision 节点 `structuredContractOutput.schemaId` 切换(plan-pi 仍为 v1);node-execution 的 invalid-output retry 指令按 schemaId 分支,patch 协议提示 merge-patch delta + 引用块,消除「prompt 要求 patch、retry 要求完整契约」的 retryable 死循环;gate 仍是唯一权威合并/校验点(零改动)。ADR 0015 补「节点级校验」决策 7
package/README.md CHANGED
@@ -24,6 +24,8 @@
24
24
 
25
25
  ### 安装
26
26
 
27
+ 需要 Node.js `>=22.19.0`。建议先运行 `node --version` 确认环境满足要求;启用 npm `engine-strict` 时,旧版本 Node.js 会直接拒绝安装。
28
+
27
29
  ```bash
28
30
  npm install -g @tea-agent/loop-agent@latest
29
31
  loop-agent --version
@@ -38,6 +38,7 @@ function hasEngineeringBoundary(input) {
38
38
  return Boolean((input.allowedPaths && input.allowedPaths.length > 0) ||
39
39
  (input.forbiddenPaths && input.forbiddenPaths.length > 0) ||
40
40
  (input.verifyCommands && input.verifyCommands.length > 0) ||
41
+ input.backendTest !== undefined ||
41
42
  input.fromText ||
42
43
  input.fromDraftPath);
43
44
  }
@@ -632,6 +633,7 @@ export async function advanceTaskLifecycle(input) {
632
633
  allowedPaths: effectiveInput.allowedPaths,
633
634
  forbiddenPaths: input.forbiddenPaths,
634
635
  verifyCommands: effectiveInput.verifyCommands,
636
+ backendTest: input.backendTest,
635
637
  },
636
638
  });
637
639
  if (!prepareResult.ok) {
@@ -687,6 +689,7 @@ export async function advanceTaskLifecycle(input) {
687
689
  allowedPaths: effectiveInput.allowedPaths,
688
690
  forbiddenPaths: input.forbiddenPaths,
689
691
  verifyCommands: effectiveInput.verifyCommands,
692
+ backendTest: input.backendTest,
690
693
  },
691
694
  });
692
695
  if (semantic.ok) {
@@ -707,6 +710,7 @@ export async function advanceTaskLifecycle(input) {
707
710
  allowedPaths: effectiveInput.allowedPaths,
708
711
  forbiddenPaths: input.forbiddenPaths,
709
712
  verifyCommands: effectiveInput.verifyCommands,
713
+ backendTest: input.backendTest,
710
714
  },
711
715
  });
712
716
  if (retry.ok) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.38.2",
4
- "gitSha": "ba66152b71609e67330471e5325c1efddd200412",
5
- "builtAt": "2026-08-20T07:31:23.829Z"
3
+ "version": "0.39.0-beta.0",
4
+ "gitSha": "cf0cf7e8e996da8d1c727ccc7cb8f4c52f8d5319",
5
+ "builtAt": "2026-08-21T03:47:18.913Z"
6
6
  }
@@ -1,4 +1,5 @@
1
1
  import { advanceTaskLifecycle, } from "../application/task-lifecycle/index.js";
2
+ import { backendTestConfigSchema, } from "../task/config-types.js";
2
3
  import { buildOperatorResult, operatorFailed, processExitCodeForOutcome, writeOperatorJson, } from "../shared/operator/index.js";
3
4
  import { createRunDagProgressObserver, validateRunDagProgressIntervalMs, } from "./run-dag-progress.js";
4
5
  const COMMAND = "task advance";
@@ -14,6 +15,7 @@ const USAGE = `usage:
14
15
  [--forbidden-path <glob>]...
15
16
  [--verify <label:command>]...
16
17
  [--verify-timeout <label:timeoutMs>]...
18
+ [--backend-test <json>]
17
19
  [--accept-recommendations <digest>]
18
20
  [--approve-gate <gate-id:digest>]
19
21
  [--reject-gate <gate-id:digest> --reason <text>]
@@ -52,6 +54,30 @@ function parseVerifyTimeout(flag) {
52
54
  }
53
55
  return { label: flag.slice(0, idx), timeoutMs };
54
56
  }
57
+ /**
58
+ * Parse `--backend-test <json>` into a validated managed backendTest config.
59
+ * `{}` is legal (explicit clear back to the default layout).
60
+ */
61
+ function parseBackendTestFlag(raw) {
62
+ let parsed;
63
+ try {
64
+ parsed = JSON.parse(raw);
65
+ }
66
+ catch {
67
+ throw new Error(`invalid --backend-test ${raw}; expected an inline JSON object`);
68
+ }
69
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
70
+ throw new Error(`invalid --backend-test ${raw}; expected an inline JSON object`);
71
+ }
72
+ const result = backendTestConfigSchema.safeParse(parsed);
73
+ if (!result.success) {
74
+ const summary = result.error.issues
75
+ .map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`)
76
+ .join("; ");
77
+ throw new Error(`invalid --backend-test: ${summary}`);
78
+ }
79
+ return result.data ?? {};
80
+ }
55
81
  export function parseTaskAdvanceArgs(args) {
56
82
  const options = {};
57
83
  const positional = [];
@@ -113,6 +139,10 @@ export function parseTaskAdvanceArgs(args) {
113
139
  options.verifyTimeout = pushList(options.verifyTimeout, next());
114
140
  continue;
115
141
  }
142
+ if (token === "--backend-test") {
143
+ options.backendTest = parseBackendTestFlag(next());
144
+ continue;
145
+ }
116
146
  if (token === "--accept-recommendations") {
117
147
  options.acceptRecommendations = next();
118
148
  continue;
@@ -273,6 +303,7 @@ export function toUseCaseInput(repoRoot, taskId, options, observer) {
273
303
  allowedPaths: options.allowedPaths,
274
304
  forbiddenPaths: options.forbiddenPaths,
275
305
  verifyCommands: verifyCommands.length > 0 ? verifyCommands : undefined,
306
+ backendTest: options.backendTest,
276
307
  approveGateToken: options.approveGate,
277
308
  rejectGateToken: options.rejectGate,
278
309
  rejectReason: options.reason,
@@ -51,9 +51,50 @@ export function normalizePiSessionEvent(event) {
51
51
  }
52
52
  return unifyToolPayloadFields(event);
53
53
  }
54
+ const TERMINAL_FINISH_REASON_TYPES = new Set([
55
+ 'message_end',
56
+ 'turn_end',
57
+ 'assistant_message',
58
+ ]);
59
+ function readExplicitFinishReason(source) {
60
+ const message = isRecord(source.message) ? source.message : undefined;
61
+ const candidates = [
62
+ source.finish_reason,
63
+ source.finishReason,
64
+ source.stop_reason,
65
+ source.stopReason,
66
+ message?.finish_reason,
67
+ message?.finishReason,
68
+ message?.stop_reason,
69
+ message?.stopReason,
70
+ ];
71
+ for (const candidate of candidates) {
72
+ if (typeof candidate === 'string' && candidate.trim().length > 0) {
73
+ return candidate;
74
+ }
75
+ }
76
+ return undefined;
77
+ }
78
+ function applyFinishReason(original, normalized) {
79
+ const originalType = typeof original.type === 'string' ? original.type : undefined;
80
+ if (!originalType || !TERMINAL_FINISH_REASON_TYPES.has(originalType)) {
81
+ return normalized;
82
+ }
83
+ const existing = readExplicitFinishReason({
84
+ finish_reason: normalized.finish_reason,
85
+ });
86
+ if (existing !== undefined) {
87
+ return normalized;
88
+ }
89
+ const promoted = readExplicitFinishReason(original);
90
+ if (promoted === undefined) {
91
+ return normalized;
92
+ }
93
+ return { ...normalized, finish_reason: promoted };
94
+ }
54
95
  /** Serialize one SDK session event as a single JSONL line. */
55
96
  export function serializeSessionEvent(event, options) {
56
- const normalized = normalizePiSessionEvent(event);
97
+ const normalized = applyFinishReason(event, normalizePiSessionEvent(event));
57
98
  if (typeof normalized.recordedAt !== 'string') {
58
99
  normalized.recordedAt = options?.recordedAt ?? new Date().toISOString();
59
100
  }
@@ -408,6 +408,50 @@ export class BoundedTextPreview {
408
408
  ].join("\n");
409
409
  }
410
410
  }
411
+ /**
412
+ * Normalize provider-specific completion-limit labels to the DAG's canonical
413
+ * `length` signal. Other terminal reasons remain observable without being
414
+ * treated as truncation.
415
+ */
416
+ export function normalizeProviderStopReason(value) {
417
+ if (typeof value !== "string" || value.trim().length === 0)
418
+ return undefined;
419
+ const trimmed = value.trim();
420
+ const compact = trimmed.toLowerCase().replace(/[\s_-]/g, "");
421
+ if ([
422
+ "length",
423
+ "maxtokens",
424
+ "maxoutputtokens",
425
+ "outputlimit",
426
+ "tokenlimit",
427
+ "tokenlimitreached",
428
+ ].includes(compact)) {
429
+ return "length";
430
+ }
431
+ return trimmed;
432
+ }
433
+ /**
434
+ * Extract a terminal stop reason from the provider event shapes used by Pi
435
+ * SDK/CLI bridges. Providers disagree on `stop*` versus `finish*`; keep this
436
+ * boundary centralized so downstream DAG recovery sees one canonical signal.
437
+ */
438
+ export function extractPiEventStopReason(event) {
439
+ const nested = [event.message, event.response, event.completion].filter(isRecord);
440
+ const containers = [...nested, event];
441
+ for (const container of containers) {
442
+ for (const key of [
443
+ "stopReason",
444
+ "stop_reason",
445
+ "finishReason",
446
+ "finish_reason",
447
+ ]) {
448
+ const normalized = normalizeProviderStopReason(container[key]);
449
+ if (normalized)
450
+ return normalized;
451
+ }
452
+ }
453
+ return undefined;
454
+ }
411
455
  export class PiJsonlStreamCollector {
412
456
  lineBuffer = "";
413
457
  discardUntilNewline = false;
@@ -415,6 +459,7 @@ export class PiJsonlStreamCollector {
415
459
  parsedEvents = 0;
416
460
  tokensUsed = 0;
417
461
  outputTooLarge = false;
462
+ stopReason;
418
463
  subagentTotalCalls = 0;
419
464
  subagentFailedCalls = 0;
420
465
  subagentModes = new Set();
@@ -454,6 +499,7 @@ export class PiJsonlStreamCollector {
454
499
  assistantText: this.assistantText.trim(),
455
500
  outputTooLarge: this.outputTooLarge,
456
501
  parsedEvents: this.parsedEvents,
502
+ stopReason: this.stopReason,
457
503
  subagentStats: this.formatSubagentStats(),
458
504
  tokensUsed: this.tokensUsed,
459
505
  };
@@ -472,6 +518,10 @@ export class PiJsonlStreamCollector {
472
518
  }
473
519
  }
474
520
  consumeEvent(event) {
521
+ const stopReason = extractPiEventStopReason(event);
522
+ if (stopReason === "length" || !this.stopReason) {
523
+ this.stopReason = stopReason ?? this.stopReason;
524
+ }
475
525
  const candidate = extractAssistantTextFromEvent(event);
476
526
  if (candidate !== undefined) {
477
527
  if (candidate.length > MAX_ASSISTANT_TEXT_CHARS) {
@@ -865,6 +915,7 @@ async function executeSingleCliAttempt(options, modelConfig, promptFilePath) {
865
915
  stdoutTruncated: stdoutPreview.truncated,
866
916
  stderrTruncated: stderrPreview.truncated,
867
917
  outputTooLarge: collected.outputTooLarge,
918
+ stopReason: collected.stopReason,
868
919
  timedOut,
869
920
  tokensUsed: collected.tokensUsed,
870
921
  subagentStats: collected.subagentStats,
@@ -1,6 +1,6 @@
1
1
  import { appendFile, mkdir } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { BoundedTextPreview, classifyPiFailure, createPiJsonlStreamCollector, DEFAULT_ABORT_GRACE_MS, DEFAULT_STALL_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, extractAssistantTextFromPiJson, } from "./pi-executor.js";
3
+ import { BoundedTextPreview, classifyPiFailure, createPiJsonlStreamCollector, DEFAULT_ABORT_GRACE_MS, DEFAULT_STALL_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, extractPiEventStopReason, extractAssistantTextFromPiJson, } from "./pi-executor.js";
4
4
  import { serializeSessionEvent } from "./pi-event-serializer.js";
5
5
  import { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY, } from "../shared/pi-retry-settings.js";
6
6
  let sdkSessionFactoryOverride;
@@ -44,15 +44,15 @@ export async function resetPiSdkReuseScopeForTests() {
44
44
  export function setPiSdkSessionFactoryForTests(factory) {
45
45
  sdkSessionFactoryOverride = factory;
46
46
  }
47
- /** Test hook: simulate SDK import success/failure without relying on optionalDependency install state. */
47
+ /** Test hook: simulate SDK import success/failure without relying on installed SDK state. */
48
48
  export function setPiSdkImportOverrideForTests(fn) {
49
49
  sdkImportOverrideForTests = fn;
50
50
  }
51
- /** Test hook: inject a mock Pi SDK module to exercise resolveSdkSessionFactory without optionalDependency. */
51
+ /** Test hook: inject a mock Pi SDK module to exercise resolveSdkSessionFactory without loading the real SDK. */
52
52
  export function setPiSdkModuleOverrideForTests(fn) {
53
53
  sdkModuleOverrideForTests = fn;
54
54
  }
55
- /** Check whether the Pi SDK optional dependency satisfies the 0.80.10 runtime contract. */
55
+ /** Check whether the Pi SDK runtime dependency satisfies the 0.83.0 runtime contract. */
56
56
  export async function checkPiSdkAvailability(_repoRoot) {
57
57
  if (sdkSessionFactoryOverride) {
58
58
  return { ok: true, detail: "pi SDK session factory override active" };
@@ -68,10 +68,10 @@ export async function checkPiSdkAvailability(_repoRoot) {
68
68
  typeof ModelRuntime?.create !== "function") {
69
69
  return {
70
70
  ok: false,
71
- detail: "pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.80.10 contract)",
71
+ detail: "pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.83.0 contract)",
72
72
  };
73
73
  }
74
- return { ok: true, detail: "pi SDK 0.80.10 contract available" };
74
+ return { ok: true, detail: "pi SDK 0.83.0 contract available" };
75
75
  }
76
76
  catch (error) {
77
77
  const message = error instanceof Error ? error.message : String(error);
@@ -404,25 +404,6 @@ function extractSdkUsageSample(event) {
404
404
  const responseKey = responseKeyCandidates.find((value) => typeof value === "string" && value.length > 0);
405
405
  return responseKey ? { responseKey, tokens } : { tokens };
406
406
  }
407
- /** Extract a non-empty stop reason from a terminal SDK event.
408
- * Probes multiple field shapes across SDK versions so a missing field fails
409
- * open (undefined) rather than misclassifying an attempt. */
410
- function readStopReason(event) {
411
- const message = isRecord(event.message) ? event.message : undefined;
412
- const candidates = [
413
- message?.stop_reason,
414
- message?.stopReason,
415
- event.stopReason,
416
- event.stop_reason,
417
- ];
418
- for (const candidate of candidates) {
419
- if (typeof candidate === "string" &&
420
- candidate.trim().length > 0) {
421
- return candidate.trim();
422
- }
423
- }
424
- return undefined;
425
- }
426
407
  function aggregateSdkTokenUsage(samples) {
427
408
  const identified = new Map();
428
409
  let anonymousMaximum = 0;
@@ -494,7 +475,7 @@ export async function executeSingleSdkAttempt(options) {
494
475
  type === "message_end" ||
495
476
  type === "agent_end" ||
496
477
  type === "agent_settled") {
497
- const candidate = readStopReason(event);
478
+ const candidate = extractPiEventStopReason(event);
498
479
  if (candidate)
499
480
  stopReason = candidate;
500
481
  }
@@ -704,7 +704,7 @@ async function executeBackendTestPipeline(input, meta) {
704
704
  // coverage findings stay advisory, but a declared filter axis missing
705
705
  // an each-value/omitted slot or its complement slot must block pytest
706
706
  // generation until the Markdown declares the slot.
707
- const partitionBlocking = coverage.facts.findings.filter((finding) => /^(?:MISSING_PARTITION|MISSING_PARTITION_SLOT|MISSING_COMPLEMENT_SLOT|MISSING_PARTITION_TABLE)/.test(finding));
707
+ const partitionBlocking = coverage.facts.findings.filter((finding) => /^(?:MISSING_PARTITION|MISSING_PARTITION_SLOT|MISSING_COMPLEMENT_SLOT|MISSING_PARTITION_TABLE|INVALID_PARTITION_DOMAIN|NO_FINITE_DOMAIN|PARTITION_SOURCE_MISMATCH)/.test(finding));
708
708
  if (partitionBlocking.length > 0) {
709
709
  return {
710
710
  ok: false,
@@ -877,7 +877,7 @@ async function executeBackendTestPipeline(input, meta) {
877
877
  });
878
878
  }
879
879
  else if (initial.status === "REPAIRABLE") {
880
- const mappedScripts = await collectBackendTestMappedPytestScripts(input.cwd);
880
+ const mappedScripts = await collectBackendTestMappedPytestScripts(input.cwd, backendLayout);
881
881
  const scenario = await runBackendScenarioParamPreflight({
882
882
  workspaceRoot: input.cwd,
883
883
  runDir: meta.runDir,
@@ -17,6 +17,7 @@ export const MANAGED_TASK_CONFIG_FIELDS = [
17
17
  "forbiddenPaths",
18
18
  "hardConstraints",
19
19
  "verifyCommands",
20
+ "backendTest",
20
21
  ];
21
22
  export const CONTRACT_FILE_NAME = "contract.json";
22
23
  export const TRANSACTIONS_DIR_NAME = ".transactions";
@@ -1,3 +1,4 @@
1
+ import { canonicalizeJsonValue } from "./canonicalize.js";
1
2
  import { MANAGED_TASK_CONFIG_FIELDS } from "./constants.js";
2
3
  function maxRisk(a, b) {
3
4
  const order = ["routine", "elevated", "blocking"];
@@ -139,6 +140,31 @@ export function diffDraftAgainstCurrent(input) {
139
140
  path: "task.json#featureId",
140
141
  });
141
142
  }
143
+ // backendTest is managed whole-object: an absent draft value means
144
+ // "keep existing" and never diffs; declared values compare canonically.
145
+ if (input.draft.backendTest !== undefined) {
146
+ const before = cfg.backendTest ?? {};
147
+ const after = input.draft.backendTest;
148
+ if (canonicalizeJsonValue(before) !== canonicalizeJsonValue(after)) {
149
+ changedManagedFields.push("backendTest");
150
+ risks.push({
151
+ code: "BACKEND_TEST_CONFIG_CHANGED",
152
+ level: "routine",
153
+ message: "backendTest configuration changed",
154
+ path: "task.json#backendTest",
155
+ });
156
+ const afterKeys = new Set(Object.keys(after));
157
+ const droppedKeys = Object.keys(before).filter((key) => !afterKeys.has(key));
158
+ if (droppedKeys.length > 0) {
159
+ risks.push({
160
+ code: "BACKEND_TEST_CONFIG_WEAKENED",
161
+ level: "elevated",
162
+ message: `drop backendTest keys: ${droppedKeys.sort().join(", ")}`,
163
+ path: "task.json#backendTest",
164
+ });
165
+ }
166
+ }
167
+ }
142
168
  }
143
169
  else {
144
170
  for (const field of MANAGED_TASK_CONFIG_FIELDS) {
@@ -174,6 +174,11 @@ export function mergeManagedTaskConfigFields(existing, draft) {
174
174
  if (draft.featureId !== undefined) {
175
175
  next.featureId = draft.featureId;
176
176
  }
177
+ // backendTest is whole-object replace when the draft declares it (`{}` is a
178
+ // legal explicit clear back to the default layout); absent keeps existing.
179
+ if (draft.backendTest !== undefined) {
180
+ next.backendTest = draft.backendTest;
181
+ }
177
182
  return next;
178
183
  }
179
184
  /**
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { taskKindSchema, taskVerifyCommandSchema } from "../config-types.js";
2
+ import { backendTestConfigSchema, taskKindSchema, taskVerifyCommandSchema, } from "../config-types.js";
3
3
  import { TASK_CONFIG_SCHEMA_VERSION, TASK_CONTRACT_CANONICALIZER_VERSION, TASK_CONTRACT_DRAFT_SCHEMA_VERSION, TASK_CONTRACT_PROJECTION_VERSION, TASK_CONTRACT_REF_SCHEMA_VERSION, } from "./constants.js";
4
4
  const sha256Schema = z
5
5
  .string()
@@ -62,6 +62,11 @@ export const taskContractDraftSchema = z
62
62
  requirement: taskContractDraftRequirementSchema,
63
63
  constraints: taskContractDraftConstraintsSchema,
64
64
  verification: taskContractDraftVerificationSchema,
65
+ /**
66
+ * Managed backend-test layout / pipeline configuration projected into
67
+ * task.json#backendTest. Optional: absent means "keep existing".
68
+ */
69
+ backendTest: backendTestConfigSchema,
65
70
  references: z.array(taskContractDraftReferenceSchema).optional(),
66
71
  sourceResolutions: z
67
72
  .array(taskContractDraftSourceResolutionSchema)
@@ -22,6 +22,18 @@ function resolveTaskKindExplicit(flags, baseDraft) {
22
22
  return flags.taskKindExplicit;
23
23
  return baseDraft?.taskKindExplicit ?? false;
24
24
  }
25
+ /**
26
+ * Resolve managed backend-test layout config.
27
+ * Precedence: explicit CLI flags > --from-draft base > existing task.json
28
+ * (the last tier self-heals tasks whose backendTest was hand-written).
29
+ */
30
+ function resolveBackendTest(flags, baseDraft, existing) {
31
+ if (flags.backendTest !== undefined)
32
+ return flags.backendTest;
33
+ if (baseDraft?.backendTest !== undefined)
34
+ return baseDraft.backendTest;
35
+ return existing?.backendTest ?? undefined;
36
+ }
25
37
  function resolveTitle(taskId, flags, facts, baseDraft, existing) {
26
38
  return (trimNonEmpty(flags.title) ??
27
39
  trimNonEmpty(baseDraft?.title) ??
@@ -205,6 +217,9 @@ export function buildPrepareDraft(input) {
205
217
  };
206
218
  if (featureId)
207
219
  draft.featureId = featureId;
220
+ const backendTest = resolveBackendTest(flags, baseDraft, existingTaskConfig);
221
+ if (backendTest !== undefined)
222
+ draft.backendTest = backendTest;
208
223
  if (references && references.length > 0) {
209
224
  draft.references = references.map((ref) => ({
210
225
  role: ref.role,
@@ -85,7 +85,7 @@ function hasProviderConfig(snapshot) {
85
85
  /**
86
86
  * Discover live Pi SDK / auth / model readiness via ModelRuntime.
87
87
  * Kept inside worker/console (dynamic import) so Worker does not depend on
88
- * executors; contract matches pi-sdk-executor 0.80.10.
88
+ * executors; contract matches pi-sdk-executor 0.83.0.
89
89
  */
90
90
  export async function discoverPiReadinessProbe(options) {
91
91
  const repoRoot = options?.repoRoot ?? process.cwd();
@@ -118,7 +118,7 @@ export async function discoverPiReadinessProbe(options) {
118
118
  modelsAvailable: false,
119
119
  interviewSessionOk: false,
120
120
  details: {
121
- sdkMessage: "Pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.80.10)",
121
+ sdkMessage: "Pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.83.0)",
122
122
  authMessage: "skipped (sdk incompatible)",
123
123
  modelsMessage: "skipped (sdk incompatible)",
124
124
  sessionMessage: "skipped (sdk incompatible)",
@@ -159,7 +159,7 @@ export async function discoverPiReadinessProbe(options) {
159
159
  modelsAvailable: false,
160
160
  interviewSessionOk: false,
161
161
  details: {
162
- sdkMessage: "Pi SDK 0.80.10 contract available",
162
+ sdkMessage: "Pi SDK 0.83.0 contract available",
163
163
  authMessage: `ModelRuntime.create failed: ${message}`,
164
164
  modelsMessage: "skipped (runtime init failed)",
165
165
  sessionMessage: "skipped (runtime init failed)",
@@ -224,7 +224,7 @@ export async function discoverPiReadinessProbe(options) {
224
224
  interviewSessionOk,
225
225
  piAvailableModels: availableModels,
226
226
  details: {
227
- sdkMessage: `Pi SDK 0.80.10 contract available (agentDir=${agentDir})`,
227
+ sdkMessage: `Pi SDK 0.83.0 contract available (agentDir=${agentDir})`,
228
228
  authMessage: authReady
229
229
  ? `credentials/providers ready (available=${availableCount}, providerConfig=${providerConfigured})`
230
230
  : "no available models and no stored/configured provider credentials (set Pi auth or provider API keys)",
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-BRlfYZoH.js";import{p as f}from"./chunk-JWPE2WC7-D3e-74fG.js";import{_ as n,l as o}from"./mermaid.core-D7Occzk_.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-DV-HypfL.js";import"./index-BXN89VPX.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-DZtolir7.js";import{p as f}from"./chunk-JWPE2WC7-NgHc6V37.js";import{_ as n,l as o}from"./mermaid.core-BVjAT9b8.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-BwT_xKrE.js";import"./index-DTZOKgAn.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-D7Occzk_.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-BVjAT9b8.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};