@tea-agent/loop-agent 0.44.0-next.1 → 0.44.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 (226) hide show
  1. package/CHANGELOG.md +379 -416
  2. package/dist/application/evaluation/budget.js +21 -0
  3. package/dist/application/evaluation/corpus-hash.js +10 -15
  4. package/dist/application/evaluation/corpus.js +2 -1
  5. package/dist/application/evaluation/frontend-browser-acceptance.js +77 -0
  6. package/dist/application/evaluation/frontend-corpus-browser-acceptance.js +175 -0
  7. package/dist/application/evaluation/frontend-gateway-evidence.js +226 -0
  8. package/dist/application/evaluation/frontend-pair-registration.js +143 -0
  9. package/dist/application/evaluation/frontend-paired-summary.js +150 -0
  10. package/dist/application/evaluation/frontend-run-observation.js +144 -0
  11. package/dist/application/evaluation/frontend-shared-evidence.js +105 -0
  12. package/dist/application/evaluation/types.js +45 -12
  13. package/dist/application/task-lifecycle/observe.js +26 -1
  14. package/dist/build-stamp.json +3 -3
  15. package/dist/cli/command-definitions.js +11 -0
  16. package/dist/cli/program.js +4 -0
  17. package/dist/commands/task-advance.js +3 -5
  18. package/dist/commands/task-source-prepare.js +13 -1
  19. package/dist/executors/dag-pi/guards.js +4 -0
  20. package/dist/executors/dag-pi/model-config.js +30 -0
  21. package/dist/executors/dag-pi/plan/fact-adoption.js +55 -0
  22. package/dist/{workflows/dag/frontend-plan-completeness.js → executors/dag-pi/plan/facts.js} +22 -28
  23. package/dist/executors/dag-pi/plan/finalize-tools.js +259 -0
  24. package/dist/executors/dag-pi/plan/ledger-contract.js +55 -0
  25. package/dist/executors/dag-pi/plan/record-tools.js +861 -0
  26. package/dist/executors/dag-pi/plan/schema.js +129 -0
  27. package/dist/executors/dag-pi/plan/session.js +1 -0
  28. package/dist/executors/dag-pi/plan/tool-helpers.js +16 -0
  29. package/dist/executors/dag-pi/tools/contract-tools.js +515 -0
  30. package/dist/executors/dag-pi/tools/design-terminal-tools.js +159 -0
  31. package/dist/executors/dag-pi/tools/plan-decision-tools.js +529 -0
  32. package/dist/executors/dag-pi/tools/review-terminal-tools.js +159 -0
  33. package/dist/executors/dag-pi/tools/scout-evidence-tools.js +270 -0
  34. package/dist/executors/dag-pi-executor.js +398 -3384
  35. package/dist/executors/pi-executor.js +6 -1
  36. package/dist/executors/pi-sdk-executor.js +112 -19
  37. package/dist/executors/shell-executor.js +88 -12
  38. package/dist/executors/shell-presets.js +1 -1
  39. package/dist/executors/shell-write-guard.js +26 -3
  40. package/dist/infrastructure/console/operation-store.js +48 -14
  41. package/dist/infrastructure/evaluation/corpus-store.js +37 -12
  42. package/dist/shared/file-identity.js +23 -0
  43. package/dist/shared/git-workspace-status.js +482 -0
  44. package/dist/shared/operator/capabilities.js +10 -5
  45. package/dist/shared/path-safety.js +2 -2
  46. package/dist/shared/wizard-local-telemetry.js +51 -0
  47. package/dist/task/config-types.js +22 -8
  48. package/dist/task/contract/project.js +13 -0
  49. package/dist/task/contract/schema.js +2 -7
  50. package/dist/task/frontend-preflight.js +1 -1
  51. package/dist/task/source-prepare/parse-intent.js +124 -5
  52. package/dist/task/source-prepare/prepare.js +6 -1
  53. package/dist/task/source-prepare/semantic-intake.js +18 -3
  54. package/dist/task/source-prepare/source-execution.js +65 -0
  55. package/dist/task/source-prepare/source-fidelity-pi.js +22 -9
  56. package/dist/task/source-prepare/source-provider-budget.js +290 -0
  57. package/dist/worker/console/chat/assistant-content.js +5 -34
  58. package/dist/worker/console/chat/chat-event-store.js +62 -10
  59. package/dist/worker/console/chat/context-insights.js +723 -0
  60. package/dist/worker/console/chat/conversation-nodes.js +383 -0
  61. package/dist/worker/console/chat/db-connections.js +854 -0
  62. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +1 -1
  63. package/dist/worker/console/chat/pi-runtime/custom-tools/sql-tools.js +655 -0
  64. package/dist/worker/console/chat/pi-runtime.js +445 -36
  65. package/dist/worker/console/chat/provider-error.js +0 -12
  66. package/dist/worker/console/chat/repo-browser.js +52 -4
  67. package/dist/worker/console/chat/routes.js +577 -72
  68. package/dist/worker/console/chat/sdd-data-alignment.js +8 -31
  69. package/dist/worker/console/chat/session-store.js +147 -19
  70. package/dist/worker/console/chat/shortcuts.js +7 -0
  71. package/dist/worker/console/chat/timeline-message-order.js +22 -0
  72. package/dist/worker/console/chat/tools.js +34 -0
  73. package/dist/worker/console/chat/turn-process.js +237 -66
  74. package/dist/worker/console/chat/user-turn-outline.js +19 -0
  75. package/dist/worker/console/console-update-runtime.js +2 -2
  76. package/dist/worker/console/dag-confirmation.js +3 -0
  77. package/dist/worker/console/dag-execution-receipt.js +3 -0
  78. package/dist/worker/console/operator-actions.js +32 -3
  79. package/dist/worker/console/operator-user-error.js +28 -1
  80. package/dist/worker/console/pi-readiness.js +4 -4
  81. package/dist/worker/console/prd-intake-bridge.js +5 -12
  82. package/dist/worker/console/recovery-cta.js +14 -14
  83. package/dist/worker/console/server.js +5 -1
  84. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-8-j6y-sd.js → abnfDiagram-N423BO3Z-r1NAPU_5.js} +1 -1
  85. package/dist/worker/console/static/assets/{arc-eoQiMvuk.js → arc-dT5T4sVL.js} +1 -1
  86. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-C4A3uMcI.js → architectureDiagram-T3A2C74G-CP_cOq46.js} +1 -1
  87. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D4zD2F-Q.js → blockDiagram-VBNYF7ZC-sToEZxEf.js} +1 -1
  88. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-j1RkJziL.js → c4Diagram-5PPSVZJV-DSxwwi2m.js} +1 -1
  89. package/dist/worker/console/static/assets/channel-CyBY_yGk.js +1 -0
  90. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-YBHmhik1.js → chunk-2GRJ4B5K-B9fh0IkF.js} +1 -1
  91. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-COfWyo9P.js → chunk-2Q5K7J3B-CAUNl-A6.js} +1 -1
  92. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-I99OUkHY.js → chunk-5RXB4S5H-BomeqbNX.js} +1 -1
  93. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-XKxoJNJ4.js → chunk-5VM5RSS4-BNAuTOLE.js} +1 -1
  94. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DLT_cYx1.js → chunk-6Q2QTUOP-DfzjRD5y.js} +1 -1
  95. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CxcKZ9mV.js → chunk-GF5L2VYU-C3uE-yc2.js} +1 -1
  96. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-V1EqXdjY.js → chunk-JWPE2WC7-C3sQMMYr.js} +1 -1
  97. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DebTtdFp.js → chunk-KBJHAD2P-D_AtEYQL.js} +1 -1
  98. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-B-ivNRDf.js → chunk-RYQCIY6F-DW4TwhKv.js} +1 -1
  99. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DC_Ds11b.js → chunk-XXDRQBXY-CTQDmJcp.js} +1 -1
  100. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D5lN4E_E.js +1 -0
  101. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D5lN4E_E.js +1 -0
  102. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-sWEqKwIb.js → cose-bilkent-JH36ORCC-iMqPuBsL.js} +1 -1
  103. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BXa_dcu4.js → cynefin-VYW2F7L2-DktIuDWs.js} +1 -1
  104. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-C-Mle84F.js → cynefinDiagram-MW4NZA55-BgVnjyEs.js} +1 -1
  105. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CXPDBITe.js → dagre-VZM6K2ZE-CR25E4bu.js} +1 -1
  106. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CC-WJQfa.js → diagram-7IWD3JNH-CPM2h5HY.js} +1 -1
  107. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CDAV5Vs4.js → diagram-B4RE2ZJO-DqF0x2g1.js} +1 -1
  108. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CmFiAcNz.js → diagram-LBJQPF4R-G8BYp2eb.js} +1 -1
  109. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DPBZHuyn.js → diagram-Q27KOJAE-BbC4kT_D.js} +1 -1
  110. package/dist/worker/console/static/assets/{diagram-UB23O5K3-jUlm_Ds3.js → diagram-UB23O5K3-CKZwZ-G5.js} +1 -1
  111. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DWhQ3mfY.js → ebnfDiagram-BXEA7PRR-kJDmheia.js} +1 -1
  112. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Tr2gMqet.js → erDiagram-JOGREHBK-DFTVHyxB.js} +1 -1
  113. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DJjVQHPA.js → flowDiagram-UKHOOZJN-BqdN5_os.js} +1 -1
  114. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-D74dQ4u0.js → ganttDiagram-PKOTCBZU-SYDjVakh.js} +1 -1
  115. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js → gitGraphDiagram-DS77QQ5N-Bf8bNpGA.js} +1 -1
  116. package/dist/worker/console/static/assets/index-B28Onmfy.css +1 -0
  117. package/dist/worker/console/static/assets/index-CTMZsDmh.js +486 -0
  118. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-ILCbxJyb.js → infoDiagram-6WML65LV-DIeAmJ45.js} +1 -1
  119. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DeuWBQ89.js → ishikawaDiagram-WSZJBQD7-oikqUAlo.js} +1 -1
  120. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CF5ih8Fk.js → journeyDiagram-NVQOT4AX-mKEF_xYT.js} +1 -1
  121. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C7yOSuRO.js → kanban-definition-27J2QSJJ-BdWJHPnN.js} +1 -1
  122. package/dist/worker/console/static/assets/{linear-BDZ9riWi.js → linear-DGRlieMH.js} +1 -1
  123. package/dist/worker/console/static/assets/{mermaid.core-7pKqYtpZ.js → mermaid.core-4P9yPC9c.js} +5 -5
  124. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-LeJDybSU.js → mindmap-definition-FAOFIHXS-5PTSkos6.js} +1 -1
  125. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BJvT3pMD.js → pegDiagram-VL7TDLO6-DONIFpG5.js} +1 -1
  126. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-_rGqpMan.js → pieDiagram-7S7Q4E2Y-D_Z4S70M.js} +1 -1
  127. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DPcSv5aA.js → quadrantDiagram-CIZ2JOQS-CAsjh8iY.js} +1 -1
  128. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Drxx4hkJ.js → railroadDiagram-AXF67PYL-Cx4Dd6ac.js} +1 -1
  129. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BCTUdU4z.js → requirementDiagram-LRYGKXZP-cQb7mwyH.js} +1 -1
  130. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B6wzbZmB.js → sankeyDiagram-W5VNT64P-Cd-DhUdf.js} +1 -1
  131. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BGt8d3QQ.js → sequenceDiagram-SI44F4Z6-AlKYRgIk.js} +1 -1
  132. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-7nlhJKo7.js → sizeCapture-X5ZJPWSS-CUy_ET4e.js} +1 -1
  133. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Cv2stqMA.js → stateDiagram-OKZ733FA-CTuVe6Sa.js} +1 -1
  134. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BsWhs8O7.js +1 -0
  135. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-qDAo4Yc1.js → swimlanes-SLNWSIFB-ChFLJb-v.js} +2 -2
  136. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CJcioYGH.js +8 -0
  137. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKbDNKTr.js → timeline-definition-Z64GVDOM-DZZeiAPr.js} +1 -1
  138. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DI-9EHic.js → vennDiagram-T6HMQDX7-t4O91cZU.js} +1 -1
  139. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BrzzRDpX.js → wardleyDiagram-T6FBY63Y-CV5qGHZu.js} +1 -1
  140. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-BclH5hGh.js → xychartDiagram-ELKLHX3M-DyXbytUI.js} +1 -1
  141. package/dist/worker/console/static/index.html +2 -2
  142. package/dist/worker/console/static-src/app/console-types.js +9 -1
  143. package/dist/worker/console/static-src/operator-chat/animation-frame-batcher.js +38 -0
  144. package/dist/worker/console/static-src/operator-chat/chat-link.js +20 -0
  145. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +186 -5
  146. package/dist/worker/console/static-src/operator-chat/chat-stream-continuity.js +101 -0
  147. package/dist/worker/console/static-src/operator-chat/compaction-message.js +18 -3
  148. package/dist/worker/console/static-src/operator-chat/context-insights-store.js +64 -0
  149. package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +98 -0
  150. package/dist/worker/console/static-src/operator-chat/file-palette-nav.js +81 -0
  151. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +17 -3
  152. package/dist/worker/console/static-src/operator-chat/process-label.js +27 -5
  153. package/dist/worker/console/static-src/operator-chat/tools-catalog.js +16 -7
  154. package/dist/worker/console/static-src/operator-chat/transcript-mode.js +21 -0
  155. package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +4 -1
  156. package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +13 -5
  157. package/dist/worker/console/static-src/operator-chat/turn-state-projection.js +166 -0
  158. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +56 -2
  159. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +211 -40
  160. package/dist/worker/console/static-src/operator-chat/useChatStream.js +124 -27
  161. package/dist/worker/console/static-src/operator-chat/useChatThread.js +88 -20
  162. package/dist/worker/console/static-src/operator-chat/useComposer.js +80 -6
  163. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +13 -3
  164. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +36 -17
  165. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +18 -0
  166. package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
  167. package/dist/worker/console/static-src/shell/console-update-reload.js +1 -1
  168. package/dist/worker/console/workspace-context.js +2 -0
  169. package/dist/worker/console/workspace-initialization.js +3 -3
  170. package/dist/worker/observability/read-model.js +23 -2
  171. package/dist/worker/observe/node-context-insights.js +479 -0
  172. package/dist/worker/observe/static/console-theme.css +174 -0
  173. package/dist/worker/observe/static/console-theme.js +36 -0
  174. package/dist/worker/observe/static/operator-chrome.css +85 -0
  175. package/dist/worker/observe/static/operator-chrome.js +51 -0
  176. package/dist/worker/scheduler/scheduled-goal-dispatch.js +3 -2
  177. package/dist/worker/scheduler/scheduled-goal-evidence.js +66 -3
  178. package/dist/worker/scheduler/scheduled-goal-store.js +10 -0
  179. package/dist/workflows/dag/budget-enforcement.js +31 -2
  180. package/dist/workflows/dag/frontend-closeout.js +3 -1
  181. package/dist/workflows/dag/frontend-contract-facts.js +11 -8
  182. package/dist/workflows/dag/frontend-design-policy.js +6 -6
  183. package/dist/workflows/dag/frontend-durable-tools.js +90 -70
  184. package/dist/workflows/dag/frontend-implementation-contract.js +146 -57
  185. package/dist/workflows/dag/frontend-plan-canary.js +66 -16
  186. package/dist/workflows/dag/frontend-plan-decision-contract.js +13 -21
  187. package/dist/workflows/dag/frontend-plan-progress.js +92 -0
  188. package/dist/workflows/dag/frontend-recovery-plan.js +10 -5
  189. package/dist/workflows/dag/frontend-recovery-run.js +51 -46
  190. package/dist/workflows/dag/frontend-repair-assertions.js +36 -0
  191. package/dist/workflows/dag/frontend-review-context.js +29 -1
  192. package/dist/workflows/dag/frontend-review-scopes.js +92 -2
  193. package/dist/workflows/dag/frontend-risk.js +8 -3
  194. package/dist/workflows/dag/frontend-root-observation.js +261 -0
  195. package/dist/workflows/dag/frontend-session-budget.js +52 -39
  196. package/dist/workflows/dag/frontend-session-context.js +10 -0
  197. package/dist/workflows/dag/frontend-test-execution-evidence.js +206 -48
  198. package/dist/workflows/dag/frontend-typed-event-store.js +11 -1
  199. package/dist/workflows/dag/frontend-verification-trace.js +6 -0
  200. package/dist/workflows/dag/frontend-writer-admission.js +3 -1
  201. package/dist/workflows/dag/init-hybrid.js +108 -37
  202. package/dist/workflows/dag/node-execution.js +11 -11
  203. package/dist/workflows/dag/output-protocol.js +1 -0
  204. package/dist/workflows/dag/rerun-feedback.js +35 -4
  205. package/dist/workflows/dag/rerun-task.js +62 -66
  206. package/dist/workflows/dag/runner.js +58 -12
  207. package/dist/workflows/dag/types.js +15 -8
  208. package/dist/workflows/loop/benchmark.js +11 -8
  209. package/docs/README.md +1 -1
  210. package/docs/architecture/runtime-boundaries.md +1 -1
  211. package/docs/governance/harness-methodology-verification.md +11 -0
  212. package/docs/templates/backend-test-dag.json +5 -4
  213. package/docs/templates/frontend-implementation-contract.schema.json +31 -11
  214. package/package.json +6 -4
  215. package/skills/frontend-design-review/SKILL.md +8 -11
  216. package/skills/frontend-design-review/references/review-checklist.md +3 -4
  217. package/skills/frontend-review/SKILL.md +5 -1
  218. package/skills/loop-agent/references/command-reference.md +8 -0
  219. package/skills/loop-agent/references/hybrid-dag.md +1 -1
  220. package/dist/worker/console/static/assets/channel-ChE7y-cx.js +0 -1
  221. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +0 -1
  222. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +0 -1
  223. package/dist/worker/console/static/assets/index-BWkIfcrK.css +0 -1
  224. package/dist/worker/console/static/assets/index-Cdkvw_H6.js +0 -469
  225. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +0 -1
  226. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +0 -8
@@ -2,6 +2,8 @@ import { createDecipheriv, randomBytes } from "node:crypto";
2
2
  import { readFile, stat } from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
+ import { injectWizardLocalTelemetryHeaders, } from "../../../shared/wizard-local-telemetry.js";
6
+ export { WIZARD_LOCAL_DEFAULT_USER_AGENT, } from "../../../shared/wizard-local-telemetry.js";
5
7
  export const SDD_SKILL_NAMES = [
6
8
  "SDD-design-analysis",
7
9
  "SDD-implementation-test-review",
@@ -14,7 +16,6 @@ export const SDD_TELEMETRY_URL = "https://wizard-ai.paic.com.cn/code_pilot/api/s
14
16
  export const DEFAULT_AI_MA_PUB_SALT_KEY = "52aad0c09e6b46818a11ee702d6fe0ba";
15
17
  export const SDD_TELEMETRY_SIGN = DEFAULT_AI_MA_PUB_SALT_KEY;
16
18
  export const AIMAX_AUTH_TOKEN_ENV = "AIMAX_AUTH_TOKEN";
17
- export const WIZARD_LOCAL_DEFAULT_USER_AGENT = "Builder/OpenCode ai-sdk/provider-utils/4.0.21 runtime/bun/1.3.11";
18
19
  const SM4_V1_PREFIX = "sm4:v1:";
19
20
  /** Decode a plaintext ticket or `sm4:v1:` ciphertext. Unknown prefixes and crypto errors fail closed. */
20
21
  export function decodeAimaxAuthToken(raw, keyHex = DEFAULT_AI_MA_PUB_SALT_KEY) {
@@ -215,7 +216,7 @@ export class SddDataAlignmentService {
215
216
  if (!response.ok || body?.ret !== 0 || body.succ !== true) {
216
217
  throw new SddDataAlignmentError("SDD_UPSTREAM_FAILED", "SDD 数据对齐上报失败", 502);
217
218
  }
218
- return { skillName };
219
+ return { skillName, telemetrySessionId };
219
220
  }
220
221
  }
221
222
  async function readBoundedTelemetryJson(response) {
@@ -251,39 +252,15 @@ async function readBoundedTelemetryJson(response) {
251
252
  reader.releaseLock();
252
253
  }
253
254
  }
254
- function deleteHeaderCaseInsensitive(headers, name) {
255
- for (const key of Object.keys(headers))
256
- if (key.toLowerCase() === name.toLowerCase())
257
- delete headers[key];
258
- }
259
- function configuredHeader(headers, name) {
260
- const key = Object.keys(headers ?? {}).find((entry) => entry.toLowerCase() === name.toLowerCase());
261
- return key ? headers?.[key] : undefined;
262
- }
263
- function setStaticHeader(headers, configured, name, defaultValue) {
264
- deleteHeaderCaseInsensitive(headers, name);
265
- headers[name] = configuredHeader(configured, name) ?? defaultValue;
266
- }
267
255
  export async function injectWizardLocalHeaders(options) {
268
256
  const model = options.context.model;
269
257
  if (model?.provider !== "wizard-local" || !model.id)
270
258
  return;
271
259
  if (!(await options.service.isEligible()))
272
260
  return;
273
- const modelRegistry = options.context.modelRegistry;
274
- if (!modelRegistry?.getApiKeyAndHeaders)
275
- return;
276
- // Keep the method call attached to ModelRegistry. Its implementation reads
277
- // the private runtime through `this`; extracting the function would make the
278
- // SDK path fail closed and silently drop all custom headers.
279
- const auth = await modelRegistry.getApiKeyAndHeaders(model);
280
- if (!auth.ok || typeof auth.apiKey !== "string" || !auth.apiKey)
281
- return;
282
- setStaticHeader(options.headers, auth.headers, "client-code", "OpenCode");
283
- setStaticHeader(options.headers, auth.headers, "User-Agent", WIZARD_LOCAL_DEFAULT_USER_AGENT);
284
- for (const name of ["x-api-key", "X-Session-Id", "X-Model-Id"])
285
- deleteHeaderCaseInsensitive(options.headers, name);
286
- options.headers["x-api-key"] = auth.apiKey;
287
- options.headers["X-Session-Id"] = await options.service.ensureTelemetrySessionId(options.sessionId);
288
- options.headers["X-Model-Id"] = model.id;
261
+ await injectWizardLocalTelemetryHeaders({
262
+ headers: options.headers,
263
+ context: options.context,
264
+ telemetrySessionId: await options.service.ensureTelemetrySessionId(options.sessionId),
265
+ });
289
266
  }
@@ -11,7 +11,8 @@
11
11
  * operator action call. Every invocation is re-authorized here.
12
12
  */
13
13
  import path from "node:path";
14
- import { readdir, rm } from "node:fs/promises";
14
+ import { randomUUID } from "node:crypto";
15
+ import { copyFile, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
15
16
  import { readJsonIfExists, writeSecureJson, } from "../../../infrastructure/console/app-data.js";
16
17
  import { dispatchOperatorAction } from "../operator-actions.js";
17
18
  import { authorizeOperatorChatTool } from "./tools.js";
@@ -19,6 +20,18 @@ import { authorizeChatSessionTool, DEFAULT_CONSOLE_CHAT_SESSION_MODE, readStored
19
20
  import { projectOperationForChat } from "./chat-event-store.js";
20
21
  import { scrubSecrets } from "./explore-tools.js";
21
22
  import { hashDeferredTurnPayload, parseDeferredTurns, } from "./deferred-turn.js";
23
+ const CHAT_IMAGE_MIME_TYPES = new Set([
24
+ "image/png",
25
+ "image/jpeg",
26
+ "image/gif",
27
+ "image/webp",
28
+ ]);
29
+ function isChatImageMimeType(value) {
30
+ return typeof value === "string" && CHAT_IMAGE_MIME_TYPES.has(value);
31
+ }
32
+ function isChatAttachmentId(value) {
33
+ return typeof value === "string" && /^[0-9a-f-]{36}$/i.test(value);
34
+ }
22
35
  /** True only for a durable session that has never received user work. */
23
36
  export function isBlankChatSession(record) {
24
37
  return (record.state === "active" &&
@@ -48,6 +61,11 @@ export function cleanChatTitle(value) {
48
61
  function recordPath(appData, sessionId) {
49
62
  return path.join(appData.chats, `${sessionId}.json`);
50
63
  }
64
+ function attachmentDir(appData, sessionId) {
65
+ if (!/^[a-zA-Z0-9_-]+$/.test(sessionId))
66
+ throw new Error("invalid chat session id");
67
+ return path.join(appData.chats, "attachments", sessionId);
68
+ }
51
69
  /** True when `sessionFile` lives inside this store's dedicated Pi JSONL dir. */
52
70
  export function isConsoleOwnedSessionFile(appData, sessionFile) {
53
71
  const value = sessionFile?.trim();
@@ -196,6 +214,12 @@ export class ChatSessionStore {
196
214
  const source = await this.get(input.sourceSessionId);
197
215
  if (!source)
198
216
  throw new Error(`chat session not found: ${input.sourceSessionId}`);
217
+ const throughIndex = input.throughMessageId
218
+ ? source.messages.findIndex((message) => message.id === input.throughMessageId && message.role === "user")
219
+ : -1;
220
+ if (input.throughMessageId && throughIndex < 0) {
221
+ throw new Error("FORK_MESSAGE_NOT_FOUND");
222
+ }
199
223
  return this.serialize(input.targetSessionId, async () => {
200
224
  const now = new Date().toISOString();
201
225
  const record = {
@@ -208,11 +232,30 @@ export class ChatSessionStore {
208
232
  updatedAt: now,
209
233
  deferredTurns: [],
210
234
  deferredQueueRevision: 0,
211
- messages: structuredClone(source.messages),
212
- toolInvocations: structuredClone(source.toolInvocations),
235
+ messages: structuredClone(throughIndex >= 0
236
+ ? source.messages.slice(0, throughIndex + 1)
237
+ : source.messages),
238
+ toolInvocations: throughIndex >= 0 ? [] : structuredClone(source.toolInvocations),
213
239
  };
214
- await this.persist(record);
215
- return record;
240
+ const attachmentIds = record.messages.flatMap((message) => Array.isArray(message.attachments)
241
+ ? message.attachments.map((attachment) => attachment.id)
242
+ : []);
243
+ if (!attachmentIds.every(isChatAttachmentId)) {
244
+ throw new Error("INVALID_CHAT_ATTACHMENT_REFERENCE");
245
+ }
246
+ try {
247
+ if (attachmentIds.length > 0) {
248
+ const targetDir = attachmentDir(this.appData, input.targetSessionId);
249
+ await mkdir(targetDir, { recursive: true });
250
+ await Promise.all(attachmentIds.map((attachmentId) => copyFile(path.join(attachmentDir(this.appData, input.sourceSessionId), attachmentId), path.join(targetDir, attachmentId))));
251
+ }
252
+ await this.persist(record);
253
+ return record;
254
+ }
255
+ catch (error) {
256
+ await this.removeAttachmentFiles(input.targetSessionId).catch(() => undefined);
257
+ throw error;
258
+ }
216
259
  });
217
260
  }
218
261
  /** Atomically backfill the per-conversation simulated identity on first use. */
@@ -286,17 +329,84 @@ export class ChatSessionStore {
286
329
  const record = await this.get(sessionId);
287
330
  if (!record)
288
331
  return false;
289
- const sessionFile = record.sessionFile;
290
- await rm(recordPath(this.appData, sessionId), { force: true });
291
- this.options.onRecordChange?.(sessionId, undefined);
292
- // Record first, then best-effort JSONL unlink. Final consistency is
293
- // "both gone" — a leftover JSONL is harmless, a leftover record is not.
294
- if (isConsoleOwnedSessionFile(this.appData, sessionFile) && sessionFile) {
295
- await rm(sessionFile, { force: true }).catch(() => undefined);
296
- }
332
+ await this.removeSessionArtifacts(sessionId, record.sessionFile);
297
333
  return true;
298
334
  });
299
335
  }
336
+ /** Roll back a runtime-created session that never acquired a durable owner. */
337
+ async discardUnownedSession(sessionId, sessionFile) {
338
+ await this.serialize(sessionId, () => this.removeSessionArtifacts(sessionId, sessionFile));
339
+ }
340
+ async removeSessionArtifacts(sessionId, sessionFile) {
341
+ await rm(recordPath(this.appData, sessionId), { force: true });
342
+ await this.removeAttachmentFiles(sessionId).catch(() => undefined);
343
+ this.options.onRecordChange?.(sessionId, undefined);
344
+ // Record first, then best-effort JSONL unlink. Final consistency is
345
+ // "both gone" — a leftover JSONL is harmless, a leftover record is not.
346
+ if (isConsoleOwnedSessionFile(this.appData, sessionFile) && sessionFile) {
347
+ await rm(sessionFile, { force: true }).catch(() => undefined);
348
+ }
349
+ }
350
+ async removeAttachmentFiles(sessionId, attachmentIds) {
351
+ const dir = attachmentDir(this.appData, sessionId);
352
+ if (!attachmentIds) {
353
+ await rm(dir, { recursive: true, force: true });
354
+ return;
355
+ }
356
+ await Promise.all(attachmentIds.map((attachmentId) => rm(path.join(dir, attachmentId), { force: true })));
357
+ }
358
+ async saveAttachments(sessionId, images) {
359
+ if (images.length === 0)
360
+ return [];
361
+ if (!(await this.get(sessionId)))
362
+ throw new Error(`chat session not found: ${sessionId}`);
363
+ const dir = attachmentDir(this.appData, sessionId);
364
+ await mkdir(dir, { recursive: true });
365
+ const stored = [];
366
+ try {
367
+ for (const [index, image] of images.entries()) {
368
+ if (!isChatImageMimeType(image.mimeType)) {
369
+ throw new Error("unsupported chat attachment mime type");
370
+ }
371
+ const id = randomUUID();
372
+ const bytes = Buffer.from(image.data, "base64");
373
+ await writeFile(path.join(dir, id), bytes, { flag: "wx", mode: 0o600 });
374
+ stored.push({
375
+ id,
376
+ name: `图片 ${index + 1}`,
377
+ mimeType: image.mimeType,
378
+ byteLength: bytes.length,
379
+ });
380
+ }
381
+ }
382
+ catch (error) {
383
+ await this.removeAttachmentFiles(sessionId, stored.map((attachment) => attachment.id)).catch(() => undefined);
384
+ throw error;
385
+ }
386
+ return stored;
387
+ }
388
+ async readAttachment(sessionId, attachmentId) {
389
+ if (!isChatAttachmentId(attachmentId))
390
+ return undefined;
391
+ const record = await this.get(sessionId);
392
+ const metadata = record?.messages
393
+ .flatMap((message) => Array.isArray(message.attachments) ? message.attachments : [])
394
+ .find((attachment) => attachment.id === attachmentId);
395
+ if (!metadata || !isChatImageMimeType(metadata.mimeType))
396
+ return undefined;
397
+ try {
398
+ return {
399
+ metadata,
400
+ bytes: await readFile(path.join(attachmentDir(this.appData, sessionId), attachmentId)),
401
+ };
402
+ }
403
+ catch {
404
+ return undefined;
405
+ }
406
+ }
407
+ async removeAttachments(sessionId, attachments) {
408
+ await this.removeAttachmentFiles(sessionId, attachments.map((attachment) => attachment.id));
409
+ }
300
410
  async putDeferredTurn(sessionId, input) {
301
411
  return this.serialize(sessionId, async () => {
302
412
  const record = await this.get(sessionId);
@@ -454,11 +564,13 @@ export class ChatSessionStore {
454
564
  const deferredTurn = index >= 0 ? queue[index] : undefined;
455
565
  if (!deferredTurn || deferredTurn.state !== "dispatching")
456
566
  return undefined;
567
+ const attachments = await this.saveAttachments(sessionId, deferredTurn.images);
457
568
  const message = {
458
569
  id: `m_${record.messages.length + 1}_${Math.random().toString(36).slice(2, 8)}`,
459
570
  role: "user",
460
571
  text,
461
572
  at: new Date().toISOString(),
573
+ ...(attachments.length > 0 ? { attachments } : {}),
462
574
  };
463
575
  queue.splice(index, 1);
464
576
  record.messages.push(message);
@@ -468,7 +580,13 @@ export class ChatSessionStore {
468
580
  if (!record.displayTitle)
469
581
  record.displayTitle = cleanChatTitle(message.text);
470
582
  record.updatedAt = message.at;
471
- await this.persist(record);
583
+ try {
584
+ await this.persist(record);
585
+ }
586
+ catch (error) {
587
+ await this.removeAttachments(sessionId, attachments).catch(() => undefined);
588
+ throw error;
589
+ }
472
590
  return {
473
591
  record,
474
592
  message,
@@ -623,11 +741,20 @@ export async function runChatTurn(options) {
623
741
  };
624
742
  }
625
743
  if (options.persistUserMessage !== false) {
626
- const userMessage = await store.appendMessage(sessionId, {
627
- role: "user",
628
- text,
629
- ...(options.clientRequestId ? { clientRequestId: options.clientRequestId } : {}),
630
- });
744
+ const attachments = await store.saveAttachments(sessionId, options.images ?? []);
745
+ let userMessage;
746
+ try {
747
+ userMessage = await store.appendMessage(sessionId, {
748
+ role: "user",
749
+ text,
750
+ ...(options.clientRequestId ? { clientRequestId: options.clientRequestId } : {}),
751
+ ...(attachments.length > 0 ? { attachments } : {}),
752
+ });
753
+ }
754
+ catch (error) {
755
+ await store.removeAttachments(sessionId, attachments).catch(() => undefined);
756
+ throw error;
757
+ }
631
758
  const afterUserMessage = await store.get(sessionId);
632
759
  if (afterUserMessage)
633
760
  await options.onUserMessagePersisted?.(afterUserMessage, userMessage);
@@ -687,6 +814,7 @@ export async function runChatTurn(options) {
687
814
  // persisted + framed above. Nothing further to persist here.
688
815
  break;
689
816
  case "tool_call":
817
+ case "tool_update":
690
818
  // Forwarded to the caller (SSE) for UI feedback. The actual tool
691
819
  // execution happens in the SDK's customTools[].execute(), which
692
820
  // routes to dispatchChatToolCall (Gate 3). Do NOT dispatch here —
@@ -42,6 +42,13 @@ export const SHORTCUTS = [
42
42
  action: "reload",
43
43
  source: "operator",
44
44
  },
45
+ {
46
+ command: "/context",
47
+ label: "Context insights",
48
+ description: "打开上下文洞察仪表盘:构成、趋势、事件与文件活动",
49
+ action: "focus:context-insights",
50
+ source: "operator",
51
+ },
45
52
  ];
46
53
  export const SDD_BINGO_SHORTCUT = {
47
54
  command: "/sdd-bingo",
@@ -0,0 +1,22 @@
1
+ /** Repair only a local submission pair after the ordinary stable sort.
2
+ * A pair-specific comparator would be non-transitive when a third row falls
3
+ * between the user and placeholder, so this bounded pass moves the user back
4
+ * to the placeholder's established timeline seat instead. */
5
+ export function ensureLinkedSubmissionOrder(messages) {
6
+ const next = [...messages];
7
+ for (let assistantIndex = 0; assistantIndex < next.length; assistantIndex += 1) {
8
+ const assistant = next[assistantIndex];
9
+ if (assistant?.role !== "assistant" || !assistant.clientRequestId)
10
+ continue;
11
+ const userIndex = next.findIndex((message, index) => index > assistantIndex &&
12
+ message.role === "user" &&
13
+ message.clientRequestId === assistant.clientRequestId);
14
+ if (userIndex < 0)
15
+ continue;
16
+ const [user] = next.splice(userIndex, 1);
17
+ if (user)
18
+ next.splice(assistantIndex, 0, user);
19
+ assistantIndex += 1;
20
+ }
21
+ return next;
22
+ }
@@ -20,6 +20,39 @@ import { OFFICIAL_DENIED_TOOL_IDS } from "../resource-loader.js";
20
20
  import { EXECUTABLE_GATE_ONLY_ACTIONS } from "./tool-adapter.js";
21
21
  import { OPERATOR_CHAT_AGENT_TOOL_NAME } from "./subagents/types.js";
22
22
  const EXECUTABLE_GATE_ONLY = new Set(EXECUTABLE_GATE_ONLY_ACTIONS);
23
+ /** Server-owned metadata for SQL custom tools advertised to the Console UI. */
24
+ export const OPERATOR_CHAT_SQL_TOOL_CAPABILITIES = Object.freeze([
25
+ {
26
+ toolId: "sql_query",
27
+ description: "对命名数据库连接执行受限只读 SQL 查询。",
28
+ kind: "read",
29
+ },
30
+ {
31
+ toolId: "sql_exec",
32
+ description: "已禁用:数据库连接强制只读,拒绝 DELETE、DROP 及所有写入或 DDL。",
33
+ kind: "read",
34
+ },
35
+ {
36
+ toolId: "sql_schema",
37
+ description: "查看数据库表及字段结构。",
38
+ kind: "read",
39
+ },
40
+ {
41
+ toolId: "sql_stats",
42
+ description: "查看数据库表行数和存储规模统计。",
43
+ kind: "read",
44
+ },
45
+ {
46
+ toolId: "sql_health",
47
+ description: "检查数据库连接健康状态与安全配置。",
48
+ kind: "read",
49
+ },
50
+ {
51
+ toolId: "sql_connections",
52
+ description: "列出、新增或移除受治理的命名数据库连接。",
53
+ kind: "mutation",
54
+ },
55
+ ]);
23
56
  /** Built-in Chat custom tools that are not operator registry actions. */
24
57
  export const OPERATOR_CHAT_BUILTIN_CUSTOM_TOOLS = Object.freeze([
25
58
  OPERATOR_CHAT_AGENT_TOOL_NAME,
@@ -35,6 +68,7 @@ export const OPERATOR_CHAT_BUILTIN_CUSTOM_TOOLS = Object.freeze([
35
68
  "browser_dialog",
36
69
  "browser_wait",
37
70
  "browser_screenshot",
71
+ ...OPERATOR_CHAT_SQL_TOOL_CAPABILITIES.map((tool) => tool.toolId),
38
72
  ]);
39
73
  /**
40
74
  * Actions the model MAY invoke. Derived from the registry but filtered by the