@tea-agent/loop-agent 0.44.0-next.1 → 0.44.0-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +379 -416
- package/dist/application/evaluation/budget.js +21 -0
- package/dist/application/evaluation/corpus-hash.js +10 -15
- package/dist/application/evaluation/corpus.js +2 -1
- package/dist/application/evaluation/frontend-browser-acceptance.js +77 -0
- package/dist/application/evaluation/frontend-corpus-browser-acceptance.js +175 -0
- package/dist/application/evaluation/frontend-gateway-evidence.js +226 -0
- package/dist/application/evaluation/frontend-pair-registration.js +143 -0
- package/dist/application/evaluation/frontend-paired-summary.js +150 -0
- package/dist/application/evaluation/frontend-run-observation.js +144 -0
- package/dist/application/evaluation/frontend-shared-evidence.js +105 -0
- package/dist/application/evaluation/types.js +45 -12
- package/dist/application/task-lifecycle/observe.js +26 -1
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +11 -0
- package/dist/cli/program.js +4 -0
- package/dist/commands/task-advance.js +3 -5
- package/dist/commands/task-source-prepare.js +13 -1
- package/dist/executors/dag-pi/guards.js +4 -0
- package/dist/executors/dag-pi/model-config.js +30 -0
- package/dist/executors/dag-pi/plan/fact-adoption.js +55 -0
- package/dist/{workflows/dag/frontend-plan-completeness.js → executors/dag-pi/plan/facts.js} +22 -28
- package/dist/executors/dag-pi/plan/finalize-tools.js +259 -0
- package/dist/executors/dag-pi/plan/ledger-contract.js +55 -0
- package/dist/executors/dag-pi/plan/record-tools.js +861 -0
- package/dist/executors/dag-pi/plan/schema.js +129 -0
- package/dist/executors/dag-pi/plan/session.js +1 -0
- package/dist/executors/dag-pi/plan/tool-helpers.js +16 -0
- package/dist/executors/dag-pi/sessions/index.js +6 -0
- package/dist/executors/dag-pi/sessions/plan-batches.js +280 -0
- package/dist/executors/dag-pi/sessions/plan-prompts.js +367 -0
- package/dist/executors/dag-pi/sessions/scout-parallel.js +197 -0
- package/dist/executors/dag-pi/sessions/segmented-plan.js +1184 -0
- package/dist/executors/dag-pi/sessions/writer-evidence.js +60 -0
- package/dist/executors/dag-pi/tools/contract-tools.js +515 -0
- package/dist/executors/dag-pi/tools/design-terminal-tools.js +159 -0
- package/dist/executors/dag-pi/tools/plan-decision-tools.js +529 -0
- package/dist/executors/dag-pi/tools/review-terminal-tools.js +159 -0
- package/dist/executors/dag-pi/tools/scout-evidence-tools.js +270 -0
- package/dist/executors/dag-pi-executor.js +733 -5782
- package/dist/executors/pi-executor.js +6 -1
- package/dist/executors/pi-sdk-executor.js +112 -19
- package/dist/executors/shell-executor.js +88 -12
- package/dist/executors/shell-presets.js +1 -1
- package/dist/executors/shell-write-guard.js +26 -3
- package/dist/infrastructure/console/operation-store.js +48 -14
- package/dist/infrastructure/evaluation/corpus-store.js +37 -12
- package/dist/shared/file-identity.js +23 -0
- package/dist/shared/git-workspace-status.js +482 -0
- package/dist/shared/operator/capabilities.js +10 -5
- package/dist/shared/path-safety.js +2 -2
- package/dist/shared/wizard-local-telemetry.js +51 -0
- package/dist/task/config-types.js +22 -8
- package/dist/task/contract/project.js +13 -0
- package/dist/task/contract/schema.js +2 -7
- package/dist/task/frontend-preflight.js +1 -1
- package/dist/task/source-prepare/parse-intent.js +124 -5
- package/dist/task/source-prepare/prepare.js +6 -1
- package/dist/task/source-prepare/semantic-intake.js +18 -3
- package/dist/task/source-prepare/source-execution.js +65 -0
- package/dist/task/source-prepare/source-fidelity-pi.js +22 -9
- package/dist/task/source-prepare/source-provider-budget.js +290 -0
- package/dist/worker/console/chat/assistant-content.js +5 -34
- package/dist/worker/console/chat/chat-event-store.js +62 -10
- package/dist/worker/console/chat/context-insights.js +723 -0
- package/dist/worker/console/chat/conversation-nodes.js +383 -0
- package/dist/worker/console/chat/db-connections.js +854 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +1 -1
- package/dist/worker/console/chat/pi-runtime/custom-tools/sql-tools.js +655 -0
- package/dist/worker/console/chat/pi-runtime.js +445 -36
- package/dist/worker/console/chat/provider-error.js +0 -12
- package/dist/worker/console/chat/repo-browser.js +52 -4
- package/dist/worker/console/chat/routes.js +577 -72
- package/dist/worker/console/chat/sdd-data-alignment.js +8 -31
- package/dist/worker/console/chat/session-store.js +147 -19
- package/dist/worker/console/chat/shortcuts.js +7 -0
- package/dist/worker/console/chat/timeline-message-order.js +22 -0
- package/dist/worker/console/chat/tools.js +34 -0
- package/dist/worker/console/chat/turn-process.js +237 -66
- package/dist/worker/console/chat/user-turn-outline.js +19 -0
- package/dist/worker/console/console-update-runtime.js +2 -2
- package/dist/worker/console/dag-confirmation.js +3 -0
- package/dist/worker/console/dag-execution-receipt.js +3 -0
- package/dist/worker/console/operator-actions.js +32 -3
- package/dist/worker/console/operator-user-error.js +28 -1
- package/dist/worker/console/pi-readiness.js +4 -4
- package/dist/worker/console/prd-intake-bridge.js +5 -12
- package/dist/worker/console/recovery-cta.js +14 -14
- package/dist/worker/console/server.js +5 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-8-j6y-sd.js → abnfDiagram-N423BO3Z-r1NAPU_5.js} +1 -1
- package/dist/worker/console/static/assets/{arc-eoQiMvuk.js → arc-dT5T4sVL.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-C4A3uMcI.js → architectureDiagram-T3A2C74G-CP_cOq46.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D4zD2F-Q.js → blockDiagram-VBNYF7ZC-sToEZxEf.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-j1RkJziL.js → c4Diagram-5PPSVZJV-DSxwwi2m.js} +1 -1
- package/dist/worker/console/static/assets/channel-CyBY_yGk.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-YBHmhik1.js → chunk-2GRJ4B5K-B9fh0IkF.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-COfWyo9P.js → chunk-2Q5K7J3B-CAUNl-A6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-I99OUkHY.js → chunk-5RXB4S5H-BomeqbNX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-XKxoJNJ4.js → chunk-5VM5RSS4-BNAuTOLE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DLT_cYx1.js → chunk-6Q2QTUOP-DfzjRD5y.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CxcKZ9mV.js → chunk-GF5L2VYU-C3uE-yc2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-V1EqXdjY.js → chunk-JWPE2WC7-C3sQMMYr.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DebTtdFp.js → chunk-KBJHAD2P-D_AtEYQL.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-B-ivNRDf.js → chunk-RYQCIY6F-DW4TwhKv.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DC_Ds11b.js → chunk-XXDRQBXY-CTQDmJcp.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D5lN4E_E.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D5lN4E_E.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-sWEqKwIb.js → cose-bilkent-JH36ORCC-iMqPuBsL.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BXa_dcu4.js → cynefin-VYW2F7L2-DktIuDWs.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-C-Mle84F.js → cynefinDiagram-MW4NZA55-BgVnjyEs.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CXPDBITe.js → dagre-VZM6K2ZE-CR25E4bu.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CC-WJQfa.js → diagram-7IWD3JNH-CPM2h5HY.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CDAV5Vs4.js → diagram-B4RE2ZJO-DqF0x2g1.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CmFiAcNz.js → diagram-LBJQPF4R-G8BYp2eb.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DPBZHuyn.js → diagram-Q27KOJAE-BbC4kT_D.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-jUlm_Ds3.js → diagram-UB23O5K3-CKZwZ-G5.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DWhQ3mfY.js → ebnfDiagram-BXEA7PRR-kJDmheia.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Tr2gMqet.js → erDiagram-JOGREHBK-DFTVHyxB.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DJjVQHPA.js → flowDiagram-UKHOOZJN-BqdN5_os.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-D74dQ4u0.js → ganttDiagram-PKOTCBZU-SYDjVakh.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js → gitGraphDiagram-DS77QQ5N-Bf8bNpGA.js} +1 -1
- package/dist/worker/console/static/assets/index-B28Onmfy.css +1 -0
- package/dist/worker/console/static/assets/index-CTMZsDmh.js +486 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-ILCbxJyb.js → infoDiagram-6WML65LV-DIeAmJ45.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DeuWBQ89.js → ishikawaDiagram-WSZJBQD7-oikqUAlo.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CF5ih8Fk.js → journeyDiagram-NVQOT4AX-mKEF_xYT.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C7yOSuRO.js → kanban-definition-27J2QSJJ-BdWJHPnN.js} +1 -1
- package/dist/worker/console/static/assets/{linear-BDZ9riWi.js → linear-DGRlieMH.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-7pKqYtpZ.js → mermaid.core-4P9yPC9c.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-LeJDybSU.js → mindmap-definition-FAOFIHXS-5PTSkos6.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BJvT3pMD.js → pegDiagram-VL7TDLO6-DONIFpG5.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-_rGqpMan.js → pieDiagram-7S7Q4E2Y-D_Z4S70M.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DPcSv5aA.js → quadrantDiagram-CIZ2JOQS-CAsjh8iY.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Drxx4hkJ.js → railroadDiagram-AXF67PYL-Cx4Dd6ac.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BCTUdU4z.js → requirementDiagram-LRYGKXZP-cQb7mwyH.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B6wzbZmB.js → sankeyDiagram-W5VNT64P-Cd-DhUdf.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BGt8d3QQ.js → sequenceDiagram-SI44F4Z6-AlKYRgIk.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-7nlhJKo7.js → sizeCapture-X5ZJPWSS-CUy_ET4e.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Cv2stqMA.js → stateDiagram-OKZ733FA-CTuVe6Sa.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BsWhs8O7.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-qDAo4Yc1.js → swimlanes-SLNWSIFB-ChFLJb-v.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CJcioYGH.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKbDNKTr.js → timeline-definition-Z64GVDOM-DZZeiAPr.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DI-9EHic.js → vennDiagram-T6HMQDX7-t4O91cZU.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BrzzRDpX.js → wardleyDiagram-T6FBY63Y-CV5qGHZu.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-BclH5hGh.js → xychartDiagram-ELKLHX3M-DyXbytUI.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +9 -1
- package/dist/worker/console/static-src/operator-chat/animation-frame-batcher.js +38 -0
- package/dist/worker/console/static-src/operator-chat/chat-link.js +20 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +186 -5
- package/dist/worker/console/static-src/operator-chat/chat-stream-continuity.js +101 -0
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +18 -3
- package/dist/worker/console/static-src/operator-chat/context-insights-store.js +64 -0
- package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +98 -0
- package/dist/worker/console/static-src/operator-chat/file-palette-nav.js +81 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +17 -3
- package/dist/worker/console/static-src/operator-chat/process-label.js +27 -5
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +16 -7
- package/dist/worker/console/static-src/operator-chat/transcript-mode.js +21 -0
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +4 -1
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +13 -5
- package/dist/worker/console/static-src/operator-chat/turn-state-projection.js +166 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +56 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +211 -40
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +124 -27
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +88 -20
- package/dist/worker/console/static-src/operator-chat/useComposer.js +80 -6
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +13 -3
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +36 -17
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +18 -0
- package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
- package/dist/worker/console/static-src/shell/console-update-reload.js +1 -1
- package/dist/worker/console/workspace-context.js +2 -0
- package/dist/worker/console/workspace-initialization.js +3 -3
- package/dist/worker/observability/read-model.js +23 -2
- package/dist/worker/observe/node-context-insights.js +479 -0
- package/dist/worker/observe/static/console-theme.css +174 -0
- package/dist/worker/observe/static/console-theme.js +36 -0
- package/dist/worker/observe/static/operator-chrome.css +85 -0
- package/dist/worker/observe/static/operator-chrome.js +51 -0
- package/dist/worker/scheduler/scheduled-goal-dispatch.js +3 -2
- package/dist/worker/scheduler/scheduled-goal-evidence.js +66 -3
- package/dist/worker/scheduler/scheduled-goal-store.js +10 -0
- package/dist/workflows/dag/budget-enforcement.js +31 -2
- package/dist/workflows/dag/checkpoint.js +686 -0
- package/dist/workflows/dag/frontend-closeout.js +3 -1
- package/dist/workflows/dag/frontend-contract-facts.js +11 -8
- package/dist/workflows/dag/frontend-design-policy.js +6 -6
- package/dist/workflows/dag/frontend-durable-tools.js +90 -70
- package/dist/workflows/dag/frontend-implementation-contract.js +146 -57
- package/dist/workflows/dag/frontend-plan-canary.js +66 -16
- package/dist/workflows/dag/frontend-plan-decision-contract.js +13 -21
- package/dist/workflows/dag/frontend-plan-progress.js +92 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +10 -5
- package/dist/workflows/dag/frontend-recovery-run.js +51 -46
- package/dist/workflows/dag/frontend-repair-assertions.js +36 -0
- package/dist/workflows/dag/frontend-review-context.js +29 -1
- package/dist/workflows/dag/frontend-review-scopes.js +92 -2
- package/dist/workflows/dag/frontend-risk.js +8 -3
- package/dist/workflows/dag/frontend-root-observation.js +261 -0
- package/dist/workflows/dag/frontend-session-budget.js +52 -39
- package/dist/workflows/dag/frontend-session-context.js +10 -0
- package/dist/workflows/dag/frontend-test-execution-evidence.js +206 -48
- package/dist/workflows/dag/frontend-typed-event-store.js +11 -1
- package/dist/workflows/dag/frontend-verification-trace.js +6 -0
- package/dist/workflows/dag/frontend-writer-admission.js +3 -1
- package/dist/workflows/dag/hybrid/sources.js +1812 -0
- package/dist/workflows/dag/hybrid/templates/backend-test.js +1807 -0
- package/dist/workflows/dag/hybrid/templates/frontend-test.js +702 -0
- package/dist/workflows/dag/hybrid/templates/frontend.js +1529 -0
- package/dist/workflows/dag/hybrid/templates/index.js +8 -0
- package/dist/workflows/dag/hybrid/templates/kg-bootstrap.js +449 -0
- package/dist/workflows/dag/hybrid/templates/knowledge-sync.js +495 -0
- package/dist/workflows/dag/hybrid/templates/shared.js +101 -0
- package/dist/workflows/dag/hybrid/templates/standard.js +265 -0
- package/dist/workflows/dag/hybrid/types.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +57 -7106
- package/dist/workflows/dag/node-execution.js +11 -11
- package/dist/workflows/dag/output-protocol.js +1 -0
- package/dist/workflows/dag/rerun-feedback.js +35 -4
- package/dist/workflows/dag/rerun-task.js +62 -66
- package/dist/workflows/dag/runner.js +38 -1159
- package/dist/workflows/dag/terminal-status.js +502 -0
- package/dist/workflows/dag/types.js +15 -8
- package/dist/workflows/loop/benchmark.js +11 -8
- package/docs/README.md +1 -1
- package/docs/architecture/dag-execution.md +1 -1
- package/docs/architecture/runtime-boundaries.md +1 -1
- package/docs/governance/harness-methodology-verification.md +11 -0
- package/docs/templates/backend-test-dag.json +5 -4
- package/docs/templates/frontend-implementation-contract.schema.json +31 -11
- package/package.json +6 -4
- package/skills/frontend-design-review/SKILL.md +8 -11
- package/skills/frontend-design-review/references/review-checklist.md +3 -4
- package/skills/frontend-review/SKILL.md +5 -1
- package/skills/loop-agent/references/command-reference.md +8 -0
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/dist/worker/console/static/assets/channel-ChE7y-cx.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +0 -1
- package/dist/worker/console/static/assets/index-BWkIfcrK.css +0 -1
- package/dist/worker/console/static/assets/index-Cdkvw_H6.js +0 -469
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +0 -8
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator Chat — context insights projection (dsh-context parity).
|
|
3
|
+
*
|
|
4
|
+
* Folds the durable Chat event ring into a context-insight dashboard view:
|
|
5
|
+
* - current composition (what the window is made of, provider-anchored)
|
|
6
|
+
* - per-request trend with step briefs, turn grouping, delta mode support
|
|
7
|
+
* - context browser elements (category → items with token estimates and
|
|
8
|
+
* bounded content previews, diffable between steps)
|
|
9
|
+
* - context lifecycle events (injections / compactions / prunes) with chips
|
|
10
|
+
* - per-file activity (reads/writes/searches, line deltas, op log)
|
|
11
|
+
* - usage totals (cache hit, cost when reported) and timing stats
|
|
12
|
+
*
|
|
13
|
+
* Estimates use the same conservative fixed-density heuristic as the
|
|
14
|
+
* compaction policy (conservativeTokenEstimate), so figures never disagree
|
|
15
|
+
* with the harness's own pressure decisions. The projection is derived
|
|
16
|
+
* EXCLUSIVELY from persisted events + bounded previews the server already
|
|
17
|
+
* stores — it invents no payload text and never re-reads the live Pi session,
|
|
18
|
+
* so it works for idle and restored sessions alike.
|
|
19
|
+
*/
|
|
20
|
+
import { conservativeTokenEstimate } from "../../../shared/pi-context-pressure/compaction-policy.js";
|
|
21
|
+
/** Fixed-density token estimate for preview text (chars → tokens). */
|
|
22
|
+
export function estimateTokensFromText(text) {
|
|
23
|
+
if (!text)
|
|
24
|
+
return 0;
|
|
25
|
+
return conservativeTokenEstimate(text);
|
|
26
|
+
}
|
|
27
|
+
/** Hard cap on a single element's preview content (chars). */
|
|
28
|
+
const ELEMENT_PREVIEW_MAX = 8_000;
|
|
29
|
+
/** Max elements retained per browser category. */
|
|
30
|
+
const ELEMENT_LIST_CAP = 200;
|
|
31
|
+
const COMPOSITION_CATEGORY_ORDER = [
|
|
32
|
+
"system",
|
|
33
|
+
"tools",
|
|
34
|
+
"user",
|
|
35
|
+
"injected",
|
|
36
|
+
"assistant",
|
|
37
|
+
"toolResults",
|
|
38
|
+
];
|
|
39
|
+
const BROWSER_CATEGORY_ORDER = [
|
|
40
|
+
"system",
|
|
41
|
+
"tools",
|
|
42
|
+
"messages",
|
|
43
|
+
"injected",
|
|
44
|
+
"toolResults",
|
|
45
|
+
"skills",
|
|
46
|
+
];
|
|
47
|
+
function emptyComposition() {
|
|
48
|
+
return {
|
|
49
|
+
totalTokens: 0,
|
|
50
|
+
contextWindow: null,
|
|
51
|
+
occupancy: null,
|
|
52
|
+
headroom: null,
|
|
53
|
+
slices: COMPOSITION_CATEGORY_ORDER.map((category) => ({
|
|
54
|
+
category,
|
|
55
|
+
tokens: 0,
|
|
56
|
+
items: 0,
|
|
57
|
+
})),
|
|
58
|
+
providerTotal: null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function sliceOf(composition, category) {
|
|
62
|
+
const slice = composition.slices.find((item) => item.category === category);
|
|
63
|
+
if (!slice) {
|
|
64
|
+
throw new Error(`composition slice missing: ${category}`);
|
|
65
|
+
}
|
|
66
|
+
return slice;
|
|
67
|
+
}
|
|
68
|
+
function addSlice(composition, category, tokens, items = 1) {
|
|
69
|
+
const slice = sliceOf(composition, category);
|
|
70
|
+
slice.tokens += tokens;
|
|
71
|
+
slice.items += items;
|
|
72
|
+
}
|
|
73
|
+
function eventTimeMs(event) {
|
|
74
|
+
const parsed = Date.parse(event.at);
|
|
75
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
76
|
+
}
|
|
77
|
+
function asRecord(value) {
|
|
78
|
+
return value && typeof value === "object"
|
|
79
|
+
? value
|
|
80
|
+
: undefined;
|
|
81
|
+
}
|
|
82
|
+
function boundedText(value, max = 4_000) {
|
|
83
|
+
if (typeof value !== "string" || !value)
|
|
84
|
+
return undefined;
|
|
85
|
+
const trimmed = value.trim();
|
|
86
|
+
if (!trimmed)
|
|
87
|
+
return undefined;
|
|
88
|
+
return trimmed.length > max ? `${trimmed.slice(0, max)}…` : trimmed;
|
|
89
|
+
}
|
|
90
|
+
function elementPreview(value) {
|
|
91
|
+
return boundedText(value, ELEMENT_PREVIEW_MAX);
|
|
92
|
+
}
|
|
93
|
+
/** First string field on a tool-args record among candidates. */
|
|
94
|
+
function argString(args, keys) {
|
|
95
|
+
for (const key of keys) {
|
|
96
|
+
const value = args[key];
|
|
97
|
+
if (typeof value === "string" && value.trim())
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
function classifyToolOperation(toolName) {
|
|
103
|
+
const normalized = toolName.toLowerCase().replace(/[-_]/g, "");
|
|
104
|
+
if (normalized === "read" || normalized === "ls")
|
|
105
|
+
return "read";
|
|
106
|
+
if (normalized === "write" || normalized === "multiedit")
|
|
107
|
+
return "write";
|
|
108
|
+
if (normalized === "edit" ||
|
|
109
|
+
normalized === "strreplaceeditor" ||
|
|
110
|
+
normalized === "strreplacebasededittool" ||
|
|
111
|
+
normalized === "applypatch") {
|
|
112
|
+
return "edit";
|
|
113
|
+
}
|
|
114
|
+
if (normalized === "grep" ||
|
|
115
|
+
normalized === "rg" ||
|
|
116
|
+
normalized === "find" ||
|
|
117
|
+
normalized === "fd" ||
|
|
118
|
+
normalized === "glob") {
|
|
119
|
+
return "search";
|
|
120
|
+
}
|
|
121
|
+
if (normalized === "bash" ||
|
|
122
|
+
normalized === "powershell" ||
|
|
123
|
+
normalized === "pwsh") {
|
|
124
|
+
return "bash";
|
|
125
|
+
}
|
|
126
|
+
return "other";
|
|
127
|
+
}
|
|
128
|
+
const READ_PATH_KEYS = ["file_path", "path", "filePath", "filename", "name"];
|
|
129
|
+
const SEARCH_PATH_KEYS = ["path", "dir", "directory", "include", "glob"];
|
|
130
|
+
/** Extract touched file paths from a tool call's arguments preview. */
|
|
131
|
+
export function filePathsFromToolArgs(toolName, args) {
|
|
132
|
+
const kind = classifyToolOperation(toolName);
|
|
133
|
+
const paths = [];
|
|
134
|
+
if (kind === "search") {
|
|
135
|
+
const query = argString(args, SEARCH_PATH_KEYS);
|
|
136
|
+
if (query)
|
|
137
|
+
paths.push(query);
|
|
138
|
+
return paths;
|
|
139
|
+
}
|
|
140
|
+
if (kind === "bash")
|
|
141
|
+
return paths;
|
|
142
|
+
const target = argString(args, READ_PATH_KEYS);
|
|
143
|
+
if (target)
|
|
144
|
+
paths.push(target);
|
|
145
|
+
return paths;
|
|
146
|
+
}
|
|
147
|
+
function lineDelta(oldText, newText) {
|
|
148
|
+
if (!newText)
|
|
149
|
+
return { added: 0, removed: 0 };
|
|
150
|
+
const newLines = newText.split("\n").length;
|
|
151
|
+
if (!oldText)
|
|
152
|
+
return { added: newLines, removed: 0 };
|
|
153
|
+
const oldLines = oldText.split("\n").length;
|
|
154
|
+
return {
|
|
155
|
+
added: Math.max(0, newLines - oldLines),
|
|
156
|
+
removed: Math.max(0, oldLines - newLines),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/** Parse a JSON args preview back into a record (best-effort, bounded). */
|
|
160
|
+
function parseArgsPreview(preview) {
|
|
161
|
+
if (!preview)
|
|
162
|
+
return {};
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(preview);
|
|
165
|
+
const record = asRecord(parsed);
|
|
166
|
+
return record ?? {};
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return {};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function deltaFor(kind, args) {
|
|
173
|
+
if (kind !== "write" && kind !== "edit")
|
|
174
|
+
return undefined;
|
|
175
|
+
const delta = lineDelta(argString(args, ["oldText", "old_string", "oldStr"]), argString(args, ["content", "newText", "new_string", "newStr"]));
|
|
176
|
+
return {
|
|
177
|
+
...(delta.added > 0 ? { linesAdded: delta.added } : {}),
|
|
178
|
+
...(delta.removed > 0 ? { linesRemoved: delta.removed } : {}),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function num(value) {
|
|
182
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
function toolSourceChip(toolName) {
|
|
188
|
+
const normalized = toolName.toLowerCase();
|
|
189
|
+
if (normalized.startsWith("task") ||
|
|
190
|
+
normalized.startsWith("dag") ||
|
|
191
|
+
normalized.startsWith("worker") ||
|
|
192
|
+
normalized.startsWith("contract") ||
|
|
193
|
+
normalized.startsWith("observe") ||
|
|
194
|
+
normalized.startsWith("prepare") ||
|
|
195
|
+
normalized.startsWith("confirm")) {
|
|
196
|
+
return "operator";
|
|
197
|
+
}
|
|
198
|
+
if (normalized.startsWith("sql_"))
|
|
199
|
+
return "builtin:sql";
|
|
200
|
+
if (normalized.startsWith("mcp"))
|
|
201
|
+
return "mcp";
|
|
202
|
+
if (normalized.startsWith("agent") || normalized === "explore") {
|
|
203
|
+
return "builtin:subagent";
|
|
204
|
+
}
|
|
205
|
+
if (["read", "write", "edit", "bash", "grep", "find", "ls"].includes(normalized)) {
|
|
206
|
+
return "pi";
|
|
207
|
+
}
|
|
208
|
+
return "extension";
|
|
209
|
+
}
|
|
210
|
+
function contentBlocksThinking(blocks) {
|
|
211
|
+
if (!Array.isArray(blocks))
|
|
212
|
+
return false;
|
|
213
|
+
return blocks.some((block) => asRecord(block)?.type === "thinking" ||
|
|
214
|
+
asRecord(block)?.type === "reasoning");
|
|
215
|
+
}
|
|
216
|
+
function capElements(elements) {
|
|
217
|
+
return elements.slice(-ELEMENT_LIST_CAP);
|
|
218
|
+
}
|
|
219
|
+
/** Fold the event ring into the context insights view. */
|
|
220
|
+
export function deriveContextInsights(events, options = {}) {
|
|
221
|
+
const ordered = [...events].sort((a, b) => a.seq - b.seq);
|
|
222
|
+
const composition = emptyComposition();
|
|
223
|
+
const trend = [];
|
|
224
|
+
const contextEvents = [];
|
|
225
|
+
const files = new Map();
|
|
226
|
+
const usage = {
|
|
227
|
+
inputTokens: 0,
|
|
228
|
+
outputTokens: 0,
|
|
229
|
+
cacheReadTokens: 0,
|
|
230
|
+
cacheWriteTokens: 0,
|
|
231
|
+
totalTokens: 0,
|
|
232
|
+
cost: null,
|
|
233
|
+
cacheHitPercent: null,
|
|
234
|
+
turns: 0,
|
|
235
|
+
steps: 0,
|
|
236
|
+
humanInputs: 0,
|
|
237
|
+
};
|
|
238
|
+
const timing = {
|
|
239
|
+
llmMs: 0,
|
|
240
|
+
toolMs: 0,
|
|
241
|
+
busyMs: 0,
|
|
242
|
+
wallMs: null,
|
|
243
|
+
overheadMs: null,
|
|
244
|
+
ttftMs: null,
|
|
245
|
+
decodeTokensPerSecond: null,
|
|
246
|
+
steps: 0,
|
|
247
|
+
};
|
|
248
|
+
const turnIds = [];
|
|
249
|
+
const turnStartMs = new Map();
|
|
250
|
+
const turnUserBrief = new Map();
|
|
251
|
+
const stepToolsCalled = new Map();
|
|
252
|
+
const toolStarts = new Map();
|
|
253
|
+
const ttftSamples = [];
|
|
254
|
+
const decodeSamples = [];
|
|
255
|
+
let contextWindow = null;
|
|
256
|
+
let providerTotal = null;
|
|
257
|
+
let lastSeq = 0;
|
|
258
|
+
let firstAt = null;
|
|
259
|
+
let lastAt = null;
|
|
260
|
+
// --- Live browser fold (the "next request" makeup) ---
|
|
261
|
+
const browserMessages = [];
|
|
262
|
+
const browserInjected = [];
|
|
263
|
+
const browserToolResults = [];
|
|
264
|
+
if (options.systemPromptPreview) {
|
|
265
|
+
addSlice(composition, "system", estimateTokensFromText(options.systemPromptPreview));
|
|
266
|
+
}
|
|
267
|
+
else if (typeof options.systemPromptChars === "number" &&
|
|
268
|
+
options.systemPromptChars > 0) {
|
|
269
|
+
addSlice(composition, "system", conservativeTokenEstimate("x".repeat(Math.min(options.systemPromptChars, 2_000_000))));
|
|
270
|
+
}
|
|
271
|
+
const browserTools = (options.tools ?? []).map((tool, index) => {
|
|
272
|
+
const tokens = estimateTokensFromText(`${tool.id}${tool.description ? `: ${tool.description}` : ""}`);
|
|
273
|
+
return {
|
|
274
|
+
id: `tool:${tool.id}:${index}`,
|
|
275
|
+
label: tool.id,
|
|
276
|
+
tokens,
|
|
277
|
+
preview: elementPreview(tool.description),
|
|
278
|
+
toolSource: toolSourceChip(tool.id),
|
|
279
|
+
seq: 0,
|
|
280
|
+
at: "",
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
for (const tool of browserTools) {
|
|
284
|
+
addSlice(composition, "tools", tool.tokens);
|
|
285
|
+
}
|
|
286
|
+
const browserSkills = (options.skillInjections ?? []).map((skill, index) => ({
|
|
287
|
+
id: `skill:${skill.name}:${index}`,
|
|
288
|
+
label: skill.name,
|
|
289
|
+
tokens: skill.tokens,
|
|
290
|
+
seq: 0,
|
|
291
|
+
at: "",
|
|
292
|
+
}));
|
|
293
|
+
for (const event of ordered) {
|
|
294
|
+
lastSeq = event.seq;
|
|
295
|
+
const atMs = eventTimeMs(event);
|
|
296
|
+
if (atMs > 0) {
|
|
297
|
+
if (firstAt === null)
|
|
298
|
+
firstAt = atMs;
|
|
299
|
+
lastAt = atMs;
|
|
300
|
+
}
|
|
301
|
+
const data = asRecord(event.data) ?? {};
|
|
302
|
+
switch (event.kind) {
|
|
303
|
+
case "user_message": {
|
|
304
|
+
const message = asRecord(data.message);
|
|
305
|
+
const text = boundedText(message?.text, ELEMENT_PREVIEW_MAX);
|
|
306
|
+
if (text) {
|
|
307
|
+
const tokens = estimateTokensFromText(text);
|
|
308
|
+
addSlice(composition, "user", tokens);
|
|
309
|
+
browserMessages.push({
|
|
310
|
+
id: `user:${event.seq}`,
|
|
311
|
+
label: "用户",
|
|
312
|
+
tokens,
|
|
313
|
+
preview: text,
|
|
314
|
+
seq: event.seq,
|
|
315
|
+
at: event.at,
|
|
316
|
+
});
|
|
317
|
+
if (!turnUserBrief.has(event.turnId)) {
|
|
318
|
+
turnUserBrief.set(event.turnId, text.slice(0, 200));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
usage.humanInputs += 1;
|
|
322
|
+
if (!turnIds.includes(event.turnId))
|
|
323
|
+
turnIds.push(event.turnId);
|
|
324
|
+
if (!turnStartMs.has(event.turnId) && atMs > 0) {
|
|
325
|
+
turnStartMs.set(event.turnId, atMs);
|
|
326
|
+
}
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
case "message_update":
|
|
330
|
+
case "message_end": {
|
|
331
|
+
const text = boundedText(data.text, ELEMENT_PREVIEW_MAX);
|
|
332
|
+
const thinking = contentBlocksThinking(data.content);
|
|
333
|
+
if (event.kind === "message_end") {
|
|
334
|
+
usage.steps += 1;
|
|
335
|
+
timing.steps += 1;
|
|
336
|
+
if (!turnIds.includes(event.turnId))
|
|
337
|
+
turnIds.push(event.turnId);
|
|
338
|
+
if (text) {
|
|
339
|
+
const tokens = estimateTokensFromText(text);
|
|
340
|
+
addSlice(composition, "assistant", tokens);
|
|
341
|
+
browserMessages.push({
|
|
342
|
+
id: `assistant:${event.seq}`,
|
|
343
|
+
label: thinking ? "助手(含思考)" : "助手",
|
|
344
|
+
tokens,
|
|
345
|
+
preview: text,
|
|
346
|
+
seq: event.seq,
|
|
347
|
+
at: event.at,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
const toolsCalled = stepToolsCalled.get(event.turnId) ?? [];
|
|
351
|
+
const recorded = asRecord(data.timing);
|
|
352
|
+
const llmMs = num(recorded?.llmMs) ?? null;
|
|
353
|
+
if (llmMs !== null)
|
|
354
|
+
timing.llmMs += llmMs;
|
|
355
|
+
const ttft = num(recorded?.ttftMs);
|
|
356
|
+
if (ttft !== undefined)
|
|
357
|
+
ttftSamples.push(ttft);
|
|
358
|
+
const decodeMs = num(recorded?.decodeMs);
|
|
359
|
+
const outputTokens = num(recorded?.outputTokens);
|
|
360
|
+
if (decodeMs !== undefined &&
|
|
361
|
+
decodeMs > 0 &&
|
|
362
|
+
outputTokens !== undefined) {
|
|
363
|
+
decodeSamples.push({ ms: decodeMs, tokens: outputTokens });
|
|
364
|
+
}
|
|
365
|
+
const start = turnStartMs.get(event.turnId);
|
|
366
|
+
const turnElapsedMs = start !== undefined && atMs >= start ? atMs - start : null;
|
|
367
|
+
const stepComposition = emptyComposition();
|
|
368
|
+
for (const slice of composition.slices) {
|
|
369
|
+
const target = sliceOf(stepComposition, slice.category);
|
|
370
|
+
target.tokens = slice.tokens;
|
|
371
|
+
target.items = slice.items;
|
|
372
|
+
}
|
|
373
|
+
stepComposition.totalTokens = composition.slices.reduce((sum, slice) => sum + slice.tokens, 0);
|
|
374
|
+
trend.push({
|
|
375
|
+
index: trend.length + 1,
|
|
376
|
+
turnId: event.turnId,
|
|
377
|
+
turnIndex: turnIds.indexOf(event.turnId) + 1,
|
|
378
|
+
at: event.at,
|
|
379
|
+
seq: event.seq,
|
|
380
|
+
composition: stepComposition.slices,
|
|
381
|
+
totalTokens: stepComposition.totalTokens,
|
|
382
|
+
actualPromptTokens: null,
|
|
383
|
+
actualOutputTokens: null,
|
|
384
|
+
actualCacheReadTokens: null,
|
|
385
|
+
userBrief: turnUserBrief.get(event.turnId),
|
|
386
|
+
responseBrief: text ? text.slice(0, 200) : undefined,
|
|
387
|
+
toolsCalled,
|
|
388
|
+
thinking,
|
|
389
|
+
turnElapsedMs,
|
|
390
|
+
llmMs,
|
|
391
|
+
});
|
|
392
|
+
stepToolsCalled.set(event.turnId, []);
|
|
393
|
+
}
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
case "tool_call": {
|
|
397
|
+
const toolName = typeof data.toolName === "string" ? data.toolName : "";
|
|
398
|
+
const argsPreview = typeof data.argsPreview === "string" ? data.argsPreview : undefined;
|
|
399
|
+
const args = parseArgsPreview(argsPreview);
|
|
400
|
+
const atTime = eventTimeMs(event);
|
|
401
|
+
if (toolName) {
|
|
402
|
+
const list = stepToolsCalled.get(event.turnId) ?? [];
|
|
403
|
+
list.push(toolName);
|
|
404
|
+
stepToolsCalled.set(event.turnId, list);
|
|
405
|
+
}
|
|
406
|
+
if (argsPreview) {
|
|
407
|
+
const tokens = estimateTokensFromText(argsPreview);
|
|
408
|
+
addSlice(composition, "injected", tokens);
|
|
409
|
+
browserInjected.push({
|
|
410
|
+
id: `args:${event.seq}`,
|
|
411
|
+
label: toolName || "工具调用",
|
|
412
|
+
tokens,
|
|
413
|
+
preview: argsPreview,
|
|
414
|
+
toolSource: toolName ? toolSourceChip(toolName) : undefined,
|
|
415
|
+
seq: event.seq,
|
|
416
|
+
at: event.at,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
if (toolName && atTime > 0) {
|
|
420
|
+
const toolCallId = typeof data.toolCallId === "string" ? data.toolCallId : "";
|
|
421
|
+
toolStarts.set(`${event.turnId}\u0000${toolCallId}`, atTime);
|
|
422
|
+
}
|
|
423
|
+
const kind = classifyToolOperation(toolName);
|
|
424
|
+
if (kind !== "other") {
|
|
425
|
+
const paths = filePathsFromToolArgs(toolName, args);
|
|
426
|
+
for (const path of paths) {
|
|
427
|
+
const entry = files.get(path) ??
|
|
428
|
+
{
|
|
429
|
+
path,
|
|
430
|
+
reads: 0,
|
|
431
|
+
writes: 0,
|
|
432
|
+
searches: 0,
|
|
433
|
+
otherOps: 0,
|
|
434
|
+
linesAdded: 0,
|
|
435
|
+
linesRemoved: 0,
|
|
436
|
+
operations: [],
|
|
437
|
+
};
|
|
438
|
+
if (kind === "read")
|
|
439
|
+
entry.reads += 1;
|
|
440
|
+
else if (kind === "write" || kind === "edit")
|
|
441
|
+
entry.writes += 1;
|
|
442
|
+
else if (kind === "search")
|
|
443
|
+
entry.searches += 1;
|
|
444
|
+
else
|
|
445
|
+
entry.otherOps += 1;
|
|
446
|
+
const opDelta = deltaFor(kind, args);
|
|
447
|
+
if (opDelta) {
|
|
448
|
+
entry.linesAdded += opDelta.linesAdded ?? 0;
|
|
449
|
+
entry.linesRemoved += opDelta.linesRemoved ?? 0;
|
|
450
|
+
}
|
|
451
|
+
entry.operations.push({
|
|
452
|
+
seq: event.seq,
|
|
453
|
+
at: event.at,
|
|
454
|
+
toolName: toolName || "unknown",
|
|
455
|
+
kind,
|
|
456
|
+
...opDelta,
|
|
457
|
+
});
|
|
458
|
+
files.set(path, entry);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
case "tool_result": {
|
|
464
|
+
const resultPreview = typeof data.resultPreview === "string"
|
|
465
|
+
? data.resultPreview
|
|
466
|
+
: undefined;
|
|
467
|
+
const toolCallId = typeof data.toolCallId === "string" ? data.toolCallId : "";
|
|
468
|
+
const key = `${event.turnId}\u0000${toolCallId}`;
|
|
469
|
+
const startedAt = toolStarts.get(key);
|
|
470
|
+
if (startedAt !== undefined && atMs >= startedAt) {
|
|
471
|
+
timing.toolMs += atMs - startedAt;
|
|
472
|
+
}
|
|
473
|
+
toolStarts.delete(key);
|
|
474
|
+
if (resultPreview) {
|
|
475
|
+
const tokens = estimateTokensFromText(resultPreview);
|
|
476
|
+
addSlice(composition, "toolResults", tokens);
|
|
477
|
+
browserToolResults.push({
|
|
478
|
+
id: `result:${event.seq}`,
|
|
479
|
+
label: typeof data.toolName === "string" && data.toolName
|
|
480
|
+
? data.toolName
|
|
481
|
+
: "工具结果",
|
|
482
|
+
tokens,
|
|
483
|
+
preview: elementPreview(resultPreview),
|
|
484
|
+
status: data.isError === true ? "error" : "ok",
|
|
485
|
+
seq: event.seq,
|
|
486
|
+
at: event.at,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
case "usage": {
|
|
492
|
+
const input = num(data.inputTokens);
|
|
493
|
+
const output = num(data.outputTokens);
|
|
494
|
+
const cacheRead = num(data.cacheReadTokens);
|
|
495
|
+
const cacheWrite = num(data.cacheWriteTokens);
|
|
496
|
+
if (input !== undefined)
|
|
497
|
+
usage.inputTokens += input;
|
|
498
|
+
if (output !== undefined)
|
|
499
|
+
usage.outputTokens += output;
|
|
500
|
+
if (cacheRead !== undefined)
|
|
501
|
+
usage.cacheReadTokens += cacheRead;
|
|
502
|
+
if (cacheWrite !== undefined)
|
|
503
|
+
usage.cacheWriteTokens += cacheWrite;
|
|
504
|
+
const total = num(data.totalTokens);
|
|
505
|
+
if (total !== undefined)
|
|
506
|
+
usage.totalTokens += total;
|
|
507
|
+
const cost = num(data.cost);
|
|
508
|
+
if (cost !== undefined) {
|
|
509
|
+
usage.cost = (usage.cost ?? 0) + cost;
|
|
510
|
+
}
|
|
511
|
+
const contextUsage = asRecord(data.contextUsage);
|
|
512
|
+
const window = num(contextUsage?.contextWindow);
|
|
513
|
+
const tokens = num(contextUsage?.tokens);
|
|
514
|
+
if (window !== undefined && window > 0)
|
|
515
|
+
contextWindow = window;
|
|
516
|
+
if (tokens !== undefined)
|
|
517
|
+
providerTotal = tokens;
|
|
518
|
+
const lastStep = trend.at(-1);
|
|
519
|
+
if (lastStep && input !== undefined) {
|
|
520
|
+
lastStep.actualPromptTokens =
|
|
521
|
+
input + (cacheRead ?? 0) + (cacheWrite ?? 0);
|
|
522
|
+
lastStep.actualOutputTokens = output ?? null;
|
|
523
|
+
lastStep.actualCacheReadTokens = cacheRead ?? null;
|
|
524
|
+
}
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
case "compaction_start":
|
|
528
|
+
case "compaction_end":
|
|
529
|
+
case "compact": {
|
|
530
|
+
const before = num(data.tokensBefore);
|
|
531
|
+
const after = num(data.estimatedTokensAfter);
|
|
532
|
+
const reclaimed = before !== undefined && after !== undefined ? before - after : null;
|
|
533
|
+
const pruned = data.pruned === true || data.kind === "prune";
|
|
534
|
+
contextEvents.push({
|
|
535
|
+
seq: event.seq,
|
|
536
|
+
at: event.at,
|
|
537
|
+
turnId: event.turnId,
|
|
538
|
+
turnIndex: turnIds.indexOf(event.turnId) + 1,
|
|
539
|
+
kind: pruned ? "prune" :
|
|
540
|
+
event.kind === "compaction_start"
|
|
541
|
+
? "compact-start"
|
|
542
|
+
: event.kind === "compaction_end"
|
|
543
|
+
? "compact-end"
|
|
544
|
+
: "compact",
|
|
545
|
+
tokenDelta: reclaimed,
|
|
546
|
+
...(before !== undefined ? { tokensBefore: before } : {}),
|
|
547
|
+
...(after !== undefined ? { tokensAfter: after } : {}),
|
|
548
|
+
...(Array.isArray(data.shadowedSeqs) ? { shadowedItems: data.shadowedSeqs.length } : {}),
|
|
549
|
+
...(typeof data.reason === "string" ? { reason: data.reason } : {}),
|
|
550
|
+
...(pruned ? { pruned: true } : {}),
|
|
551
|
+
detail: boundedText(asRecord(data.details)?.text, 300),
|
|
552
|
+
});
|
|
553
|
+
const lastStep = trend.at(-1);
|
|
554
|
+
if (lastStep && event.kind !== "compaction_start") {
|
|
555
|
+
lastStep.eventMark = pruned ? "prune" : "compact";
|
|
556
|
+
}
|
|
557
|
+
if (reclaimed !== null && reclaimed > 0) {
|
|
558
|
+
const userSlice = sliceOf(composition, "user");
|
|
559
|
+
const assistantSlice = sliceOf(composition, "assistant");
|
|
560
|
+
const toolSlice = sliceOf(composition, "toolResults");
|
|
561
|
+
let remaining = reclaimed;
|
|
562
|
+
for (const slice of [userSlice, assistantSlice, toolSlice]) {
|
|
563
|
+
const take = Math.min(slice.tokens, remaining);
|
|
564
|
+
slice.tokens -= take;
|
|
565
|
+
remaining -= take;
|
|
566
|
+
if (remaining <= 0)
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
default:
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
composition.totalTokens = composition.slices.reduce((sum, slice) => sum + slice.tokens, 0);
|
|
577
|
+
composition.contextWindow = contextWindow;
|
|
578
|
+
composition.providerTotal = providerTotal;
|
|
579
|
+
if (providerTotal !== null &&
|
|
580
|
+
providerTotal > 0 &&
|
|
581
|
+
composition.totalTokens > 0) {
|
|
582
|
+
const scale = providerTotal / composition.totalTokens;
|
|
583
|
+
for (const slice of composition.slices) {
|
|
584
|
+
slice.tokens = Math.round(slice.tokens * scale);
|
|
585
|
+
}
|
|
586
|
+
composition.totalTokens = providerTotal;
|
|
587
|
+
}
|
|
588
|
+
if (contextWindow !== null && contextWindow > 0) {
|
|
589
|
+
composition.occupancy =
|
|
590
|
+
Math.round((composition.totalTokens / contextWindow) * 1000) / 10;
|
|
591
|
+
composition.headroom = Math.max(0, contextWindow - composition.totalTokens);
|
|
592
|
+
}
|
|
593
|
+
const billed = usage.inputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
|
|
594
|
+
usage.cacheHitPercent =
|
|
595
|
+
billed > 0
|
|
596
|
+
? Math.round((usage.cacheReadTokens / billed) * 1000) / 10
|
|
597
|
+
: null;
|
|
598
|
+
usage.turns = turnIds.length;
|
|
599
|
+
if (usage.totalTokens === 0 && billed + usage.outputTokens > 0) {
|
|
600
|
+
usage.totalTokens = billed + usage.outputTokens;
|
|
601
|
+
}
|
|
602
|
+
timing.busyMs = timing.llmMs + timing.toolMs;
|
|
603
|
+
if (firstAt !== null && lastAt !== null && lastAt >= firstAt) {
|
|
604
|
+
timing.wallMs = lastAt - firstAt;
|
|
605
|
+
timing.overheadMs = Math.max(0, timing.wallMs - timing.busyMs);
|
|
606
|
+
}
|
|
607
|
+
if (ttftSamples.length > 0) {
|
|
608
|
+
timing.ttftMs = Math.round(ttftSamples.reduce((sum, v) => sum + v, 0) / ttftSamples.length);
|
|
609
|
+
}
|
|
610
|
+
if (decodeSamples.length > 0) {
|
|
611
|
+
const ms = decodeSamples.reduce((sum, s) => sum + s.ms, 0);
|
|
612
|
+
const tokens = decodeSamples.reduce((sum, s) => sum + s.tokens, 0);
|
|
613
|
+
if (ms > 0 && tokens > 0) {
|
|
614
|
+
timing.decodeTokensPerSecond = Math.round((tokens / ms) * 1000);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
// --- Browser snapshots: live + one per trend step (rolling elements) ---
|
|
618
|
+
const browserSteps = [];
|
|
619
|
+
const liveMessages = capElements(browserMessages);
|
|
620
|
+
const liveInjected = capElements(browserInjected);
|
|
621
|
+
const liveToolResults = capElements(browserToolResults);
|
|
622
|
+
const liveCategories = [];
|
|
623
|
+
const pushCategory = (category, elements) => {
|
|
624
|
+
if (elements.length === 0)
|
|
625
|
+
return;
|
|
626
|
+
liveCategories.push({
|
|
627
|
+
category,
|
|
628
|
+
elements,
|
|
629
|
+
totalTokens: elements.reduce((sum, e) => sum + e.tokens, 0),
|
|
630
|
+
});
|
|
631
|
+
};
|
|
632
|
+
pushCategory("system", options.systemPromptPreview
|
|
633
|
+
? [{
|
|
634
|
+
id: "system:prompt",
|
|
635
|
+
label: "System prompt",
|
|
636
|
+
tokens: estimateTokensFromText(options.systemPromptPreview),
|
|
637
|
+
preview: elementPreview(options.systemPromptPreview),
|
|
638
|
+
seq: 0,
|
|
639
|
+
at: "",
|
|
640
|
+
}]
|
|
641
|
+
: []);
|
|
642
|
+
pushCategory("tools", browserTools);
|
|
643
|
+
pushCategory("messages", liveMessages);
|
|
644
|
+
pushCategory("injected", liveInjected);
|
|
645
|
+
pushCategory("toolResults", liveToolResults);
|
|
646
|
+
pushCategory("skills", browserSkills);
|
|
647
|
+
browserSteps.push({
|
|
648
|
+
stepId: "live",
|
|
649
|
+
categories: liveCategories,
|
|
650
|
+
totalTokens: liveCategories.reduce((sum, c) => sum + c.totalTokens, 0),
|
|
651
|
+
});
|
|
652
|
+
// Retained steps: rebuild each step's makeup from cumulative snapshots at
|
|
653
|
+
// that point in the ring (bounded to the most recent steps).
|
|
654
|
+
const RETAINED_BROWSER_STEPS = 30;
|
|
655
|
+
const recentTrend = trend.slice(-RETAINED_BROWSER_STEPS);
|
|
656
|
+
for (let i = 0; i < recentTrend.length; i++) {
|
|
657
|
+
const step = recentTrend[i];
|
|
658
|
+
const cutoffSeq = step.seq;
|
|
659
|
+
const upto = (list) => capElements(list.filter((e) => e.seq <= cutoffSeq));
|
|
660
|
+
const messagesAt = upto(browserMessages);
|
|
661
|
+
const injectedAt = upto(browserInjected);
|
|
662
|
+
const resultsAt = upto(browserToolResults);
|
|
663
|
+
const categories = [];
|
|
664
|
+
const push = (category, elements) => {
|
|
665
|
+
if (elements.length === 0)
|
|
666
|
+
return;
|
|
667
|
+
categories.push({
|
|
668
|
+
category,
|
|
669
|
+
elements,
|
|
670
|
+
totalTokens: elements.reduce((sum, e) => sum + e.tokens, 0),
|
|
671
|
+
});
|
|
672
|
+
};
|
|
673
|
+
push("system", options.systemPromptPreview
|
|
674
|
+
? [{
|
|
675
|
+
id: "system:prompt",
|
|
676
|
+
label: "System prompt",
|
|
677
|
+
tokens: estimateTokensFromText(options.systemPromptPreview),
|
|
678
|
+
preview: elementPreview(options.systemPromptPreview),
|
|
679
|
+
seq: 0,
|
|
680
|
+
at: "",
|
|
681
|
+
}]
|
|
682
|
+
: []);
|
|
683
|
+
push("tools", browserTools);
|
|
684
|
+
push("messages", messagesAt);
|
|
685
|
+
push("injected", injectedAt);
|
|
686
|
+
push("toolResults", resultsAt);
|
|
687
|
+
push("skills", browserSkills);
|
|
688
|
+
browserSteps.push({
|
|
689
|
+
stepId: String(step.index),
|
|
690
|
+
turnId: step.turnId,
|
|
691
|
+
at: step.at,
|
|
692
|
+
categories,
|
|
693
|
+
totalTokens: categories.reduce((sum, c) => sum + c.totalTokens, 0),
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
return {
|
|
697
|
+
schemaVersion: 1,
|
|
698
|
+
...(options.sessionId ? { sessionId: options.sessionId } : {}),
|
|
699
|
+
generatedAt: new Date().toISOString(),
|
|
700
|
+
lastEventSeq: lastSeq,
|
|
701
|
+
composition,
|
|
702
|
+
trend,
|
|
703
|
+
events: contextEvents,
|
|
704
|
+
files: [...files.values()].sort((a, b) => {
|
|
705
|
+
const opsA = a.reads + a.writes + a.searches + a.otherOps;
|
|
706
|
+
const opsB = b.reads + b.writes + b.searches + b.otherOps;
|
|
707
|
+
if (opsB !== opsA)
|
|
708
|
+
return opsB - opsA;
|
|
709
|
+
return a.path.localeCompare(b.path);
|
|
710
|
+
}),
|
|
711
|
+
browser: browserSteps,
|
|
712
|
+
usage,
|
|
713
|
+
timing,
|
|
714
|
+
systemPromptChars: options.systemPromptChars ?? null,
|
|
715
|
+
systemPromptPreview: options.systemPromptPreview ?? null,
|
|
716
|
+
tools: browserTools.map((t) => ({
|
|
717
|
+
id: t.label,
|
|
718
|
+
description: "",
|
|
719
|
+
tokens: t.tokens,
|
|
720
|
+
})),
|
|
721
|
+
skillInjections: options.skillInjections ?? [],
|
|
722
|
+
};
|
|
723
|
+
}
|