@tea-agent/loop-agent 0.41.1 → 0.42.0-next.10

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 (302) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +397 -5
  3. package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
  4. package/dist/adapters/context-transfer/pi-session.js +61 -0
  5. package/dist/application/dag/generate-task-dag.js +75 -19
  6. package/dist/application/dag/run-dag.js +41 -0
  7. package/dist/application/task-lifecycle/advance.js +24 -5
  8. package/dist/application/task-lifecycle/observe.js +171 -17
  9. package/dist/application/task-lifecycle/plan-transitions.js +42 -7
  10. package/dist/application/task-lifecycle/recommendations.js +13 -2
  11. package/dist/build-stamp.json +3 -3
  12. package/dist/cli/command-definitions.js +19 -0
  13. package/dist/cli/program.js +29 -2
  14. package/dist/commands/client-recovery.js +3 -0
  15. package/dist/commands/dag-artifact.js +284 -0
  16. package/dist/commands/dag-context.js +184 -0
  17. package/dist/commands/dag-follow-up.js +138 -0
  18. package/dist/commands/dag-rerun-task.js +2 -0
  19. package/dist/commands/dag-rerun.js +206 -1
  20. package/dist/commands/init.js +27 -1
  21. package/dist/commands/task-advance.js +19 -0
  22. package/dist/executors/dag-pi-executor.js +4013 -86
  23. package/dist/executors/pi-executor.js +15 -4
  24. package/dist/executors/pi-read-budget-policy.js +239 -0
  25. package/dist/executors/pi-sdk-executor.js +3 -0
  26. package/dist/executors/shell-executor.js +978 -188
  27. package/dist/executors/shell-write-guard.js +7 -0
  28. package/dist/{worker → infrastructure}/console/app-data.js +4 -0
  29. package/dist/infrastructure/console/artifact-revision-store.js +430 -0
  30. package/dist/infrastructure/console/context-export-store.js +160 -0
  31. package/dist/infrastructure/console/dir-lock.js +132 -0
  32. package/dist/infrastructure/console/operation-store.js +417 -0
  33. package/dist/infrastructure/harness/artifact-store.js +10 -1
  34. package/dist/infrastructure/harness/atomic-write.js +12 -2
  35. package/dist/shared/context-transfer/artifact-revision.js +172 -0
  36. package/dist/shared/context-transfer.js +418 -0
  37. package/dist/shared/dag-failure-category.js +12 -0
  38. package/dist/shared/openspec-spec.js +70 -4
  39. package/dist/shared/operator/capabilities.js +21 -0
  40. package/dist/shared/operator/safe-run-summary.js +1 -0
  41. package/dist/shared/path-safety.js +93 -0
  42. package/dist/shared/preview.js +28 -4
  43. package/dist/task/config-types.js +107 -6
  44. package/dist/task/contract/adopt.js +4 -0
  45. package/dist/task/contract/import-revision.js +4 -0
  46. package/dist/task/contract/project.js +3 -0
  47. package/dist/task/contract/schema.js +2 -1
  48. package/dist/task/frontend-project-capability.js +203 -20
  49. package/dist/task/runtime.js +5 -2
  50. package/dist/task/source-prepare/build-draft.js +3 -3
  51. package/dist/task/source-prepare/fragment-inventory.js +64 -25
  52. package/dist/task/source-prepare/prepare.js +126 -21
  53. package/dist/task/source-prepare/semantic-intake.js +6 -2
  54. package/dist/task/source-references.js +22 -1
  55. package/dist/task/task-demand-routing.js +3 -0
  56. package/dist/worker/console/chat/chat-event-store.js +2 -2
  57. package/dist/worker/console/chat/model-resolver.js +7 -4
  58. package/dist/worker/console/chat/pi-runtime.js +86 -5
  59. package/dist/worker/console/chat/resource-preferences-store.js +1 -1
  60. package/dist/worker/console/chat/routes.js +136 -53
  61. package/dist/worker/console/chat/sdd-data-alignment.js +222 -0
  62. package/dist/worker/console/chat/session-catalog.js +2 -0
  63. package/dist/worker/console/chat/session-store.js +43 -1
  64. package/dist/worker/console/chat/shortcuts.js +18 -2
  65. package/dist/worker/console/chat/user-questions.js +1 -1
  66. package/dist/worker/console/console-update-runtime.js +1 -1
  67. package/dist/worker/console/context-transfer-diagnostics.js +198 -0
  68. package/dist/worker/console/dag-confirmation.js +1 -1
  69. package/dist/worker/console/dag-execution-receipt.js +1 -1
  70. package/dist/worker/console/doctor.js +1 -1
  71. package/dist/worker/console/draft-store.js +1 -1
  72. package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
  73. package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
  74. package/dist/worker/console/human-gate-token.js +1 -1
  75. package/dist/worker/console/index.js +6 -3
  76. package/dist/worker/console/interview/assessment.js +1 -1
  77. package/dist/worker/console/interview/session.js +1 -1
  78. package/dist/worker/console/operation-runner.js +45 -4
  79. package/dist/worker/console/operation-sse.js +1 -1
  80. package/dist/worker/console/operation-wait.js +1 -1
  81. package/dist/worker/console/operator-actions.js +273 -10
  82. package/dist/worker/console/operator-surface-health.js +1 -1
  83. package/dist/worker/console/operator-user-error.js +169 -0
  84. package/dist/worker/console/pi-plugins.js +57 -0
  85. package/dist/worker/console/pi-readiness.js +3 -2
  86. package/dist/worker/console/routes.js +637 -8
  87. package/dist/worker/console/security.js +35 -0
  88. package/dist/worker/console/server.js +29 -4
  89. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-D7oU983t.js → abnfDiagram-N423BO3Z-CcS17TBr.js} +1 -1
  90. package/dist/worker/console/static/assets/{arc-BrKSRdFS.js → arc-COptKq2S.js} +1 -1
  91. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CiXV7L56.js → architectureDiagram-T3A2C74G-h4LKMHjP.js} +1 -1
  92. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D_Hs_gIG.js → blockDiagram-VBNYF7ZC-COA1MOH0.js} +1 -1
  93. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DbM-Lqqh.js → c4Diagram-5PPSVZJV-DJUf0QPm.js} +1 -1
  94. package/dist/worker/console/static/assets/channel-DdBCaOJ6.js +1 -0
  95. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D1M8uqJw.js → chunk-2GRJ4B5K-mtWfKrUX.js} +1 -1
  96. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-4D3RFHT6.js → chunk-2Q5K7J3B-B8pXsxDQ.js} +1 -1
  97. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CdwpY_n9.js → chunk-5RXB4S5H-ipKzByl1.js} +1 -1
  98. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BLeLZ9PG.js → chunk-5VM5RSS4-DYi3Ald_.js} +1 -1
  99. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-CeRNEMvc.js → chunk-6Q2QTUOP-DQtGYoty.js} +1 -1
  100. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CjkHP8Vy.js → chunk-GF5L2VYU-BU0qS2YV.js} +1 -1
  101. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-B54ZUQY-.js → chunk-JWPE2WC7-DVH9dIGE.js} +1 -1
  102. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-4bKaMVmx.js → chunk-KBJHAD2P-Dt60SmgM.js} +1 -1
  103. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-Cyjfznu_.js → chunk-RYQCIY6F-BoaUGuEY.js} +1 -1
  104. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CDN-zGFV.js → chunk-XXDRQBXY-_WHFDTp2.js} +1 -1
  105. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DZFra1GO.js +1 -0
  106. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DZFra1GO.js +1 -0
  107. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-HxJmz411.js → cose-bilkent-JH36ORCC-BxkbTIRd.js} +1 -1
  108. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DtSt5K2w.js → cynefin-VYW2F7L2-Bf2UVnoG.js} +1 -1
  109. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-VtkSpRmG.js → cynefinDiagram-MW4NZA55-Bo23q1J_.js} +1 -1
  110. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bj4DT4du.js → dagre-VZM6K2ZE-Dd_UU49i.js} +1 -1
  111. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Bpuk6BmP.js → diagram-7IWD3JNH-ebUa1a9y.js} +1 -1
  112. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BHYGv_yO.js → diagram-B4RE2ZJO-BucthU8r.js} +1 -1
  113. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DEx3WwnT.js → diagram-LBJQPF4R-BgqNpAkm.js} +1 -1
  114. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Cj0dS5e8.js → diagram-Q27KOJAE-DC4q5NGa.js} +1 -1
  115. package/dist/worker/console/static/assets/{diagram-UB23O5K3-B97h8l21.js → diagram-UB23O5K3-CBqCaMeb.js} +1 -1
  116. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DMSFAhBd.js → ebnfDiagram-BXEA7PRR-CGtnbQ3-.js} +1 -1
  117. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DhVOCbt6.js → erDiagram-JOGREHBK-CG3LUao5.js} +1 -1
  118. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-GlakITWL.js → flowDiagram-UKHOOZJN-D2ZVgoFS.js} +1 -1
  119. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-VxPpu9eQ.js → ganttDiagram-PKOTCBZU-DoYFZiKt.js} +1 -1
  120. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bs_e5DRT.js → gitGraphDiagram-DS77QQ5N-CKoP1s6j.js} +1 -1
  121. package/dist/worker/console/static/assets/index-CAZ2fC_X.css +1 -0
  122. package/dist/worker/console/static/assets/index-vbTcFnFs.js +449 -0
  123. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BXYuU4Uw.js → infoDiagram-6WML65LV-Duofv8p2.js} +1 -1
  124. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C34erCbU.js → ishikawaDiagram-WSZJBQD7-D2nlCkA1.js} +1 -1
  125. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DZCAiOU3.js → journeyDiagram-NVQOT4AX-Dd4IHdus.js} +1 -1
  126. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C9gaTdUm.js → kanban-definition-27J2QSJJ-Bi20AOdt.js} +1 -1
  127. package/dist/worker/console/static/assets/{linear-CvI0Z17U.js → linear-D59YJ9kB.js} +1 -1
  128. package/dist/worker/console/static/assets/{mermaid.core-DMMSIx7j.js → mermaid.core-Bl13LpNa.js} +5 -5
  129. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BKKP0EO6.js → mindmap-definition-FAOFIHXS-C6DLP5eY.js} +1 -1
  130. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-C3LiFbl5.js → pegDiagram-VL7TDLO6-BjO4cy64.js} +1 -1
  131. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-Bb3Om2Dz.js → pieDiagram-7S7Q4E2Y-D7i2qZTG.js} +1 -1
  132. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DNthMjzc.js → quadrantDiagram-CIZ2JOQS-Dx8o_h0y.js} +1 -1
  133. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-miFsr4Tv.js → railroadDiagram-AXF67PYL-B55orI_t.js} +1 -1
  134. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-yXUncmn6.js → requirementDiagram-LRYGKXZP-zWaehp6f.js} +1 -1
  135. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-DLeWKLak.js → sankeyDiagram-W5VNT64P-CcA-pjvD.js} +1 -1
  136. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CI3he1R3.js → sequenceDiagram-SI44F4Z6-BOFbzNHI.js} +1 -1
  137. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B7jfDkS5.js → sizeCapture-X5ZJPWSS-BjAejah1.js} +1 -1
  138. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BmrZod7J.js → stateDiagram-OKZ733FA-DXYwxJwZ.js} +1 -1
  139. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Clg3V9t1.js +1 -0
  140. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CDOlHVSY.js → swimlanes-SLNWSIFB-OYiOch8n.js} +2 -2
  141. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DX1dxAAW.js +8 -0
  142. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cz-XX1gy.js → timeline-definition-Z64GVDOM-cZfH3nmU.js} +1 -1
  143. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-caV4cI8C.js → vennDiagram-T6HMQDX7-BDE7b3E1.js} +1 -1
  144. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-dImPlnIO.js → wardleyDiagram-T6FBY63Y-kyyGy9WJ.js} +1 -1
  145. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CtRYVJcn.js → xychartDiagram-ELKLHX3M-CJj6VTog.js} +1 -1
  146. package/dist/worker/console/static/index.html +7 -2
  147. package/dist/worker/console/static-src/active-run-badge.js +17 -0
  148. package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
  149. package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
  150. package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
  151. package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
  152. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +258 -23
  153. package/dist/worker/console/static-src/operator-chat/useChatThread.js +112 -4
  154. package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -4
  155. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
  156. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
  157. package/dist/worker/console/static-src/shell/console-update-reload.js +52 -0
  158. package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
  159. package/dist/worker/console/workspace-context.js +142 -2
  160. package/dist/worker/console/workspace-registry.js +2 -2
  161. package/dist/worker/continuation/worker-continuation.js +278 -0
  162. package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
  163. package/dist/worker/observe/health.js +1 -1
  164. package/dist/worker/observe/node-transparency.js +572 -0
  165. package/dist/worker/observe/routes.js +55 -1
  166. package/dist/worker/observe/static/api.js +69 -5
  167. package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
  168. package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
  169. package/dist/worker/observe/static/dag-helpers.js +4 -2
  170. package/dist/worker/observe/static/dag-node-purpose.js +5 -0
  171. package/dist/worker/observe/static/inspect-workspace.js +23 -0
  172. package/dist/worker/observe/static/kpi.js +2 -2
  173. package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
  174. package/dist/worker/observe/static/operator-chrome.js +37 -23
  175. package/dist/worker/observe/static/relations.js +2 -2
  176. package/dist/worker/observe/static/router.d.ts +12 -1
  177. package/dist/worker/observe/static/router.js +48 -6
  178. package/dist/worker/observe/static/run-processing.js +4 -2
  179. package/dist/worker/observe/static/shell-chrome.js +2 -2
  180. package/dist/worker/observe/static/state.js +8 -1
  181. package/dist/worker/observe/static/styles.css +1092 -86
  182. package/dist/worker/observe/static/views/dag-inspector.js +1228 -64
  183. package/dist/worker/observe/static/views/dag.js +10 -2
  184. package/dist/worker/observe/static/views/dags.js +3 -1
  185. package/dist/worker/observe/static/views/dashboard.js +14 -6
  186. package/dist/worker/observe/static/views/pool.js +7 -2
  187. package/dist/worker/observe/static/views/run.js +12 -3
  188. package/dist/worker/observe/static/views/session-timeline.js +142 -18
  189. package/dist/worker/observe/static/views/task.js +7 -2
  190. package/dist/worker/outcomes/adapters.js +19 -0
  191. package/dist/worker/outcomes/types.js +1 -0
  192. package/dist/worker/pool/attempt-lease.js +97 -66
  193. package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
  194. package/dist/worker/pool/reconcile.js +46 -1
  195. package/dist/worker/pool/run-store.js +2 -0
  196. package/dist/worker/pool/state-projection.js +2 -0
  197. package/dist/worker/task-spec/workflow-routing.js +8 -3
  198. package/dist/workflows/dag/artifact-bindings.js +149 -0
  199. package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
  200. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +215 -28
  201. package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
  202. package/dist/workflows/dag/backend-test-plan-protocol.js +106 -0
  203. package/dist/workflows/dag/backend-test-scenario-param.js +172 -25
  204. package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
  205. package/dist/workflows/dag/backend-test-writer-completeness.js +104 -12
  206. package/dist/workflows/dag/budget-enforcement.js +10 -0
  207. package/dist/workflows/dag/context-receipt.js +305 -0
  208. package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
  209. package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
  210. package/dist/workflows/dag/context-transfer/renderers.js +93 -0
  211. package/dist/workflows/dag/contract-validator-registrations.js +1 -2
  212. package/dist/workflows/dag/dag-retry-schema.js +138 -0
  213. package/dist/workflows/dag/frontend-closeout.js +221 -0
  214. package/dist/workflows/dag/frontend-design-policy.js +400 -0
  215. package/dist/workflows/dag/frontend-human-decision.js +182 -0
  216. package/dist/workflows/dag/frontend-implementation-contract.js +1237 -192
  217. package/dist/workflows/dag/frontend-plan-render.js +2 -1
  218. package/dist/workflows/dag/frontend-prewrite-gate.js +256 -350
  219. package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
  220. package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
  221. package/dist/workflows/dag/frontend-recovery-controller.js +226 -0
  222. package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
  223. package/dist/workflows/dag/frontend-recovery-plan.js +21 -10
  224. package/dist/workflows/dag/frontend-recovery-run.js +166 -34
  225. package/dist/workflows/dag/frontend-repair.js +1 -432
  226. package/dist/workflows/dag/frontend-review-context.js +261 -15
  227. package/dist/workflows/dag/frontend-review-findings.js +270 -0
  228. package/dist/workflows/dag/frontend-shadow-dual-write.js +941 -0
  229. package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
  230. package/dist/workflows/dag/frontend-shape-facts.js +419 -0
  231. package/dist/workflows/dag/frontend-shape.js +435 -0
  232. package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
  233. package/dist/workflows/dag/frontend-split-application-service.js +203 -0
  234. package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
  235. package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
  236. package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
  237. package/dist/workflows/dag/frontend-verification-trace.js +252 -25
  238. package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
  239. package/dist/workflows/dag/frontend-writer-admission.js +319 -0
  240. package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
  241. package/dist/workflows/dag/frontend-writer-status.js +256 -0
  242. package/dist/workflows/dag/init-hybrid.js +1174 -562
  243. package/dist/workflows/dag/interrupt-request.js +7 -0
  244. package/dist/workflows/dag/node-execution.js +940 -31
  245. package/dist/workflows/dag/path-safety.js +1 -0
  246. package/dist/workflows/dag/prompt.js +99 -7
  247. package/dist/workflows/dag/recovery-lease.js +170 -0
  248. package/dist/workflows/dag/report.js +37 -1
  249. package/dist/workflows/dag/rerun-feedback.js +315 -1
  250. package/dist/workflows/dag/rerun-plan.js +568 -3
  251. package/dist/workflows/dag/rerun-run.js +642 -18
  252. package/dist/workflows/dag/rerun-task.js +251 -13
  253. package/dist/workflows/dag/retry-policy.js +219 -104
  254. package/dist/workflows/dag/runner.js +596 -121
  255. package/dist/workflows/dag/scheduler.js +133 -20
  256. package/dist/workflows/dag/skill-snapshot.js +17 -0
  257. package/dist/workflows/dag/types.js +426 -22
  258. package/dist/workflows/dag/validate.js +28 -12
  259. package/docs/architecture/runtime-boundaries.md +6 -6
  260. package/docs/architecture/worker-and-feature.md +1 -1
  261. package/docs/examples/README.md +5 -0
  262. package/docs/init-surface.manifest.json +30 -12
  263. package/docs/skills/vetted-skill-registry.md +4 -2
  264. package/docs/templates/README.md +2 -0
  265. package/docs/templates/agent-dag-report.schema.json +8 -2
  266. package/docs/templates/agent-dag.schema.json +33 -1
  267. package/docs/templates/backend-test-dag.json +23 -20
  268. package/docs/templates/frontend-implementation-contract.schema.json +4 -1
  269. package/docs/templates/frontend-implementation-dag.json +89 -0
  270. package/docs/templates/product-line/task.yaml +1 -1
  271. package/docs/templates/spec-registry.schema.json +45 -0
  272. package/harness.json +1 -1
  273. package/package.json +6 -3
  274. package/skills/frontend-bounded-implement/SKILL.md +15 -14
  275. package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
  276. package/skills/frontend-contract/SKILL.md +23 -0
  277. package/skills/frontend-contract/references/contract-protocol.md +34 -0
  278. package/skills/frontend-design-review/SKILL.md +22 -41
  279. package/skills/frontend-plan/SKILL.md +26 -0
  280. package/skills/frontend-plan/references/decision-contract.md +37 -0
  281. package/skills/frontend-plan/references/design-decisions.md +17 -0
  282. package/skills/frontend-review/SKILL.md +20 -15
  283. package/skills/frontend-review/references/review-findings.md +6 -7
  284. package/skills/frontend-scout/SKILL.md +25 -0
  285. package/skills/frontend-scout/references/design-evidence.md +16 -0
  286. package/skills/frontend-scout/references/scout-evidence.md +23 -0
  287. package/skills/frontend-verification/SKILL.md +1 -1
  288. package/skills/loop-agent/references/command-reference.md +7 -0
  289. package/skills/loop-agent/references/hybrid-dag.md +2 -2
  290. package/dist/worker/console/operation-store.js +0 -169
  291. package/dist/worker/console/static/assets/channel-D1xajGpe.js +0 -1
  292. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BmuGTfRr.js +0 -1
  293. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BmuGTfRr.js +0 -1
  294. package/dist/worker/console/static/assets/index-BLzcAykx.js +0 -451
  295. package/dist/worker/console/static/assets/index-Cs9JUSPs.css +0 -1
  296. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-D9ykKEWP.js +0 -1
  297. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BvPOXALT.js +0 -8
  298. package/skills/frontend-implementation/SKILL.md +0 -52
  299. package/skills/frontend-implementation/references/code-standards.md +0 -33
  300. package/skills/frontend-implementation/references/design-spec.md +0 -56
  301. package/skills/frontend-implementation/references/node-contracts.md +0 -31
  302. /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
@@ -13,10 +13,11 @@
13
13
  * Read routes (GET capabilities / sessions) are open to loopback.
14
14
  */
15
15
  import { randomUUID } from "node:crypto";
16
+ import { access } from "node:fs/promises";
16
17
  import { parseAssistantContent } from "./assistant-content.js";
17
18
  import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, } from "./tools.js";
18
19
  import { buildModelCallableToolSchemas } from "./tool-adapter.js";
19
- import { cleanChatTitle, acceptedOperationIdFromResult } from "./session-store.js";
20
+ import { cleanChatTitle, acceptedOperationIdFromResult, isBlankChatSession } from "./session-store.js";
20
21
  import { hashChatTurnPayload, projectChatToolEventPayload, projectCompactSnapshot, } from "./chat-event-store.js";
21
22
  import { projectDeferredTurnSummary, projectDeferredTurnSummaries, } from "./deferred-turn.js";
22
23
  import { THINKING_LEVELS, RuntimeSnapshotUnavailableError, } from "./pi-runtime.js";
@@ -41,7 +42,13 @@ import { openSseResponse, writeSseEvent } from "../operation-sse.js";
41
42
  import { buildTodoCompactionInstructions, projectCurrentTodos } from "./todos.js";
42
43
  import { buildGoalCompactionInstructions, buildGoalContinuationContext, } from "./goals.js";
43
44
  import { classifyGoalRoundAdmission, } from "./goal-round-driver.js";
44
- import { isTerminalOperationState } from "../operation-store.js";
45
+ import { isTerminalOperationState } from "../../../infrastructure/console/operation-store.js";
46
+ import { SddDataAlignmentError, } from "./sdd-data-alignment.js";
47
+ function projectBrowserChatSession(record) {
48
+ const { telemetrySessionId, ...projected } = record;
49
+ void telemetrySessionId;
50
+ return projected;
51
+ }
45
52
  /**
46
53
  * Reattach the durable event-ring order to session-store rows. The session
47
54
  * store remains the content authority; the ring only supplies chronology.
@@ -468,7 +475,7 @@ function sendRuntimeUnavailable(res, error) {
468
475
  * handled here — only reopen/init readiness failures.
469
476
  */
470
477
  function mapReopenSessionFailure(reopen) {
471
- if (reopen.code === "NO_SESSION_FILE") {
478
+ if (reopen.code === "NO_SESSION_FILE" || reopen.code === "SESSION_FILE_MISSING") {
472
479
  return {
473
480
  status: 404,
474
481
  code: "PI_SESSION_NOT_FOUND",
@@ -488,6 +495,19 @@ function mapReopenSessionFailure(reopen) {
488
495
  message: reopen.message,
489
496
  };
490
497
  }
498
+ /** Drop an orphan catalog record whose promised Pi JSONL is gone. */
499
+ async function healMissingSessionFile(deps, sessionId, reopen) {
500
+ if (reopen && reopen.code !== "NO_SESSION_FILE" && reopen.code !== "SESSION_FILE_MISSING") {
501
+ return;
502
+ }
503
+ if (typeof deps.store.remove !== "function")
504
+ return;
505
+ await deps.store.remove(sessionId).catch(() => undefined);
506
+ }
507
+ async function mapFailedReopen(deps, sessionId, reopen) {
508
+ await healMissingSessionFile(deps, sessionId, reopen);
509
+ return mapReopenSessionFailure(reopen);
510
+ }
491
511
  export async function handleChatCapabilities(_req, res, deps) {
492
512
  const toolSchemas = buildModelCallableToolSchemas();
493
513
  sendJson(res, 200, {
@@ -555,9 +575,15 @@ workspaceId) {
555
575
  });
556
576
  return;
557
577
  }
578
+ let createdSessionId;
579
+ let persisted = false;
558
580
  try {
559
581
  const snapshot = snapshotInput === undefined ? undefined : projectCompactSnapshot(snapshotInput);
560
- // Fast path: durable shell + Console record before full AgentSession ready.
582
+ // A Console record promises that its Pi JSONL can be reopened after a
583
+ // Console restart. Do not make that promise until Pi has finished
584
+ // materializing the session: the old fast path wrote a record for a
585
+ // future JSONL path, leaving an unrecoverable orphan when init failed or
586
+ // the process restarted in that window.
561
587
  const handle = await deps.runtime.createSession({
562
588
  ...(provider && modelId ? { model: { provider, modelId } } : {}),
563
589
  ...(snapshot
@@ -577,56 +603,51 @@ workspaceId) {
577
603
  });
578
604
  return;
579
605
  }
606
+ createdSessionId = handle.sessionId;
607
+ await deps.runtime.ensureSessionReady(handle.sessionId);
608
+ const selection = deps.runtime.getRuntimeSelection(handle.sessionId);
580
609
  const session = await deps.store.create({
581
610
  sessionId: handle.sessionId,
582
611
  sessionFile: handle.sessionFile,
583
- modelProvider: handle.model?.provider,
584
- modelId: handle.model?.modelId,
612
+ modelProvider: selection.model?.provider ?? handle.model?.provider,
613
+ modelId: selection.model?.modelId ?? handle.model?.modelId,
585
614
  // AC-R7-002: explicit parameter from the dispatcher — the runtime
586
615
  // store.create wrapper is gone; body workspaceId (if any) only acts as
587
616
  // a fallback for direct (non-dispatcher) handler tests.
588
617
  workspaceId: workspaceId ?? (typeof body.workspaceId === "string" && body.workspaceId ? body.workspaceId : undefined),
589
618
  });
619
+ persisted = true;
590
620
  await deps.attachSession?.(handle.sessionId).catch(() => undefined);
591
621
  if (snapshot)
592
622
  deps.events.append(handle.sessionId, `${handle.sessionId}:snapshot`, {
593
623
  kind: "compact",
594
624
  data: snapshot,
595
625
  });
596
- // Best-effort: after materialization, patch resolved default model onto the
597
- // durable record. Failures stay on the init fence (PI_SESSION_INIT_FAILED).
598
- if (typeof deps.runtime.ensureSessionReady === "function") {
599
- void deps.runtime
600
- .ensureSessionReady(handle.sessionId)
601
- .then(async () => {
602
- if (typeof deps.runtime.getRuntimeSelection !== "function")
603
- return;
604
- const selection = deps.runtime.getRuntimeSelection(handle.sessionId);
605
- if (!selection?.model)
606
- return;
607
- await deps.store.setRuntimeSelection(handle.sessionId, {
608
- modelProvider: selection.model.provider,
609
- modelId: selection.model.modelId,
610
- });
611
- })
612
- .catch(() => undefined);
613
- }
614
626
  sendJson(res, 201, {
615
627
  ok: true,
616
628
  sessionId: handle.sessionId,
617
629
  sessionFile: handle.sessionFile,
618
630
  model: handle.model,
619
- session,
631
+ session: { ...session, isBlank: isBlankChatSession(session) },
620
632
  activeTools: handle.activeTools,
621
633
  hasBash: true,
622
- ...(handle.initializing ? { initializing: true } : {}),
623
634
  });
624
635
  }
625
636
  catch (error) {
637
+ // Before store.create succeeds, this handle has no durable owner. Dispose
638
+ // it so a failed create cannot survive in-memory until the next restart.
639
+ if (createdSessionId && !persisted)
640
+ deps.runtime.dispose(createdSessionId);
626
641
  const message = error instanceof Error ? error.message : String(error);
627
- sendJson(res, 500, {
642
+ const code = error && typeof error === "object" && "code" in error
643
+ ? String(error.code ?? "")
644
+ : "";
645
+ sendJson(res, code === "PI_SESSION_INIT_FAILED" ? 503 : 500, {
628
646
  ok: false,
629
- error: { code: "CHAT_SESSION_CREATE_FAILED", message },
647
+ error: {
648
+ code: code === "PI_SESSION_INIT_FAILED" ? code : "CHAT_SESSION_CREATE_FAILED",
649
+ message,
650
+ },
630
651
  });
631
652
  }
632
653
  }
@@ -731,10 +752,7 @@ export async function handleBranchChatSession(req, res, deps, sessionId) {
731
752
  sessionFile: record.sessionFile,
732
753
  });
733
754
  if (!reopened.ok) {
734
- const mapped = mapReopenSessionFailure({
735
- code: reopened.code,
736
- message: reopened.message,
737
- });
755
+ const mapped = await mapFailedReopen(deps, sessionId, reopened);
738
756
  sendJson(res, mapped.status, {
739
757
  ok: false,
740
758
  error: { code: mapped.code, message: mapped.message },
@@ -809,7 +827,31 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
809
827
  });
810
828
  return;
811
829
  }
830
+ if (record.sessionFile) {
831
+ try {
832
+ await access(record.sessionFile);
833
+ }
834
+ catch (error) {
835
+ const code = error && typeof error === "object" && "code" in error
836
+ ? String(error.code ?? "")
837
+ : "";
838
+ if (code === "ENOENT") {
839
+ await healMissingSessionFile(deps, sessionId, {
840
+ code: "NO_SESSION_FILE",
841
+ });
842
+ sendJson(res, 404, {
843
+ ok: false,
844
+ error: {
845
+ code: "PI_SESSION_NOT_FOUND",
846
+ message: `Pi session file missing or not openable: ${record.sessionFile}`,
847
+ },
848
+ });
849
+ return;
850
+ }
851
+ }
852
+ }
812
853
  await deps.operationLinker.recoverSession(sessionId);
854
+ const messagePage = await deps.store.listMessages(sessionId, { limit: 50 });
813
855
  const composerDraft = await new ComposerDraftStore(deps.appData).get(sessionId);
814
856
  const snapshot = deps.events.snapshot(sessionId);
815
857
  const telemetry = deriveChatSessionTelemetry(snapshot);
@@ -820,8 +862,8 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
820
862
  ok: true,
821
863
  eventEpoch: deps.events.epoch(sessionId),
822
864
  session: {
823
- ...record,
824
- messages: projectStoredMessagesWithEventOrder(record.messages, snapshot),
865
+ ...projectBrowserChatSession(record),
866
+ messages: projectStoredMessagesWithEventOrder(messagePage.messages, snapshot),
825
867
  composerDraft,
826
868
  activeTurnId: activeTurn?.turnId,
827
869
  turnPhase: activeTurn
@@ -833,6 +875,10 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
833
875
  todos: projectCurrentTodos(snapshot) ?? [],
834
876
  goal: deps.goalRegistry?.get(sessionId) ?? null,
835
877
  },
878
+ history: {
879
+ hasMore: messagePage.hasMore,
880
+ ...(messagePage.nextCursor ? { nextCursor: messagePage.nextCursor } : {}),
881
+ },
836
882
  hasBash: true,
837
883
  sessionStats: telemetry.stats,
838
884
  usage: telemetry.usage,
@@ -1406,6 +1452,7 @@ export async function listChatSessionSummaries(deps, ownerWorkspaceId) {
1406
1452
  : "idle",
1407
1453
  deferredTurn: projectDeferredTurnSummary(session.deferredTurns?.[0]),
1408
1454
  deferredTurns: projectDeferredTurnSummaries(session.deferredTurns ?? []),
1455
+ isBlank: isBlankChatSession(session) && !active,
1409
1456
  };
1410
1457
  });
1411
1458
  }
@@ -1436,7 +1483,7 @@ export async function handlePatchChatSession(req, res, deps, sessionId) {
1436
1483
  });
1437
1484
  return;
1438
1485
  }
1439
- sendJson(res, 200, { ok: true, session: record });
1486
+ sendJson(res, 200, { ok: true, session: projectBrowserChatSession(record) });
1440
1487
  }
1441
1488
  catch (error) {
1442
1489
  sendJson(res, 404, {
@@ -1486,10 +1533,7 @@ export async function handleReopenChatSession(req, res, deps, sessionId) {
1486
1533
  : {}),
1487
1534
  });
1488
1535
  if (!result.ok) {
1489
- const mapped = mapReopenSessionFailure({
1490
- code: result.code,
1491
- message: result.message,
1492
- });
1536
+ const mapped = await mapFailedReopen(deps, sessionId, result);
1493
1537
  sendJson(res, mapped.status, {
1494
1538
  ok: false,
1495
1539
  error: { code: mapped.code, message: mapped.message },
@@ -1575,10 +1619,7 @@ export async function handleChatPrompt(req, res, deps, sessionId) {
1575
1619
  : {}),
1576
1620
  });
1577
1621
  if (!reopen.ok) {
1578
- const mapped = mapReopenSessionFailure({
1579
- code: reopen.code,
1580
- message: reopen.message,
1581
- });
1622
+ const mapped = await mapFailedReopen(deps, sessionId, reopen);
1582
1623
  sendJson(res, mapped.status, {
1583
1624
  ok: false,
1584
1625
  error: { code: mapped.code, message: mapped.message },
@@ -1847,10 +1888,7 @@ export async function handleCreateChatTurn(req, res, deps, sessionId, trustedPay
1847
1888
  : {}),
1848
1889
  });
1849
1890
  if (!reopen.ok) {
1850
- const mapped = mapReopenSessionFailure({
1851
- code: reopen.code,
1852
- message: reopen.message,
1853
- });
1891
+ const mapped = await mapFailedReopen(deps, sessionId, reopen);
1854
1892
  sendJson(res, mapped.status, {
1855
1893
  ok: false,
1856
1894
  accepted: false,
@@ -2776,6 +2814,9 @@ export async function handleChatCommands(res, deps, sessionId) {
2776
2814
  res.end(JSON.stringify({
2777
2815
  ok: true,
2778
2816
  commands,
2817
+ capabilities: {
2818
+ sddBingo: Boolean(await deps.sddDataAlignment?.isEligible()),
2819
+ },
2779
2820
  ...(warning ? { warning } : {}),
2780
2821
  }));
2781
2822
  }
@@ -2786,10 +2827,46 @@ export async function handleChatCommands(res, deps, sessionId) {
2786
2827
  res.end(JSON.stringify({
2787
2828
  ok: true,
2788
2829
  commands: [],
2830
+ capabilities: { sddBingo: false },
2789
2831
  warning: error instanceof Error ? error.message : "commands unavailable",
2790
2832
  }));
2791
2833
  }
2792
2834
  }
2835
+ async function handleSddBingo(req, res, deps, sessionId) {
2836
+ const gate = gateMutation(req, deps);
2837
+ if (!gate.ok) {
2838
+ sendJson(res, gate.status, { ok: false, error: { code: gate.code, message: gate.message } });
2839
+ return;
2840
+ }
2841
+ if (!deps.sddDataAlignment) {
2842
+ sendJson(res, 403, {
2843
+ ok: false,
2844
+ error: { code: "SDD_NOT_ELIGIBLE", message: "当前用户不可使用 SDD 数据对齐" },
2845
+ });
2846
+ return;
2847
+ }
2848
+ try {
2849
+ const body = await readJsonBody(req);
2850
+ if (Object.keys(body).some((key) => key !== "skillName")) {
2851
+ throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "请求体只允许 skillName 字段", 400);
2852
+ }
2853
+ await deps.sddDataAlignment.report(sessionId, body.skillName);
2854
+ sendJson(res, 200, { ok: true });
2855
+ }
2856
+ catch (error) {
2857
+ if (error instanceof SddDataAlignmentError) {
2858
+ sendJson(res, error.status, {
2859
+ ok: false,
2860
+ error: { code: error.code, message: error.message },
2861
+ });
2862
+ return;
2863
+ }
2864
+ sendJson(res, 502, {
2865
+ ok: false,
2866
+ error: { code: "SDD_UPSTREAM_FAILED", message: "SDD 数据对齐上报失败" },
2867
+ });
2868
+ }
2869
+ }
2793
2870
  export async function handleRuntimeContext(res, deps, sessionId) {
2794
2871
  const ensured = await ensureActiveSession(deps, sessionId);
2795
2872
  if (!ensured.ok) {
@@ -2921,7 +2998,11 @@ export async function handleModels(req, res, deps, sessionId) {
2921
2998
  // folded into a 409 MODEL_SWITCH_FAILED (the frontend refresh path
2922
2999
  // re-reads the snapshot to converge on the applied selection).
2923
3000
  const snapshot = await deps.runtime.getRuntimeSnapshot(sessionId);
2924
- sendJson(res, 200, { ok: true, session: updated, snapshot });
3001
+ sendJson(res, 200, {
3002
+ ok: true,
3003
+ session: projectBrowserChatSession(updated),
3004
+ snapshot,
3005
+ });
2925
3006
  }
2926
3007
  catch (error) {
2927
3008
  if (error instanceof RuntimeSnapshotUnavailableError) {
@@ -2999,7 +3080,7 @@ export async function ensureActiveSession(deps, sessionId) {
2999
3080
  : {}),
3000
3081
  });
3001
3082
  if (!reopened.ok) {
3002
- return { ok: false, ...mapReopenSessionFailure(reopened) };
3083
+ return { ok: false, ...(await mapFailedReopen(deps, sessionId, reopened)) };
3003
3084
  }
3004
3085
  return { ok: true };
3005
3086
  }
@@ -3946,10 +4027,7 @@ export async function handleChatCompact(req, res, deps, sessionId) {
3946
4027
  : {}),
3947
4028
  });
3948
4029
  if (!reopened.ok) {
3949
- const mapped = mapReopenSessionFailure({
3950
- code: reopened.code,
3951
- message: reopened.message,
3952
- });
4030
+ const mapped = await mapFailedReopen(deps, sessionId, reopened);
3953
4031
  sendJson(res, mapped.status, {
3954
4032
  ok: false,
3955
4033
  error: { code: mapped.code, message: mapped.message },
@@ -4188,6 +4266,11 @@ export async function handleChatRequest(req, res, deps, pathname, workspaceScope
4188
4266
  await handleSkillAutoInvocation(req, res, deps, decodeURIComponent(skillAutoInvocation[1]), decodeURIComponent(skillAutoInvocation[2]));
4189
4267
  return true;
4190
4268
  }
4269
+ const sddBingo = pathname.match(/^\/api\/operator\/v1\/chat\/sessions\/([^/]+)\/sdd-bingo$/);
4270
+ if (method === "POST" && sddBingo) {
4271
+ await handleSddBingo(req, res, deps, decodeURIComponent(sddBingo[1]));
4272
+ return true;
4273
+ }
4191
4274
  const m4Route = pathname.match(/^\/api\/operator\/v1\/chat\/sessions\/([^/]+)\/(draft|files|runtime-context|runtime-snapshot|models|model|reload|commands)$/);
4192
4275
  if (m4Route) {
4193
4276
  const id = decodeURIComponent(m4Route[1]);
@@ -0,0 +1,222 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { readFile, stat } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ export const SDD_DEFAULT_SKILL_NAME = "SDD-implementation-test-review";
6
+ export const SDD_TELEMETRY_URL = "https://wizard-ai.paic.com.cn/code_pilot/api/skill-usage-log";
7
+ export const SDD_TELEMETRY_SIGN = "52aad0c09e6b46818a11ee702d6fe0ba";
8
+ export const WIZARD_LOCAL_DEFAULT_USER_AGENT = "Builder/OpenCode ai-sdk/provider-utils/4.0.21 runtime/bun/1.3.11";
9
+ function sameAuthFileVersion(left, right) {
10
+ if (!left.exists || !right.exists)
11
+ return left.exists === right.exists;
12
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size &&
13
+ left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
14
+ }
15
+ /** Reads ~/.aima-go/auth.json with metadata-based parse caching and fail-closed semantics. */
16
+ export class AimaxAuthReader {
17
+ authFile;
18
+ cached;
19
+ constructor(authFile = path.join(os.homedir(), ".aima-go", "auth.json")) {
20
+ this.authFile = authFile;
21
+ }
22
+ async getToken() {
23
+ let version;
24
+ try {
25
+ const info = await stat(this.authFile, { bigint: true });
26
+ version = { exists: true, dev: info.dev, ino: info.ino, size: info.size,
27
+ mtimeNs: info.mtimeNs, ctimeNs: info.ctimeNs };
28
+ }
29
+ catch {
30
+ this.cached = { version: { exists: false } };
31
+ return undefined;
32
+ }
33
+ if (this.cached && sameAuthFileVersion(this.cached.version, version))
34
+ return this.cached.token;
35
+ let token;
36
+ try {
37
+ const parsed = JSON.parse(await readFile(this.authFile, "utf8"));
38
+ const candidate = parsed["aimax-auth-token"];
39
+ if (typeof candidate === "string" && candidate.trim())
40
+ token = candidate;
41
+ }
42
+ catch {
43
+ // Malformed or unreadable files are intentionally ineligible.
44
+ }
45
+ this.cached = { version, ...(token ? { token } : {}) };
46
+ return token;
47
+ }
48
+ async isEligible() {
49
+ return Boolean(await this.getToken());
50
+ }
51
+ }
52
+ const OPENCODE_RANDOM_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
53
+ const OPENCODE_TIME_MASK = (1n << 48n) - 1n;
54
+ let lastOpenCodeMillisecond = -1;
55
+ let openCodeMillisecondCounter = 0;
56
+ /** Generate the OpenCode-compatible `ses_` identifier without an OpenCode dependency. */
57
+ export function createOpenCodeSessionId(options = {}) {
58
+ const now = Math.floor((options.now ?? Date.now)());
59
+ if (now === lastOpenCodeMillisecond)
60
+ openCodeMillisecondCounter = (openCodeMillisecondCounter + 1) & 0xfff;
61
+ else {
62
+ lastOpenCodeMillisecond = now;
63
+ openCodeMillisecondCounter = 0;
64
+ }
65
+ const ordered = BigInt(now) * 0x1000n + BigInt(openCodeMillisecondCounter);
66
+ const timePart = ((~ordered) & OPENCODE_TIME_MASK).toString(16).padStart(12, "0");
67
+ const bytes = (options.random ?? randomBytes)(14);
68
+ let randomPart = "";
69
+ for (const byte of bytes)
70
+ randomPart += OPENCODE_RANDOM_ALPHABET[byte % OPENCODE_RANDOM_ALPHABET.length];
71
+ return `ses_${timePart}${randomPart}`;
72
+ }
73
+ export class SddDataAlignmentError extends Error {
74
+ code;
75
+ status;
76
+ constructor(code, message, status) {
77
+ super(message);
78
+ this.code = code;
79
+ this.status = status;
80
+ this.name = "SddDataAlignmentError";
81
+ }
82
+ }
83
+ export function normalizeSddSkillName(value) {
84
+ if (value !== undefined && typeof value !== "string") {
85
+ throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "skillName 必须是字符串", 400);
86
+ }
87
+ const raw = value ?? "";
88
+ const normalized = raw.trim() || SDD_DEFAULT_SKILL_NAME;
89
+ if ([...normalized].length > 256 || /[\u0000-\u001f\u007f]/u.test(raw)) {
90
+ throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "skillName 不能超过 256 个字符或包含控制字符", 400);
91
+ }
92
+ return normalized;
93
+ }
94
+ export class SddDataAlignmentService {
95
+ store;
96
+ auth;
97
+ fetchImpl;
98
+ constructor(store, auth = new AimaxAuthReader(), fetchImpl = fetch) {
99
+ this.store = store;
100
+ this.auth = auth;
101
+ this.fetchImpl = fetchImpl;
102
+ }
103
+ async isEligible() {
104
+ return this.auth.isEligible();
105
+ }
106
+ async ensureTelemetrySessionId(sessionId) {
107
+ const value = await this.store.ensureTelemetrySessionId(sessionId, createOpenCodeSessionId);
108
+ if (!value)
109
+ throw new SddDataAlignmentError("SDD_SESSION_NOT_FOUND", "当前对话不存在", 404);
110
+ return value;
111
+ }
112
+ async report(sessionId, rawSkillName) {
113
+ const token = await this.auth.getToken();
114
+ if (!token) {
115
+ throw new SddDataAlignmentError("SDD_NOT_ELIGIBLE", "当前用户不可使用 SDD 数据对齐", 403);
116
+ }
117
+ const skillName = normalizeSddSkillName(rawSkillName);
118
+ const telemetrySessionId = await this.ensureTelemetrySessionId(sessionId);
119
+ const controller = new AbortController();
120
+ const timeout = setTimeout(() => controller.abort(), 30_000);
121
+ let response;
122
+ try {
123
+ response = await this.fetchImpl(SDD_TELEMETRY_URL, {
124
+ method: "POST",
125
+ headers: {
126
+ "Content-Type": "application/json",
127
+ "ws-auth": token,
128
+ "ws-sign": SDD_TELEMETRY_SIGN,
129
+ Connection: "keep-alive",
130
+ "User-Agent": "opencode/1.3.6",
131
+ Accept: "*/*",
132
+ Host: "wizard-ai.paic.com.cn",
133
+ "Accept-Encoding": "gzip, deflate, br, zstd",
134
+ },
135
+ body: JSON.stringify({ sessionId: telemetrySessionId, builderId: "opencode", skillName }),
136
+ signal: controller.signal,
137
+ });
138
+ }
139
+ catch {
140
+ if (controller.signal.aborted) {
141
+ throw new SddDataAlignmentError("SDD_UPSTREAM_TIMEOUT", "SDD 数据对齐上报超时", 504);
142
+ }
143
+ throw new SddDataAlignmentError("SDD_UPSTREAM_FAILED", "SDD 数据对齐上报失败", 502);
144
+ }
145
+ finally {
146
+ clearTimeout(timeout);
147
+ }
148
+ const body = await readBoundedTelemetryJson(response);
149
+ if (!response.ok || body?.ret !== 0 || body.succ !== true) {
150
+ throw new SddDataAlignmentError("SDD_UPSTREAM_FAILED", "SDD 数据对齐上报失败", 502);
151
+ }
152
+ }
153
+ }
154
+ async function readBoundedTelemetryJson(response) {
155
+ const reader = response.body?.getReader();
156
+ if (!reader)
157
+ return undefined;
158
+ const chunks = [];
159
+ let total = 0;
160
+ try {
161
+ for (;;) {
162
+ const item = await reader.read();
163
+ if (item.done)
164
+ break;
165
+ total += item.value.byteLength;
166
+ if (total > 64 * 1024) {
167
+ await reader.cancel();
168
+ return undefined;
169
+ }
170
+ chunks.push(item.value);
171
+ }
172
+ const merged = new Uint8Array(total);
173
+ let offset = 0;
174
+ for (const chunk of chunks) {
175
+ merged.set(chunk, offset);
176
+ offset += chunk.byteLength;
177
+ }
178
+ return JSON.parse(new TextDecoder().decode(merged));
179
+ }
180
+ catch {
181
+ return undefined;
182
+ }
183
+ finally {
184
+ reader.releaseLock();
185
+ }
186
+ }
187
+ function deleteHeaderCaseInsensitive(headers, name) {
188
+ for (const key of Object.keys(headers))
189
+ if (key.toLowerCase() === name.toLowerCase())
190
+ delete headers[key];
191
+ }
192
+ function configuredHeader(headers, name) {
193
+ const key = Object.keys(headers ?? {}).find((entry) => entry.toLowerCase() === name.toLowerCase());
194
+ return key ? headers?.[key] : undefined;
195
+ }
196
+ function setStaticHeader(headers, configured, name, defaultValue) {
197
+ deleteHeaderCaseInsensitive(headers, name);
198
+ headers[name] = configuredHeader(configured, name) ?? defaultValue;
199
+ }
200
+ export async function injectWizardLocalHeaders(options) {
201
+ const model = options.context.model;
202
+ if (model?.provider !== "wizard-local" || !model.id)
203
+ return;
204
+ if (!(await options.service.isEligible()))
205
+ return;
206
+ const modelRegistry = options.context.modelRegistry;
207
+ if (!modelRegistry?.getApiKeyAndHeaders)
208
+ return;
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);
213
+ if (!auth.ok || typeof auth.apiKey !== "string" || !auth.apiKey)
214
+ return;
215
+ setStaticHeader(options.headers, auth.headers, "client-code", "OpenCode");
216
+ setStaticHeader(options.headers, auth.headers, "User-Agent", WIZARD_LOCAL_DEFAULT_USER_AGENT);
217
+ for (const name of ["x-api-key", "X-Session-Id", "X-Model-Id"])
218
+ deleteHeaderCaseInsensitive(options.headers, name);
219
+ options.headers["x-api-key"] = auth.apiKey;
220
+ options.headers["X-Session-Id"] = await options.service.ensureTelemetrySessionId(options.sessionId);
221
+ options.headers["X-Model-Id"] = model.id;
222
+ }
@@ -1,3 +1,4 @@
1
+ import { isBlankChatSession } from "./session-store.js";
1
2
  import { projectDeferredTurnSummaries, projectDeferredTurnSummary, } from "./deferred-turn.js";
2
3
  /**
3
4
  * Process-wide Session baseline, mirroring DSH's independent session.list.
@@ -33,6 +34,7 @@ export class ChatSessionCatalog {
33
34
  : {}),
34
35
  deferredTurn: projectDeferredTurnSummary(record.deferredTurns?.[0]),
35
36
  deferredTurns: projectDeferredTurnSummaries(record.deferredTurns ?? []),
37
+ isBlank: isBlankChatSession(record),
36
38
  ...(workspaceId ? { workspaceId } : {}),
37
39
  });
38
40
  }