@tea-agent/loop-agent 0.41.1-beta.0 → 0.41.1
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 +47 -267
- package/dist/application/dag/generate-task-dag.js +19 -75
- package/dist/application/task-lifecycle/advance.js +5 -24
- package/dist/application/task-lifecycle/observe.js +17 -171
- package/dist/application/task-lifecycle/plan-transitions.js +7 -42
- package/dist/application/task-lifecycle/recommendations.js +2 -13
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +0 -7
- package/dist/cli/program.js +0 -5
- package/dist/commands/client-recovery.js +0 -3
- package/dist/commands/init.js +1 -27
- package/dist/commands/task-advance.js +0 -19
- package/dist/executors/dag-pi-executor.js +231 -3784
- package/dist/executors/pi-executor.js +4 -15
- package/dist/executors/shell-executor.js +217 -945
- package/dist/executors/shell-write-guard.js +0 -7
- package/dist/shared/dag-failure-category.js +0 -12
- package/dist/shared/openspec-spec.js +4 -70
- package/dist/shared/operator/capabilities.js +0 -7
- package/dist/task/config-types.js +7 -34
- package/dist/task/contract/adopt.js +0 -4
- package/dist/task/contract/import-revision.js +0 -4
- package/dist/task/contract/project.js +0 -3
- package/dist/task/contract/schema.js +1 -2
- package/dist/task/frontend-project-capability.js +20 -203
- package/dist/task/runtime.js +2 -5
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +9 -15
- package/dist/task/source-prepare/prepare.js +1 -89
- package/dist/task/source-prepare/semantic-intake.js +2 -6
- package/dist/task/source-references.js +1 -22
- package/dist/worker/console/chat/chat-ui-policy.js +4 -2
- package/dist/worker/console/chat/pi-runtime.js +176 -9
- package/dist/worker/console/chat/repo-browser.js +10 -3
- package/dist/worker/console/chat/routes.js +42 -22
- package/dist/worker/console/chat/semantic-activity.js +12 -11
- package/dist/worker/console/chat/session-stats.js +96 -0
- package/dist/worker/console/chat/session-store.js +117 -70
- package/dist/worker/console/chat/shortcuts.js +1 -1
- package/dist/worker/console/chat/turn-process.js +139 -60
- package/dist/worker/console/index.js +0 -3
- package/dist/worker/console/operation-store.js +6 -226
- package/dist/worker/console/operator-actions.js +7 -169
- package/dist/worker/console/pi-plugins.js +3 -1
- package/dist/worker/console/server.js +0 -3
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bho4we4c.js → abnfDiagram-N423BO3Z-D7oU983t.js} +1 -1
- package/dist/worker/console/static/assets/{arc-TBDkTeK0.js → arc-BrKSRdFS.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BF_fZrn2.js → architectureDiagram-T3A2C74G-CiXV7L56.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CCnPgUe2.js → blockDiagram-VBNYF7ZC-D_Hs_gIG.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BCYZJLAT.js → c4Diagram-5PPSVZJV-DbM-Lqqh.js} +1 -1
- package/dist/worker/console/static/assets/channel-D1xajGpe.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D8K6adhU.js → chunk-2GRJ4B5K-D1M8uqJw.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DkT1Q78b.js → chunk-2Q5K7J3B-4D3RFHT6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DuhvpIr7.js → chunk-5RXB4S5H-CdwpY_n9.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BnK6dC49.js → chunk-5VM5RSS4-BLeLZ9PG.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DPz__z7r.js → chunk-6Q2QTUOP-CeRNEMvc.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-0kkppM2j.js → chunk-GF5L2VYU-CjkHP8Vy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BB41iK5t.js → chunk-JWPE2WC7-B54ZUQY-.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CiVrxKHU.js → chunk-KBJHAD2P-4bKaMVmx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-C2h9Xje1.js → chunk-RYQCIY6F-Cyjfznu_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-3iOSHdbM.js → chunk-XXDRQBXY-CDN-zGFV.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BmuGTfRr.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BmuGTfRr.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BN3A7tkk.js → cose-bilkent-JH36ORCC-HxJmz411.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CTuBy0Or.js → cynefin-VYW2F7L2-DtSt5K2w.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-wb6tUlMN.js → cynefinDiagram-MW4NZA55-VtkSpRmG.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-GP1Ivx8B.js → dagre-VZM6K2ZE-Bj4DT4du.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-ejrcK6L0.js → diagram-7IWD3JNH-Bpuk6BmP.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-YFkCCvUU.js → diagram-B4RE2ZJO-BHYGv_yO.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-B4rrvIaL.js → diagram-LBJQPF4R-DEx3WwnT.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-C-YIZcUf.js → diagram-Q27KOJAE-Cj0dS5e8.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPF5wcL1.js → diagram-UB23O5K3-B97h8l21.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CU5SzXjO.js → ebnfDiagram-BXEA7PRR-DMSFAhBd.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CpL414DE.js → erDiagram-JOGREHBK-DhVOCbt6.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-MrhLzNau.js → flowDiagram-UKHOOZJN-GlakITWL.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Bg4qFuUo.js → ganttDiagram-PKOTCBZU-VxPpu9eQ.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-ColxSXLR.js → gitGraphDiagram-DS77QQ5N-Bs_e5DRT.js} +1 -1
- package/dist/worker/console/static/assets/index-BLzcAykx.js +451 -0
- package/dist/worker/console/static/assets/index-Cs9JUSPs.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Czvtm6gc.js → infoDiagram-6WML65LV-BXYuU4Uw.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B7Xp8HyJ.js → ishikawaDiagram-WSZJBQD7-C34erCbU.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DWYZcvph.js → journeyDiagram-NVQOT4AX-DZCAiOU3.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-KTLZRUjx.js → kanban-definition-27J2QSJJ-C9gaTdUm.js} +1 -1
- package/dist/worker/console/static/assets/{linear-_Exn7bfl.js → linear-CvI0Z17U.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-Cxg8kX8V.js → mermaid.core-DMMSIx7j.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-oKrlo2Bt.js → mindmap-definition-FAOFIHXS-BKKP0EO6.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-XAeteiNd.js → pegDiagram-VL7TDLO6-C3LiFbl5.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BTf1K-lp.js → pieDiagram-7S7Q4E2Y-Bb3Om2Dz.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C0IlY0Jf.js → quadrantDiagram-CIZ2JOQS-DNthMjzc.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BVbKtZhW.js → railroadDiagram-AXF67PYL-miFsr4Tv.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C5e4JFcr.js → requirementDiagram-LRYGKXZP-yXUncmn6.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cz30hc29.js → sankeyDiagram-W5VNT64P-DLeWKLak.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DS33n6d0.js → sequenceDiagram-SI44F4Z6-CI3he1R3.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-46w7v-9T.js → sizeCapture-X5ZJPWSS-B7jfDkS5.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-pbcD5SsZ.js → stateDiagram-OKZ733FA-BmrZod7J.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-D9ykKEWP.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DuivYBSj.js → swimlanes-SLNWSIFB-CDOlHVSY.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BvPOXALT.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cb9VZVIl.js → timeline-definition-Z64GVDOM-Cz-XX1gy.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-D2-mfOgR.js → vennDiagram-T6HMQDX7-caV4cI8C.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DeS2aC76.js → wardleyDiagram-T6FBY63Y-dImPlnIO.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-WGsrFjxH.js → xychartDiagram-ELKLHX3M-CtRYVJcn.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/useRunProgress.js +9 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +19 -8
- package/dist/worker/console/static-src/operator-chat/dag-progress-link.js +22 -0
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +15 -7
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -0
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +106 -0
- package/dist/worker/console/workspace-context.js +1 -111
- package/dist/worker/observability/read-model.js +4 -0
- package/dist/worker/observe/static/dag-node-purpose.js +0 -5
- package/dist/worker/observe/static/views/dag-inspector.js +2 -7
- package/dist/worker/observe/static/views/session-timeline.js +46 -15
- package/dist/workflows/dag/backend-test-result-contract.js +1 -1
- package/dist/workflows/dag/contract-validator-registrations.js +2 -1
- package/dist/workflows/dag/frontend-implementation-contract.js +162 -932
- package/dist/workflows/dag/frontend-plan-render.js +1 -2
- package/dist/workflows/dag/frontend-prewrite-gate.js +349 -255
- package/dist/workflows/dag/frontend-recovery-plan.js +10 -17
- package/dist/workflows/dag/frontend-recovery-run.js +20 -33
- package/dist/workflows/dag/frontend-repair.js +432 -1
- package/dist/workflows/dag/frontend-review-context.js +15 -261
- package/dist/workflows/dag/frontend-verification-trace.js +24 -249
- package/dist/workflows/dag/frontend-worktree-diff.js +17 -250
- package/dist/workflows/dag/init-hybrid.js +494 -685
- package/dist/workflows/dag/interrupt-request.js +0 -7
- package/dist/workflows/dag/node-execution.js +4 -732
- package/dist/workflows/dag/prompt.js +1 -34
- package/dist/workflows/dag/report.js +1 -37
- package/dist/workflows/dag/rerun-feedback.js +1 -256
- package/dist/workflows/dag/rerun-plan.js +2 -10
- package/dist/workflows/dag/rerun-run.js +0 -23
- package/dist/workflows/dag/rerun-task.js +0 -29
- package/dist/workflows/dag/retry-policy.js +104 -214
- package/dist/workflows/dag/runner.js +122 -429
- package/dist/workflows/dag/scheduler.js +16 -114
- package/dist/workflows/dag/types.js +12 -239
- package/dist/workflows/dag/validate.js +12 -28
- package/docs/init-surface.manifest.json +12 -30
- package/docs/skills/vetted-skill-registry.md +2 -4
- package/docs/templates/README.md +0 -2
- package/docs/templates/agent-dag-report.schema.json +2 -8
- package/docs/templates/agent-dag.schema.json +1 -1
- package/docs/templates/frontend-implementation-contract.schema.json +1 -4
- package/harness.json +3 -3
- package/package.json +1 -1
- package/skills/frontend-bounded-implement/SKILL.md +14 -15
- package/skills/frontend-design-review/SKILL.md +41 -22
- package/skills/frontend-implementation/SKILL.md +52 -0
- package/skills/frontend-implementation/references/code-standards.md +33 -0
- package/skills/frontend-implementation/references/design-spec.md +56 -0
- package/skills/frontend-implementation/references/node-contracts.md +31 -0
- package/skills/frontend-review/SKILL.md +15 -20
- package/skills/frontend-review/references/review-findings.md +7 -6
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +0 -1
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/commands/dag-follow-up.js +0 -138
- package/dist/executors/pi-read-budget-policy.js +0 -239
- package/dist/worker/console/frontend-human-decision-adapter.js +0 -19
- package/dist/worker/console/frontend-split-operation-adapter.js +0 -20
- package/dist/worker/console/static/assets/channel-bnXW0U3C.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-rZpq_twR.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-rZpq_twR.js +0 -1
- package/dist/worker/console/static/assets/index-D3CC4eUz.css +0 -1
- package/dist/worker/console/static/assets/index-DLt4ZFvD.js +0 -437
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-zBMYiUyi.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-d_rms-5i.js +0 -8
- package/dist/worker/console/static-src/active-run-badge.js +0 -17
- package/dist/worker/materialize/frontend-split-task-materializer.js +0 -72
- package/dist/workflows/dag/dag-retry-schema.js +0 -138
- package/dist/workflows/dag/frontend-closeout.js +0 -221
- package/dist/workflows/dag/frontend-design-policy.js +0 -400
- package/dist/workflows/dag/frontend-human-decision.js +0 -182
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +0 -159
- package/dist/workflows/dag/frontend-read-budgets.js +0 -12
- package/dist/workflows/dag/frontend-recovery-capsule.js +0 -455
- package/dist/workflows/dag/frontend-recovery-controller.js +0 -202
- package/dist/workflows/dag/frontend-recovery-lineage.js +0 -178
- package/dist/workflows/dag/frontend-review-findings.js +0 -270
- package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -914
- package/dist/workflows/dag/frontend-shape-capsule-store.js +0 -191
- package/dist/workflows/dag/frontend-shape-facts.js +0 -409
- package/dist/workflows/dag/frontend-shape.js +0 -427
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +0 -108
- package/dist/workflows/dag/frontend-split-application-service.js +0 -203
- package/dist/workflows/dag/frontend-split-orchestrator.js +0 -899
- package/dist/workflows/dag/frontend-typed-event-store.js +0 -452
- package/dist/workflows/dag/frontend-typed-event-transaction.js +0 -180
- package/dist/workflows/dag/frontend-writer-admission.js +0 -285
- package/dist/workflows/dag/frontend-writer-status.js +0 -256
- package/dist/workflows/dag/recovery-lease.js +0 -80
- package/docs/templates/frontend-implementation-dag.json +0 -89
- package/docs/templates/spec-registry.schema.json +0 -45
- package/skills/frontend-bounded-implement/references/code-standards.md +0 -19
- package/skills/frontend-contract/SKILL.md +0 -23
- package/skills/frontend-contract/references/contract-protocol.md +0 -34
- package/skills/frontend-plan/SKILL.md +0 -26
- package/skills/frontend-plan/references/decision-contract.md +0 -37
- package/skills/frontend-plan/references/design-decisions.md +0 -17
- package/skills/frontend-scout/SKILL.md +0 -25
- package/skills/frontend-scout/references/design-evidence.md +0 -16
- package/skills/frontend-scout/references/scout-evidence.md +0 -23
|
@@ -34,7 +34,7 @@ import { dispatchOperatorAction } from "../operator-actions.js";
|
|
|
34
34
|
import { makeHumanGateCard, } from "./human-gate-card.js";
|
|
35
35
|
import { ContractApplyReceiptStore } from "./contract-apply-receipt-store.js";
|
|
36
36
|
import { projectTaskContext } from "./context-panel.js";
|
|
37
|
-
import { deriveChatSessionTelemetry } from "./session-stats.js";
|
|
37
|
+
import { deriveChatSessionTelemetry, deriveChatTurnTelemetry } from "./session-stats.js";
|
|
38
38
|
import { contractApplyPayloadHash, issueHumanGateToken, verifyHumanGateToken, } from "../human-gate-token.js";
|
|
39
39
|
import { sendJson } from "../routes.js";
|
|
40
40
|
import { openSseResponse, writeSseEvent } from "../operation-sse.js";
|
|
@@ -99,8 +99,16 @@ export function projectStoredMessagesWithEventOrder(messages, events) {
|
|
|
99
99
|
}
|
|
100
100
|
return {
|
|
101
101
|
...message,
|
|
102
|
-
...(start
|
|
103
|
-
|
|
102
|
+
...(start
|
|
103
|
+
? {
|
|
104
|
+
eventSeq: start.seq,
|
|
105
|
+
turnId: start.turnId,
|
|
106
|
+
startedAt: start.at,
|
|
107
|
+
}
|
|
108
|
+
: {}),
|
|
109
|
+
...(finish
|
|
110
|
+
? { finishedSeq: finish.seq, finishedAt: finish.at }
|
|
111
|
+
: {}),
|
|
104
112
|
};
|
|
105
113
|
});
|
|
106
114
|
}
|
|
@@ -828,6 +836,7 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
|
|
|
828
836
|
hasBash: true,
|
|
829
837
|
sessionStats: telemetry.stats,
|
|
830
838
|
usage: telemetry.usage,
|
|
839
|
+
turnTelemetry: deriveChatTurnTelemetry(snapshot),
|
|
831
840
|
contextPressure: typeof deps.runtime.getContextPressureTelemetry === "function"
|
|
832
841
|
? deps.runtime.getContextPressureTelemetry(sessionId)
|
|
833
842
|
: undefined,
|
|
@@ -1056,20 +1065,10 @@ export async function handleSteerDeferredTurn(req, res, deps, sessionId, clientR
|
|
|
1056
1065
|
}
|
|
1057
1066
|
const item = claim.deferredTurn;
|
|
1058
1067
|
try {
|
|
1059
|
-
await deps.runtime.steer(sessionId, item.text, item.images);
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
});
|
|
1064
|
-
deps.events.append(sessionId, active.turnId, {
|
|
1065
|
-
kind: "user_message",
|
|
1066
|
-
data: {
|
|
1067
|
-
clientRequestId: item.clientRequestId,
|
|
1068
|
-
message: { id: userMessage.id, text: userMessage.text, at: userMessage.at },
|
|
1069
|
-
},
|
|
1070
|
-
});
|
|
1071
|
-
await deps.store.clearDeferredTurnForRequest(sessionId, item.clientRequestId);
|
|
1072
|
-
await emitDeferredTurnUpdate(deps, sessionId, active.turnId, undefined, item.revision + 2);
|
|
1068
|
+
await deps.runtime.steer(sessionId, item.clientRequestId, item.text, item.images);
|
|
1069
|
+
// session.steer() only acknowledges queueing. Keep the durable item in
|
|
1070
|
+
// dispatching until Pi emits the corresponding user message_start.
|
|
1071
|
+
await emitDeferredTurnUpdate(deps, sessionId, active.turnId, item, item.revision);
|
|
1073
1072
|
sendJson(res, 200, {
|
|
1074
1073
|
ok: true,
|
|
1075
1074
|
accepted: true,
|
|
@@ -1093,6 +1092,21 @@ export async function handleSteerDeferredTurn(req, res, deps, sessionId, clientR
|
|
|
1093
1092
|
});
|
|
1094
1093
|
}
|
|
1095
1094
|
}
|
|
1095
|
+
async function persistAcceptedSteering(deps, sessionId, turnId, userMessage, clientRequestId) {
|
|
1096
|
+
deps.events.append(sessionId, turnId, {
|
|
1097
|
+
kind: "user_message",
|
|
1098
|
+
data: {
|
|
1099
|
+
clientRequestId,
|
|
1100
|
+
steering: true,
|
|
1101
|
+
message: {
|
|
1102
|
+
id: userMessage.id,
|
|
1103
|
+
text: userMessage.text,
|
|
1104
|
+
at: userMessage.at,
|
|
1105
|
+
},
|
|
1106
|
+
},
|
|
1107
|
+
});
|
|
1108
|
+
await emitDeferredTurnUpdate(deps, sessionId, turnId, undefined, 0);
|
|
1109
|
+
}
|
|
1096
1110
|
/** Lightweight busy-truth endpoint for the client reconcile loop (pi-web
|
|
1097
1111
|
* get_state equivalent): exposes the active turn + the latest turn's durable
|
|
1098
1112
|
* outcome WITHOUT the full message payload, so the UI can poll it on an
|
|
@@ -1652,6 +1666,9 @@ export async function handleChatPrompt(req, res, deps, sessionId) {
|
|
|
1652
1666
|
if (!res.writableEnded)
|
|
1653
1667
|
writeChatSse(res, persisted);
|
|
1654
1668
|
},
|
|
1669
|
+
onSteeringMessagePersisted: async (_record, userMessage, accepted) => {
|
|
1670
|
+
await persistAcceptedSteering(deps, sessionId, turnId, userMessage, accepted.clientRequestId);
|
|
1671
|
+
},
|
|
1655
1672
|
onOperationAccepted: (accepted) => deps.operationLinker.link({
|
|
1656
1673
|
sessionId,
|
|
1657
1674
|
turnId,
|
|
@@ -1962,6 +1979,9 @@ export async function handleCreateChatTurn(req, res, deps, sessionId, trustedPay
|
|
|
1962
1979
|
if (placeholder)
|
|
1963
1980
|
emitSessionTitle(deps, sessionId, turn.turnId, placeholder);
|
|
1964
1981
|
},
|
|
1982
|
+
onSteeringMessagePersisted: async (_record, userMessage, accepted) => {
|
|
1983
|
+
await persistAcceptedSteering(deps, sessionId, turn.turnId, userMessage, accepted.clientRequestId);
|
|
1984
|
+
},
|
|
1965
1985
|
persistUserMessage: trustedPayload?.persistUserMessage,
|
|
1966
1986
|
onOperationAccepted: (accepted) => deps.operationLinker.link({
|
|
1967
1987
|
sessionId,
|
|
@@ -2637,13 +2657,13 @@ async function handleRepoHtmlPreview(res, deps, rawPath) {
|
|
|
2637
2657
|
"Cache-Control": "no-store",
|
|
2638
2658
|
"X-Content-Type-Options": "nosniff",
|
|
2639
2659
|
"Referrer-Policy": "no-referrer",
|
|
2640
|
-
//
|
|
2641
|
-
// sandbox
|
|
2642
|
-
//
|
|
2660
|
+
// Project scripts may drive tabs and other local interactions, but the
|
|
2661
|
+
// opaque sandbox still withholds same-origin access, forms, popups and
|
|
2662
|
+
// top-level navigation. Network requests remain disabled below.
|
|
2643
2663
|
"Content-Security-Policy": [
|
|
2644
|
-
"sandbox",
|
|
2664
|
+
"sandbox allow-scripts",
|
|
2645
2665
|
"default-src 'none'",
|
|
2646
|
-
|
|
2666
|
+
`script-src 'unsafe-inline' ${origin}`,
|
|
2647
2667
|
"object-src 'none'",
|
|
2648
2668
|
"frame-src 'none'",
|
|
2649
2669
|
"form-action 'none'",
|
|
@@ -296,13 +296,6 @@ function operationIdFromArgs(args) {
|
|
|
296
296
|
const operationId = record?.operationId;
|
|
297
297
|
return typeof operationId === "string" && operationId ? operationId : undefined;
|
|
298
298
|
}
|
|
299
|
-
function maxWaitSeconds(args) {
|
|
300
|
-
const record = parseJsonRecord(args);
|
|
301
|
-
const raw = record?.maxWaitMs;
|
|
302
|
-
if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0)
|
|
303
|
-
return undefined;
|
|
304
|
-
return Math.max(1, Math.round(raw / 1000));
|
|
305
|
-
}
|
|
306
299
|
function activityRefForTool(tool) {
|
|
307
300
|
// Plan A (2026-08-16): server backfills operationId on the persisted tool
|
|
308
301
|
// entry (and live tool_result frames carry it in the result payload), so
|
|
@@ -313,6 +306,17 @@ function activityRefForTool(tool) {
|
|
|
313
306
|
operationIdFromArgs(tool.resultText);
|
|
314
307
|
return operationId ? `operation:${operationId}` : `tool:${tool.id}`;
|
|
315
308
|
}
|
|
309
|
+
export function dagProgressActivityForTool(tool) {
|
|
310
|
+
if (tool.toolName !== "operationWait")
|
|
311
|
+
return undefined;
|
|
312
|
+
const activityRef = activityRefForTool(tool);
|
|
313
|
+
if (!activityRef.startsWith("operation:"))
|
|
314
|
+
return undefined;
|
|
315
|
+
return {
|
|
316
|
+
id: `dag-progress:${activityRef}`,
|
|
317
|
+
activityRef,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
316
320
|
function semanticKindForStatus(status) {
|
|
317
321
|
if (status === "running")
|
|
318
322
|
return "progress";
|
|
@@ -377,14 +381,11 @@ function nodeActivityFromMessages(messages, id, activityRef) {
|
|
|
377
381
|
function operationWaitActivity(tool) {
|
|
378
382
|
const activityRef = activityRefForTool(tool);
|
|
379
383
|
const id = `activity:${tool.id}`;
|
|
380
|
-
const seconds = maxWaitSeconds(tool.args);
|
|
381
384
|
if (tool.status === "running") {
|
|
382
385
|
return {
|
|
383
386
|
id,
|
|
384
387
|
kind: "progress",
|
|
385
|
-
title:
|
|
386
|
-
? `正在等待新的执行进展,最长等待时间 ${seconds} 秒`
|
|
387
|
-
: "正在等待新的执行进展",
|
|
388
|
+
title: "正在等待新的执行进展",
|
|
388
389
|
activityRef,
|
|
389
390
|
};
|
|
390
391
|
}
|
|
@@ -156,6 +156,102 @@ export function deriveChatSessionTelemetry(events) {
|
|
|
156
156
|
export function billedInputTokens(usage) {
|
|
157
157
|
return usage.inputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
|
|
158
158
|
}
|
|
159
|
+
export function deriveChatTurnTelemetry(events) {
|
|
160
|
+
const byTurn = new Map();
|
|
161
|
+
const turnOf = (turnId) => {
|
|
162
|
+
let entry = byTurn.get(turnId);
|
|
163
|
+
if (!entry) {
|
|
164
|
+
entry = {
|
|
165
|
+
turnId,
|
|
166
|
+
steps: 0,
|
|
167
|
+
llmMs: 0,
|
|
168
|
+
toolMs: 0,
|
|
169
|
+
toolCalls: 0,
|
|
170
|
+
samples: [],
|
|
171
|
+
};
|
|
172
|
+
byTurn.set(turnId, entry);
|
|
173
|
+
}
|
|
174
|
+
return entry;
|
|
175
|
+
};
|
|
176
|
+
const toolStarts = new Map();
|
|
177
|
+
for (const event of events) {
|
|
178
|
+
const at = eventTime(event);
|
|
179
|
+
const turn = turnOf(event.turnId);
|
|
180
|
+
if (event.kind === "usage") {
|
|
181
|
+
const sample = usageSample(event.data);
|
|
182
|
+
if (sample)
|
|
183
|
+
turn.samples.push(sample);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (event.kind === "user_message") {
|
|
187
|
+
if (turn.startedAt === undefined && at !== undefined)
|
|
188
|
+
turn.startedAt = at;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (event.kind === "agent_start") {
|
|
192
|
+
if (at !== undefined)
|
|
193
|
+
turn.stepStart = at;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (event.kind === "tool_call") {
|
|
197
|
+
turn.toolCalls += 1;
|
|
198
|
+
const toolCallId = event.data.toolCallId;
|
|
199
|
+
if (typeof toolCallId === "string" && toolCallId && at !== undefined) {
|
|
200
|
+
toolStarts.set(`${event.turnId}\u0000${toolCallId}`, at);
|
|
201
|
+
}
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (event.kind === "tool_result") {
|
|
205
|
+
const toolCallId = event.data.toolCallId;
|
|
206
|
+
if (typeof toolCallId === "string" && toolCallId && at !== undefined) {
|
|
207
|
+
const key = `${event.turnId}\u0000${toolCallId}`;
|
|
208
|
+
const startedAt = toolStarts.get(key);
|
|
209
|
+
if (startedAt !== undefined && at >= startedAt) {
|
|
210
|
+
turn.toolMs += at - startedAt;
|
|
211
|
+
}
|
|
212
|
+
toolStarts.delete(key);
|
|
213
|
+
// Next assistant step within the same turn starts after this tool.
|
|
214
|
+
turn.stepStart = at;
|
|
215
|
+
}
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (event.kind !== "message_end")
|
|
219
|
+
continue;
|
|
220
|
+
turn.steps += 1;
|
|
221
|
+
const recorded = stepTiming(event.data);
|
|
222
|
+
if (recorded?.llmMs !== undefined)
|
|
223
|
+
turn.llmMs += recorded.llmMs;
|
|
224
|
+
else if (at !== undefined &&
|
|
225
|
+
turn.stepStart !== undefined &&
|
|
226
|
+
at >= turn.stepStart) {
|
|
227
|
+
turn.llmMs += at - turn.stepStart;
|
|
228
|
+
}
|
|
229
|
+
if (recorded?.ttftMs !== undefined) {
|
|
230
|
+
turn.ttftMs = (turn.ttftMs ?? 0) + recorded.ttftMs;
|
|
231
|
+
}
|
|
232
|
+
if (recorded?.decodeMs !== undefined &&
|
|
233
|
+
recorded.decodeMs > 0 &&
|
|
234
|
+
recorded.outputTokens !== undefined) {
|
|
235
|
+
turn.decodeMs = (turn.decodeMs ?? 0) + recorded.decodeMs;
|
|
236
|
+
turn.decodeTokens = (turn.decodeTokens ?? 0) + recorded.outputTokens;
|
|
237
|
+
}
|
|
238
|
+
turn.stepStart = undefined;
|
|
239
|
+
}
|
|
240
|
+
return [...byTurn.values()].map((entry) => ({
|
|
241
|
+
turnId: entry.turnId,
|
|
242
|
+
steps: entry.steps,
|
|
243
|
+
llmMs: entry.llmMs,
|
|
244
|
+
toolMs: entry.toolMs,
|
|
245
|
+
toolCalls: entry.toolCalls,
|
|
246
|
+
...(entry.ttftMs !== undefined ? { ttftMs: entry.ttftMs } : {}),
|
|
247
|
+
...(entry.decodeMs !== undefined ? { decodeMs: entry.decodeMs } : {}),
|
|
248
|
+
...(entry.decodeTokens !== undefined
|
|
249
|
+
? { decodeTokens: entry.decodeTokens }
|
|
250
|
+
: {}),
|
|
251
|
+
usage: aggregateUsage(entry.samples),
|
|
252
|
+
...(entry.startedAt !== undefined ? { startedAt: entry.startedAt } : {}),
|
|
253
|
+
}));
|
|
254
|
+
}
|
|
159
255
|
export function cacheHitPercent(usage) {
|
|
160
256
|
const billed = billedInputTokens(usage);
|
|
161
257
|
return billed > 0 ? Math.round((usage.cacheReadTokens / billed) * 100) : null;
|
|
@@ -384,6 +384,44 @@ export class ChatSessionStore {
|
|
|
384
384
|
return { deferredTurn: existing, clearedRevision: existing.revision + 1 };
|
|
385
385
|
});
|
|
386
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Commit Pi's steering acceptance as one durable record transition. Removing
|
|
389
|
+
* the dispatching queue item and appending its user message together prevents
|
|
390
|
+
* a crash between two writes from replaying or losing the accepted input.
|
|
391
|
+
*/
|
|
392
|
+
async acceptSteeringMessage(sessionId, clientRequestId, text) {
|
|
393
|
+
return this.serialize(sessionId, async () => {
|
|
394
|
+
const record = await this.get(sessionId);
|
|
395
|
+
if (!record)
|
|
396
|
+
throw new Error(`chat session not found: ${sessionId}`);
|
|
397
|
+
const queue = [...(record.deferredTurns ?? [])];
|
|
398
|
+
const index = queue.findIndex((item) => item.clientRequestId === clientRequestId);
|
|
399
|
+
const deferredTurn = index >= 0 ? queue[index] : undefined;
|
|
400
|
+
if (!deferredTurn || deferredTurn.state !== "dispatching")
|
|
401
|
+
return undefined;
|
|
402
|
+
const message = {
|
|
403
|
+
id: `m_${record.messages.length + 1}_${Math.random().toString(36).slice(2, 8)}`,
|
|
404
|
+
role: "user",
|
|
405
|
+
text,
|
|
406
|
+
at: new Date().toISOString(),
|
|
407
|
+
};
|
|
408
|
+
queue.splice(index, 1);
|
|
409
|
+
record.messages.push(message);
|
|
410
|
+
record.deferredTurns = queue;
|
|
411
|
+
record.deferredTurn = queue[0];
|
|
412
|
+
record.deferredQueueRevision = (record.deferredQueueRevision ?? 0) + 1;
|
|
413
|
+
if (!record.displayTitle)
|
|
414
|
+
record.displayTitle = cleanChatTitle(message.text);
|
|
415
|
+
record.updatedAt = message.at;
|
|
416
|
+
await this.persist(record);
|
|
417
|
+
return {
|
|
418
|
+
record,
|
|
419
|
+
message,
|
|
420
|
+
deferredTurn,
|
|
421
|
+
clearedRevision: deferredTurn.revision + 1,
|
|
422
|
+
};
|
|
423
|
+
});
|
|
424
|
+
}
|
|
387
425
|
async appendMessage(sessionId, message) {
|
|
388
426
|
return this.serialize(sessionId, async () => {
|
|
389
427
|
const record = await this.get(sessionId);
|
|
@@ -520,84 +558,91 @@ export async function runChatTurn(options) {
|
|
|
520
558
|
// observable to tests and to a reconnecting client reading the record).
|
|
521
559
|
const pending = [];
|
|
522
560
|
let settledEvent;
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
else {
|
|
532
|
-
options.onEvent?.(event);
|
|
561
|
+
let eventChain = Promise.resolve();
|
|
562
|
+
const processEvent = async (event) => {
|
|
563
|
+
if (event.type === "steering_accepted") {
|
|
564
|
+
const committed = await store.acceptSteeringMessage(sessionId, event.clientRequestId, event.text);
|
|
565
|
+
if (committed) {
|
|
566
|
+
await options.onSteeringMessagePersisted?.(committed.record, committed.message, {
|
|
567
|
+
clientRequestId: event.clientRequestId,
|
|
568
|
+
});
|
|
533
569
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
// execution happens in the SDK's customTools[].execute(), which
|
|
550
|
-
// routes to dispatchChatToolCall (Gate 3). Do NOT dispatch here —
|
|
551
|
-
// that would double-execute every tool call (P0.2).
|
|
552
|
-
break;
|
|
553
|
-
case "tool_result": {
|
|
554
|
-
const acceptedOperationId = acceptedOperationIdFromResult(event.toolName, event.result);
|
|
555
|
-
if (acceptedOperationId && options.onOperationAccepted) {
|
|
556
|
-
pending.push(Promise.resolve(options.onOperationAccepted({
|
|
557
|
-
sessionId,
|
|
558
|
-
toolCallId: event.toolCallId,
|
|
559
|
-
toolName: event.toolName,
|
|
560
|
-
operationId: acceptedOperationId,
|
|
561
|
-
})));
|
|
562
|
-
}
|
|
563
|
-
// tool_execution_end carries the real result of the tool call.
|
|
564
|
-
// Persist exactly once (the tool message + audit trail). This is
|
|
565
|
-
// the single point of record for tool outcomes (P0.2).
|
|
566
|
-
const resultText = safeStringifyResult(event.result);
|
|
567
|
-
// ChatSessionStore serializes writes per session, so these two calls
|
|
568
|
-
// are safe to fire concurrently — neither clobbers the other.
|
|
569
|
-
pending.push(store.appendMessage(sessionId, {
|
|
570
|
-
role: "tool",
|
|
571
|
-
text: resultText,
|
|
572
|
-
toolCallId: event.toolCallId,
|
|
573
|
-
toolName: event.toolName,
|
|
574
|
-
isError: event.isError,
|
|
575
|
-
// Durable tool→operation link (plan A): the restored timeline can
|
|
576
|
-
// resolve operation:<id> refs without the live operation-ref event.
|
|
577
|
-
...(acceptedOperationId
|
|
578
|
-
? { operationId: acceptedOperationId }
|
|
579
|
-
: {}),
|
|
580
|
-
}));
|
|
581
|
-
pending.push(store.recordToolInvocation(sessionId, {
|
|
582
|
-
toolCallId: event.toolCallId,
|
|
583
|
-
toolName: event.toolName,
|
|
584
|
-
ok: !event.isError,
|
|
585
|
-
}));
|
|
586
|
-
break;
|
|
587
|
-
}
|
|
588
|
-
case "error":
|
|
589
|
-
turnError = { code: "CHAT_TURN_ERROR", message: event.message };
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (event.type === "agent_settled") {
|
|
573
|
+
// The durable operation-ref linker may perform an async canonical
|
|
574
|
+
// operation read. Hold settled until those writes finish so replay
|
|
575
|
+
// never observes agent_settled before its accepted operation ref.
|
|
576
|
+
settledEvent = event;
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
options.onEvent?.(event);
|
|
580
|
+
}
|
|
581
|
+
switch (event.type) {
|
|
582
|
+
case "message_update":
|
|
583
|
+
case "message_end":
|
|
584
|
+
if (event.type === "message_end") {
|
|
590
585
|
pending.push(store.appendMessage(sessionId, {
|
|
591
586
|
role: "assistant",
|
|
592
|
-
|
|
593
|
-
text: event.providerError.message,
|
|
594
|
-
providerError: event.providerError,
|
|
587
|
+
text: event.text,
|
|
595
588
|
...(event.content && event.content.length > 0
|
|
596
589
|
? { content: event.content }
|
|
597
590
|
: {}),
|
|
598
591
|
}));
|
|
599
|
-
|
|
592
|
+
}
|
|
593
|
+
break;
|
|
594
|
+
case "tool_call":
|
|
595
|
+
// Forwarded to the caller (SSE) for UI feedback. The actual tool
|
|
596
|
+
// execution happens in the SDK's customTools[].execute(), which
|
|
597
|
+
// routes to dispatchChatToolCall (Gate 3). Do NOT dispatch here —
|
|
598
|
+
// that would double-execute every tool call (P0.2).
|
|
599
|
+
break;
|
|
600
|
+
case "tool_result": {
|
|
601
|
+
const acceptedOperationId = acceptedOperationIdFromResult(event.toolName, event.result);
|
|
602
|
+
if (acceptedOperationId && options.onOperationAccepted) {
|
|
603
|
+
pending.push(Promise.resolve(options.onOperationAccepted({
|
|
604
|
+
sessionId,
|
|
605
|
+
toolCallId: event.toolCallId,
|
|
606
|
+
toolName: event.toolName,
|
|
607
|
+
operationId: acceptedOperationId,
|
|
608
|
+
})));
|
|
609
|
+
}
|
|
610
|
+
const resultText = safeStringifyResult(event.result);
|
|
611
|
+
pending.push(store.appendMessage(sessionId, {
|
|
612
|
+
role: "tool",
|
|
613
|
+
text: resultText,
|
|
614
|
+
toolCallId: event.toolCallId,
|
|
615
|
+
toolName: event.toolName,
|
|
616
|
+
isError: event.isError,
|
|
617
|
+
...(acceptedOperationId ? { operationId: acceptedOperationId } : {}),
|
|
618
|
+
}));
|
|
619
|
+
pending.push(store.recordToolInvocation(sessionId, {
|
|
620
|
+
toolCallId: event.toolCallId,
|
|
621
|
+
toolName: event.toolName,
|
|
622
|
+
ok: !event.isError,
|
|
623
|
+
}));
|
|
624
|
+
break;
|
|
600
625
|
}
|
|
626
|
+
case "error":
|
|
627
|
+
turnError = { code: "CHAT_TURN_ERROR", message: event.message };
|
|
628
|
+
pending.push(store.appendMessage(sessionId, {
|
|
629
|
+
role: "assistant",
|
|
630
|
+
kind: "provider-error",
|
|
631
|
+
text: event.providerError.message,
|
|
632
|
+
providerError: event.providerError,
|
|
633
|
+
...(event.content && event.content.length > 0
|
|
634
|
+
? { content: event.content }
|
|
635
|
+
: {}),
|
|
636
|
+
}));
|
|
637
|
+
break;
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
try {
|
|
641
|
+
await runtime.prompt(sessionId, text, (event) => {
|
|
642
|
+
// Pi's event callback is synchronous. Serialize projection work so the
|
|
643
|
+
// accepted steering user_message is durable and sequenced before any
|
|
644
|
+
// assistant/tool event that follows it.
|
|
645
|
+
eventChain = eventChain.then(() => processEvent(event)).catch(() => undefined);
|
|
601
646
|
}, {
|
|
602
647
|
signal: options.signal,
|
|
603
648
|
images: options.images,
|
|
@@ -605,10 +650,12 @@ export async function runChatTurn(options) {
|
|
|
605
650
|
});
|
|
606
651
|
}
|
|
607
652
|
catch (error) {
|
|
653
|
+
await eventChain;
|
|
608
654
|
await Promise.allSettled(pending);
|
|
609
655
|
const message = error instanceof Error ? error.message : String(error);
|
|
610
656
|
return { ok: false, error: { code: "CHAT_TURN_FAILED", message } };
|
|
611
657
|
}
|
|
658
|
+
await eventChain;
|
|
612
659
|
await Promise.allSettled(pending);
|
|
613
660
|
if (settledEvent)
|
|
614
661
|
options.onEvent?.(settledEvent);
|
|
@@ -37,7 +37,7 @@ export const SHORTCUTS = [
|
|
|
37
37
|
{
|
|
38
38
|
command: "/reload",
|
|
39
39
|
label: "Reload session",
|
|
40
|
-
description: "重新加载当前 Pi
|
|
40
|
+
description: "重新加载当前 Pi 会话的设置、扩展、技能、提示词、主题、上下文文件及模型与认证配置(不产生消息)",
|
|
41
41
|
action: "reload",
|
|
42
42
|
source: "operator",
|
|
43
43
|
},
|