@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
@@ -265,6 +265,7 @@ export async function createWorkspaceContext(record, deps) {
265
265
  const aimaxAuth = new AimaxAuthReader();
266
266
  await aimaxAuth.getToken();
267
267
  const sddDataAlignment = new SddDataAlignmentService(chatStore, aimaxAuth, deps.fetchImpl);
268
+ actionContext.sddDataAlignment = sddDataAlignment;
268
269
  const chatEventEpoch = randomBytes(12).toString("hex");
269
270
  const chatEvents = createChatEventStore({
270
271
  appData,
@@ -318,6 +319,7 @@ export async function createWorkspaceContext(record, deps) {
318
319
  terminalRegistry,
319
320
  scmService,
320
321
  browserAutomation,
322
+ globalAppDataRoot: appData.root,
321
323
  });
322
324
  let chatRouteDeps;
323
325
  const goalRoundDriver = new GoalRoundDriver((sessionId) => driveGoalRound(chatRouteDeps, sessionId));
@@ -2,7 +2,7 @@ import { lstat, readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { acquireUpdateLock, updateLockPath } from "../../shared/update/state.js";
4
4
  import os from "node:os";
5
- import { TERMINAL_OPERATION_STATES } from "../../infrastructure/console/operation-store.js";
5
+ import { isOperationOccupied } from "../../infrastructure/console/operation-store.js";
6
6
  import { readInitRuntimeActivity } from "../observability/init-runtime-activity.js";
7
7
  const record = (value) => {
8
8
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -138,7 +138,7 @@ export class WorkspaceInitializationService {
138
138
  }
139
139
  async check(refresh = false) {
140
140
  const latest = (await this.options.operations.list()).filter(op => op.action === "workspaceInitialize").at(-1);
141
- const running = latest && !TERMINAL_OPERATION_STATES.has(latest.state) && latest.state !== "needs-reconcile";
141
+ const running = latest && isOperationOccupied(latest.state);
142
142
  if (!running && (refresh || !this.cached || Date.now() - this.cached.at > 30_000)) {
143
143
  const generation = this.generation;
144
144
  this.pending ??= this.inspect().then(value => { if (generation === this.generation)
@@ -174,7 +174,7 @@ export class WorkspaceInitializationService {
174
174
  if (activity.workerProjectionError || activity.activeDagRunIds.length || activity.activeWorkerTasks || activity.activeWorkerBatches || this.options.activeChatTurnCount())
175
175
  throw new Error("当前工作区有任务或对话正在运行,请结束后重试");
176
176
  const operations = await this.options.operations.list();
177
- if (operations.some(op => op.operationId !== operationId && !TERMINAL_OPERATION_STATES.has(op.state) && op.state !== "needs-reconcile"))
177
+ if (operations.some(op => op.operationId !== operationId && isOperationOccupied(op.state)))
178
178
  throw new Error("当前工作区有其他操作正在运行,请稍后重试");
179
179
  const before = await this.inspect();
180
180
  if (before.state === "error")
@@ -2176,6 +2176,12 @@ function isExecutedNodeStatus(status) {
2176
2176
  return false;
2177
2177
  return EXECUTED_NODE_STATUSES.has(status.toLowerCase());
2178
2178
  }
2179
+ /**
2180
+ * Node statuses that have not reached a terminal outcome. A live node's last
2181
+ * recorded attempt can be a failure that is already being retried, so history
2182
+ * copy must not present it as the final result.
2183
+ */
2184
+ const LIVE_NODE_STATUSES = new Set(["running", "started", "paused"]);
2179
2185
  const PASS_SUCCESS_STATUSES = new Set([
2180
2186
  "finished",
2181
2187
  "completed",
@@ -2221,12 +2227,21 @@ export function projectExecutionSummary(options) {
2221
2227
  const recovered = totalAttemptCount > 1 &&
2222
2228
  firstOutcome === "failure" &&
2223
2229
  finalOutcome === "success";
2230
+ // A node that is still live (running/started/paused) has not reached a
2231
+ // final outcome: its last *recorded* attempt may have failed while a
2232
+ // retry is already queued. Labelling that "最终失败" told the operator
2233
+ // the run was over when it was still working, so keep the attempt count
2234
+ // and mark the last attempt instead of the node.
2235
+ const stillRunning = options.status !== undefined &&
2236
+ LIVE_NODE_STATUSES.has(options.status.toLowerCase());
2224
2237
  // 一次就成功/失败:卡片不展示 historyLabel;Inspector 仍可拿 attempts 元数据。
2225
2238
  const historyLabel = totalAttemptCount <= 1
2226
2239
  ? undefined
2227
2240
  : recovered
2228
2241
  ? `${totalAttemptCount} 次 · 先失败后成功`
2229
- : `${totalAttemptCount} 次 · 最终${finalOutcome === "success" ? "成功" : "失败"}`;
2242
+ : stillRunning && finalOutcome === "failure"
2243
+ ? `${totalAttemptCount} 次 · 最近一次失败,仍在重试`
2244
+ : `${totalAttemptCount} 次 · 最终${finalOutcome === "success" ? "成功" : "失败"}`;
2230
2245
  return {
2231
2246
  totalAttemptCount,
2232
2247
  ...(recovered ? { recovered: true } : {}),
@@ -2266,12 +2281,18 @@ export function projectExecutionSummary(options) {
2266
2281
  }
2267
2282
  // 仅一轮 pass 时不在卡片展示(无叙事价值);多轮才展示中文「修复轮次 N」
2268
2283
  // 主文案,裸 P1/P2 编号不再作为主标题(原始编号仅 hover/title/诊断)。
2284
+ // 与 attempt 分支同理:节点仍在运行时,最后一个已记录 pass 的失败只是
2285
+ // 阶段性事实,不能当作最终结论。
2286
+ const livePasses = options.status !== undefined &&
2287
+ LIVE_NODE_STATUSES.has(options.status.toLowerCase());
2269
2288
  const historyLabel = passes.length <= 1
2270
2289
  ? undefined
2271
2290
  : `修复轮次 ${passes.join(",")}${finalOutcome === "success"
2272
2291
  ? " · 最终成功"
2273
2292
  : finalOutcome === "failure"
2274
- ? " · 最终失败"
2293
+ ? livePasses
2294
+ ? " · 最近一轮失败,仍在重试"
2295
+ : " · 最终失败"
2275
2296
  : ""}`;
2276
2297
  return {
2277
2298
  totalAttemptCount: passes.length,
@@ -0,0 +1,479 @@
1
+ /**
2
+ * Read-only context insights projection for a DAG node attempt — the
3
+ * loop-agent counterpart of the dsh-context dashboard, built exclusively from
4
+ * recorded run facts:
5
+ *
6
+ * - context receipt (`<node>/context-receipts/attempt-N.json`) → composition
7
+ * - session events (`<node>/session-events.jsonl`) → per-request usage
8
+ * trend, tool activity, file footprint, timing
9
+ * - node record (`<node>.json`) → skill injection footprint, headline totals
10
+ *
11
+ * Everything is derived deterministically from persisted facts; nothing is
12
+ * estimated beyond what the recorded bytes already contain. All projections are
13
+ * read-only, bounded, and redaction-preserving (paths already pass through
14
+ * redactSecrets in node-transparency before reaching here for receipt data;
15
+ * file-activity paths are redacted in this module).
16
+ */
17
+ import { readFile } from "node:fs/promises";
18
+ import path from "node:path";
19
+ import { redactSecrets } from "../../shared/preview.js";
20
+ import { isSafeObservabilityIdentifier } from "../observability/event-store.js";
21
+ import { resolveDagRunArtifact, resolveDagRunJson, } from "./dag-run-artifacts.js";
22
+ export const NODE_CONTEXT_INSIGHTS_SCHEMA_VERSION = 1;
23
+ /** Upper bound on session-event lines scanned per attempt (defensive). */
24
+ const SESSION_EVENT_LINE_LIMIT = 50_000;
25
+ function isRecord(value) {
26
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
27
+ }
28
+ function asInt(value) {
29
+ return typeof value === "number" && Number.isFinite(value) && value >= 0
30
+ ? value
31
+ : null;
32
+ }
33
+ function asString(value) {
34
+ return typeof value === "string" && value.length > 0 ? value : null;
35
+ }
36
+ /** Minimal per-line JSON parse (session events are JSONL). */
37
+ function parseSessionEventLine(line) {
38
+ try {
39
+ const parsed = JSON.parse(line);
40
+ return isRecord(parsed) ? parsed : null;
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ function messageOf(event) {
47
+ const message = event.message;
48
+ return isRecord(message) ? message : null;
49
+ }
50
+ function contentBlocksOf(message) {
51
+ const content = message?.content;
52
+ return Array.isArray(content) ? content : [];
53
+ }
54
+ function usageOf(message) {
55
+ const usage = message?.usage;
56
+ return isRecord(usage) ? usage : null;
57
+ }
58
+ function toolNameOfArgs(args) {
59
+ for (const key of ["path", "file", "file_path", "filePath"]) {
60
+ const value = args[key];
61
+ if (typeof value === "string" && value.length > 0)
62
+ return value;
63
+ }
64
+ return null;
65
+ }
66
+ function searchTargetsOfArgs(args) {
67
+ const targets = [];
68
+ const push = (value) => {
69
+ if (typeof value === "string" && value.length > 0)
70
+ targets.push(value);
71
+ };
72
+ push(args.path);
73
+ push(args.directory);
74
+ push(args.dir);
75
+ const pattern = asString(args.pattern) ?? asString(args.query);
76
+ if (pattern)
77
+ targets.push(`pattern:${pattern.slice(0, 120)}`);
78
+ return targets;
79
+ }
80
+ /** Extract referenced file paths from a bash command string (best effort). */
81
+ function filesFromCommand(command) {
82
+ const files = [];
83
+ const patterns = [
84
+ /(?:^|\s)(?:cat|head|tail|less|more)\s+([^\s;&|]+)/g,
85
+ /(?:^|\s)(?:rg|grep|find|fd|ls)\s+.*?([^\s;&|]*\.[A-Za-z0-9]{1,8})/g,
86
+ /(?:^|\s)(?:sed|awk|sort|uniq|wc)\s+.*?([^\s;&|]*\.[A-Za-z0-9]{1,8})/g,
87
+ ];
88
+ for (const pattern of patterns) {
89
+ let match;
90
+ while ((match = pattern.exec(command)) !== null) {
91
+ const candidate = match[1];
92
+ if (candidate && candidate.length > 1)
93
+ files.push(candidate);
94
+ }
95
+ }
96
+ return files;
97
+ }
98
+ function classifyFileOp(toolName) {
99
+ const name = toolName.toLowerCase();
100
+ if (["read", "read_file", "readfile", "view"].includes(name))
101
+ return "read";
102
+ if (["write", "write_file", "writefile", "create"].includes(name))
103
+ return "write";
104
+ if (["edit", "str_replace", "str_replace_editor", "apply_patch"].includes(name))
105
+ return "edit";
106
+ if (["grep", "rg", "find", "fd", "search", "glob", "list", "ls"].includes(name))
107
+ return "search";
108
+ return null;
109
+ }
110
+ function normalizeRepoPath(raw, repoRoot) {
111
+ let value = raw;
112
+ const resolvedRoot = path.resolve(repoRoot);
113
+ const rootVariants = [
114
+ resolvedRoot,
115
+ resolvedRoot.replace(/\\/g, "/"),
116
+ resolvedRoot.replace(/\\/g, "/").toLowerCase(),
117
+ ];
118
+ for (const variant of rootVariants) {
119
+ if (value.toLowerCase().startsWith(`${variant.toLowerCase()}\\`) ||
120
+ value.toLowerCase().startsWith(`${variant.toLowerCase()}/`)) {
121
+ value = value.slice(variant.length + 1);
122
+ break;
123
+ }
124
+ if (value.toLowerCase() === variant.toLowerCase()) {
125
+ value = ".";
126
+ break;
127
+ }
128
+ }
129
+ return value.replace(/\\/g, "/");
130
+ }
131
+ function accumulateFileActivity(fileActivity, key, op) {
132
+ const existing = fileActivity.get(key) ?? {
133
+ path: key,
134
+ reads: 0,
135
+ writes: 0,
136
+ edits: 0,
137
+ searches: 0,
138
+ };
139
+ if (op === "read")
140
+ existing.reads += 1;
141
+ else if (op === "write")
142
+ existing.writes += 1;
143
+ else if (op === "edit")
144
+ existing.edits += 1;
145
+ else
146
+ existing.searches += 1;
147
+ fileActivity.set(key, existing);
148
+ }
149
+ /**
150
+ * Pure projection over parsed session events. Exported for unit tests.
151
+ */
152
+ export function projectSessionInsights(events, repoRoot) {
153
+ let requestCount = 0;
154
+ let toolCalls = 0;
155
+ let toolErrors = 0;
156
+ let humanInputs = 0;
157
+ let thinkingSteps = 0;
158
+ let inputTokens = 0;
159
+ let outputTokens = 0;
160
+ let cacheReadTokens = 0;
161
+ let cacheWriteTokens = 0;
162
+ let cacheSamples = 0;
163
+ let cacheHits = 0;
164
+ const requests = [];
165
+ const toolCounters = new Map();
166
+ const fileActivity = new Map();
167
+ // Tool calls issued in the current in-flight model request (reset per
168
+ // assistant message_end) so each request row attributes its own calls.
169
+ let pendingToolCalls = 0;
170
+ let pendingThinking = false;
171
+ for (const event of events) {
172
+ if (!event)
173
+ continue;
174
+ const type = asString(event.type);
175
+ if (type === "tool_execution_start") {
176
+ toolCalls += 1;
177
+ pendingToolCalls += 1;
178
+ const toolName = asString(event.toolName) ?? "unknown";
179
+ const row = toolCounters.get(toolName) ?? {
180
+ toolName,
181
+ calls: 0,
182
+ errors: 0,
183
+ };
184
+ row.calls += 1;
185
+ toolCounters.set(toolName, row);
186
+ const args = isRecord(event.args) ? event.args : {};
187
+ const op = classifyFileOp(toolName);
188
+ if (op === "read" || op === "write" || op === "edit") {
189
+ const target = toolNameOfArgs(args);
190
+ if (target) {
191
+ accumulateFileActivity(fileActivity, normalizeRepoPath(redactSecrets(target), repoRoot), op);
192
+ }
193
+ }
194
+ else if (op === "search") {
195
+ for (const target of searchTargetsOfArgs(args)) {
196
+ accumulateFileActivity(fileActivity, normalizeRepoPath(redactSecrets(target), repoRoot), "search");
197
+ }
198
+ }
199
+ else if (toolName.toLowerCase() === "bash") {
200
+ const command = asString(args.command) ?? "";
201
+ for (const file of filesFromCommand(command)) {
202
+ accumulateFileActivity(fileActivity, normalizeRepoPath(redactSecrets(file), repoRoot), "search");
203
+ }
204
+ }
205
+ continue;
206
+ }
207
+ if (type === "tool_execution_end") {
208
+ if (event.isError === true)
209
+ toolErrors += 1;
210
+ continue;
211
+ }
212
+ const message = messageOf(event);
213
+ if (!message)
214
+ continue;
215
+ const role = asString(message.role);
216
+ if (type === "message_start" && role === "assistant") {
217
+ // A new model request begins.
218
+ requestCount += 1;
219
+ pendingToolCalls = 0;
220
+ pendingThinking = false;
221
+ continue;
222
+ }
223
+ if (type === "message_end" && role === "assistant") {
224
+ for (const block of contentBlocksOf(message)) {
225
+ if (isRecord(block) && asString(block.type) === "thinking") {
226
+ thinkingSteps += 1;
227
+ pendingThinking = true;
228
+ }
229
+ }
230
+ const usage = usageOf(message);
231
+ const input = asInt(usage?.input);
232
+ const output = asInt(usage?.output);
233
+ const cacheRead = asInt(usage?.cacheRead);
234
+ const cacheWrite = asInt(usage?.cacheWrite);
235
+ if (usage && (input ?? 0) + (cacheRead ?? 0) > 0) {
236
+ cacheSamples += 1;
237
+ if ((cacheRead ?? 0) > 0)
238
+ cacheHits += 1;
239
+ }
240
+ if (input != null)
241
+ inputTokens += input;
242
+ if (output != null)
243
+ outputTokens += output;
244
+ if (cacheRead != null)
245
+ cacheReadTokens += cacheRead;
246
+ if (cacheWrite != null)
247
+ cacheWriteTokens += cacheWrite;
248
+ requests.push({
249
+ request: requestCount,
250
+ recordedAt: asString(event.recordedAt),
251
+ model: asString(message.model),
252
+ provider: asString(message.provider),
253
+ usage: usage == null
254
+ ? null
255
+ : {
256
+ input,
257
+ output,
258
+ cacheRead,
259
+ cacheWrite,
260
+ totalTokens: asInt(usage.totalTokens),
261
+ },
262
+ stopReason: asString(message.stopReason),
263
+ toolCalls: pendingToolCalls,
264
+ thinking: pendingThinking,
265
+ });
266
+ pendingToolCalls = 0;
267
+ pendingThinking = false;
268
+ continue;
269
+ }
270
+ if (type === "message_end" && role === "user") {
271
+ humanInputs += 1;
272
+ }
273
+ }
274
+ const toolActivity = [...toolCounters.values()].sort((a, b) => b.calls - a.calls || a.toolName.localeCompare(b.toolName));
275
+ const fileActivityRows = [...fileActivity.values()].sort((a, b) => b.reads + b.writes + b.edits + b.searches -
276
+ (a.reads + a.writes + a.edits + a.searches) ||
277
+ a.path.localeCompare(b.path));
278
+ return {
279
+ requests,
280
+ toolActivity,
281
+ fileActivity: fileActivityRows,
282
+ stats: {
283
+ requests: requestCount,
284
+ toolCalls,
285
+ toolErrors,
286
+ humanInputs,
287
+ thinkingSteps,
288
+ cacheHitRate: cacheSamples > 0 ? cacheHits / cacheSamples : null,
289
+ inputTokens,
290
+ outputTokens,
291
+ cacheReadTokens,
292
+ cacheWriteTokens,
293
+ },
294
+ };
295
+ }
296
+ export async function loadDagNodeContextInsights(repoRoot, dagRunId, nodeId, attempt) {
297
+ if (!isSafeObservabilityIdentifier(dagRunId)) {
298
+ return {
299
+ status: 400,
300
+ body: { error: "Invalid DAG run identifier", reason: "invalid-id" },
301
+ };
302
+ }
303
+ if (!isSafeObservabilityIdentifier(nodeId)) {
304
+ return {
305
+ status: 400,
306
+ body: { error: "Invalid node identifier", reason: "invalid-id" },
307
+ };
308
+ }
309
+ const resolvedRun = resolveDagRunJson(repoRoot, dagRunId);
310
+ if (!resolvedRun.ok) {
311
+ return {
312
+ status: resolvedRun.reason === "invalid-id" ? 400 : 404,
313
+ body: { error: "DAG run not found", reason: resolvedRun.reason },
314
+ };
315
+ }
316
+ // Node record (<nodeId>.json) carries attempt metadata + headline totals.
317
+ const recordResolved = resolveDagRunArtifact(repoRoot, dagRunId, [
318
+ `${nodeId}.json`,
319
+ ]);
320
+ let nodeRecord = null;
321
+ if (recordResolved.ok) {
322
+ try {
323
+ const raw = JSON.parse(await readFile(recordResolved.result.absolutePath, "utf8"));
324
+ if (isRecord(raw))
325
+ nodeRecord = raw;
326
+ }
327
+ catch {
328
+ nodeRecord = null;
329
+ }
330
+ }
331
+ const selectedAttempt = normalizeAttemptNumber(attempt, nodeRecord);
332
+ // Context receipt composition.
333
+ const receiptResolved = resolveDagRunArtifact(repoRoot, dagRunId, [
334
+ nodeId,
335
+ "context-receipts",
336
+ `attempt-${selectedAttempt}.json`,
337
+ ]);
338
+ let composition = null;
339
+ if (receiptResolved.ok) {
340
+ try {
341
+ const raw = JSON.parse(await readFile(receiptResolved.result.absolutePath, "utf8"));
342
+ if (isRecord(raw) && Array.isArray(raw.orderedComponents)) {
343
+ const byKind = new Map();
344
+ for (const entry of raw.orderedComponents) {
345
+ if (!isRecord(entry))
346
+ continue;
347
+ const kind = asString(entry.kind);
348
+ const bytes = asInt(entry.bytes) ?? 0;
349
+ if (!kind)
350
+ continue;
351
+ const slice = byKind.get(kind) ?? { kind, items: 0, bytes: 0 };
352
+ slice.items += 1;
353
+ slice.bytes += bytes;
354
+ byKind.set(kind, slice);
355
+ }
356
+ composition = [...byKind.values()].sort((a, b) => b.bytes - a.bytes);
357
+ }
358
+ }
359
+ catch {
360
+ composition = null;
361
+ }
362
+ }
363
+ // Session events for the node (single file across attempts in v1 format).
364
+ const sessionResolved = resolveDagRunArtifact(repoRoot, dagRunId, [
365
+ nodeId,
366
+ "session-events.jsonl",
367
+ ]);
368
+ let sessionProjection = null;
369
+ if (sessionResolved.ok) {
370
+ try {
371
+ const raw = await readFile(sessionResolved.result.absolutePath, "utf8");
372
+ const lines = raw.split("\n").slice(0, SESSION_EVENT_LINE_LIMIT);
373
+ const events = lines
374
+ .map((line) => line.trim())
375
+ .filter(Boolean)
376
+ .map(parseSessionEventLine);
377
+ sessionProjection = projectSessionInsights(events, repoRoot);
378
+ }
379
+ catch {
380
+ sessionProjection = null;
381
+ }
382
+ }
383
+ const attemptRecord = findAttemptRecord(nodeRecord, selectedAttempt);
384
+ const timing = timingFromRecords(nodeRecord, attemptRecord);
385
+ const skills = skillInjectionsOf(nodeRecord);
386
+ const body = {
387
+ schemaVersion: NODE_CONTEXT_INSIGHTS_SCHEMA_VERSION,
388
+ dagRunId,
389
+ nodeId,
390
+ attempt: selectedAttempt,
391
+ generatedFrom: {
392
+ sessionEvents: sessionProjection != null,
393
+ contextReceipt: composition != null,
394
+ nodeRecord: nodeRecord != null,
395
+ },
396
+ stats: {
397
+ requests: sessionProjection?.stats.requests ?? 0,
398
+ toolCalls: sessionProjection?.stats.toolCalls ?? 0,
399
+ toolErrors: sessionProjection?.stats.toolErrors ?? 0,
400
+ humanInputs: sessionProjection?.stats.humanInputs ?? 0,
401
+ thinkingSteps: sessionProjection?.stats.thinkingSteps ?? 0,
402
+ tokensUsed: asInt(attemptRecord?.tokensUsed) ?? asInt(nodeRecord?.tokensUsed),
403
+ parsedEvents: asInt(attemptRecord?.parsedEvents) ?? asInt(nodeRecord?.parsedEvents),
404
+ writeToolCallCount: asInt(attemptRecord?.writeToolCallCount) ??
405
+ asInt(nodeRecord?.writeToolCallCount),
406
+ cacheHitRate: sessionProjection?.stats.cacheHitRate ?? null,
407
+ inputTokens: sessionProjection?.stats.inputTokens ?? null,
408
+ outputTokens: sessionProjection?.stats.outputTokens ?? null,
409
+ cacheReadTokens: sessionProjection?.stats.cacheReadTokens ?? null,
410
+ cacheWriteTokens: sessionProjection?.stats.cacheWriteTokens ?? null,
411
+ },
412
+ timing,
413
+ composition,
414
+ skills,
415
+ toolActivity: sessionProjection?.toolActivity ?? [],
416
+ fileActivity: sessionProjection?.fileActivity ?? [],
417
+ usageTrend: sessionProjection?.requests ?? [],
418
+ };
419
+ return { status: 200, body };
420
+ }
421
+ function normalizeAttemptNumber(rawAttempt, nodeRecord) {
422
+ if (Number.isSafeInteger(rawAttempt) && (rawAttempt ?? 0) > 0) {
423
+ return rawAttempt;
424
+ }
425
+ if (typeof nodeRecord?.currentAttempt === "number" &&
426
+ Number.isSafeInteger(nodeRecord.currentAttempt) &&
427
+ nodeRecord.currentAttempt > 0) {
428
+ return nodeRecord.currentAttempt;
429
+ }
430
+ return 1;
431
+ }
432
+ function findAttemptRecord(nodeRecord, attempt) {
433
+ if (!nodeRecord)
434
+ return null;
435
+ const attempts = nodeRecord.attempts;
436
+ if (!Array.isArray(attempts))
437
+ return null;
438
+ for (const entry of attempts) {
439
+ if (isRecord(entry) && entry.attempt === attempt)
440
+ return entry;
441
+ }
442
+ return null;
443
+ }
444
+ function timingFromRecords(nodeRecord, attemptRecord) {
445
+ const attemptTiming = isRecord(attemptRecord?.timing)
446
+ ? attemptRecord.timing
447
+ : null;
448
+ const nodeTiming = isRecord(nodeRecord?.timing)
449
+ ? nodeRecord.timing
450
+ : null;
451
+ return {
452
+ startedAt: asString(attemptRecord?.startedAt) ?? asString(nodeRecord?.startedAt),
453
+ finishedAt: asString(attemptRecord?.finishedAt) ?? asString(nodeRecord?.finishedAt),
454
+ durationMs: asInt(attemptRecord?.durationMs) ?? asInt(nodeRecord?.durationMs),
455
+ executorDurationMs: asInt(attemptTiming?.executorDurationMs) ??
456
+ asInt(nodeTiming?.executorDurationMs),
457
+ };
458
+ }
459
+ function skillInjectionsOf(nodeRecord) {
460
+ const raw = nodeRecord?.resolvedSkills;
461
+ if (!Array.isArray(raw))
462
+ return [];
463
+ const skills = [];
464
+ for (const entry of raw) {
465
+ if (!isRecord(entry))
466
+ continue;
467
+ const name = asString(entry.name);
468
+ if (!name)
469
+ continue;
470
+ skills.push({
471
+ name,
472
+ resolution: asString(entry.resolution) ?? "unknown",
473
+ bytes: asInt(entry.bytes),
474
+ truncated: entry.truncated === true,
475
+ injectionMode: asString(entry.injectionMode),
476
+ });
477
+ }
478
+ return skills;
479
+ }