@tea-agent/loop-agent 0.40.0-next.1 → 0.40.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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +106 -109
- package/README.md +2 -2
- package/dist/application/task-lifecycle/advance.js +12 -0
- package/dist/build-stamp.json +3 -3
- package/dist/cli/update/runtime-activity.js +1 -29
- package/dist/commands/dag-rerun.js +1 -1
- package/dist/executors/dag-pi-executor.js +69 -7
- package/dist/executors/pi-executor.js +4 -2
- package/dist/executors/pi-sdk-executor.js +46 -1
- package/dist/executors/pi-writer-tool-policy.js +57 -1
- package/dist/executors/shell-executor.js +43 -3
- package/dist/executors/workspace-write-snapshot.js +68 -0
- package/dist/infrastructure/harness/atomic-write.js +23 -0
- package/dist/shared/dag-prompt-override.js +4 -5
- package/dist/shared/update/npm-client.js +33 -11
- package/dist/task/config-types.js +10 -0
- package/dist/task/contract/apply.js +11 -1
- package/dist/task/contract/constants.js +2 -0
- package/dist/task/contract/hash.js +3 -0
- package/dist/task/contract/observe.js +25 -4
- package/dist/task/contract/paths.js +2 -1
- package/dist/task/contract/project.js +15 -1
- package/dist/task/contract/schema.js +2 -0
- package/dist/task/contract/transaction.js +12 -0
- package/dist/task/source-prepare/completeness.js +188 -0
- package/dist/task/source-prepare/fragment-inventory.js +474 -0
- package/dist/task/source-prepare/index.js +3 -0
- package/dist/task/source-prepare/ledger-reconciliation.js +127 -0
- package/dist/task/source-prepare/ledger-review.js +214 -0
- package/dist/task/source-prepare/ledger.js +545 -0
- package/dist/task/source-prepare/prepare.js +262 -1
- package/dist/task/source-prepare/semantic-intake.js +19 -3
- package/dist/task/source-prepare/source-fidelity-pi.js +384 -0
- package/dist/task/source-prepare/types.js +22 -0
- package/dist/worker/cli.js +12 -26
- package/dist/worker/console/chat/chat-event-store.js +75 -5
- package/dist/worker/console/chat/context-panel.js +7 -0
- package/dist/worker/console/chat/deferred-turn.js +12 -0
- package/dist/worker/console/chat/pi-runtime.js +331 -18
- package/dist/worker/console/chat/provider-error.js +98 -0
- package/dist/worker/console/chat/routes.js +389 -56
- package/dist/worker/console/chat/session-stats.js +179 -0
- package/dist/worker/console/chat/session-store.js +93 -63
- package/dist/worker/console/chat/todos.js +115 -0
- package/dist/worker/console/chat/tool-preview.js +42 -0
- package/dist/worker/console/chat/turn-process.js +20 -55
- package/dist/worker/console/chat/user-questions.js +266 -0
- package/dist/worker/console/chat/workspace-landing.js +79 -0
- package/dist/worker/console/console-handoff.js +82 -0
- package/dist/worker/console/console-update-and-init.js +115 -0
- package/dist/worker/console/console-update-runtime.js +84 -0
- package/dist/worker/console/draft-store.js +49 -0
- package/dist/worker/console/native-directory-picker.js +13 -0
- package/dist/worker/console/operation-store.js +1 -1
- package/dist/worker/console/operator-actions.js +129 -4
- package/dist/worker/console/operator-surface-health.js +1 -0
- package/dist/worker/console/prd-intake-bridge.js +13 -0
- package/dist/worker/console/routes.js +312 -1
- package/dist/worker/console/security.js +4 -4
- package/dist/worker/console/server.js +583 -172
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Br92EGb7.js → abnfDiagram-N423BO3Z-Bs-CDAXM.js} +1 -1
- package/dist/worker/console/static/assets/{arc-oulImtQq.js → arc-CAkA3We3.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-tyHc6aqO.js → architectureDiagram-T3A2C74G-DEmI_zqr.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CY86BSpT.js → blockDiagram-VBNYF7ZC-uDHANebE.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BKKsDmNs.js → c4Diagram-5PPSVZJV-Bv97b4_B.js} +1 -1
- package/dist/worker/console/static/assets/channel-amxUpk7o.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CXZdjl3B.js → chunk-2GRJ4B5K-shmQKsFz.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-B-aIFDQ5.js → chunk-2Q5K7J3B-BAraNvcN.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BDgAW_B2.js → chunk-5RXB4S5H-BirhxUop.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DABa4UPR.js → chunk-5VM5RSS4-C9LgUJpR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-aGbysVOx.js → chunk-6Q2QTUOP-DrU-s-t7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-B8MSZDVs.js → chunk-GF5L2VYU-Tx-H_FA2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CpelV73g.js → chunk-JWPE2WC7-BjLtNFrR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BE04ty3z.js → chunk-KBJHAD2P-UNLVwSsy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-BUxTkaaR.js → chunk-RYQCIY6F-CBEUX8XD.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CYYBVKfX.js → chunk-XXDRQBXY-WTL7IZOq.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-bejwBVIz.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-bejwBVIz.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CEQF_xmQ.js → cose-bilkent-JH36ORCC-CkHG6W3s.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CqpxN5IT.js → cynefin-VYW2F7L2-BRzFqTaL.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-B3PzanVi.js → cynefinDiagram-MW4NZA55-2_CFp_i7.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BSUGCNx1.js → dagre-VZM6K2ZE-BJtsmdkE.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CdLevU5T.js → diagram-7IWD3JNH-DNEFFo6w.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BjpF_Kok.js → diagram-B4RE2ZJO-B_IWV7eo.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-C4I6NJny.js → diagram-LBJQPF4R-DvvRx2dz.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-C8hecCr5.js → diagram-Q27KOJAE-CsibsWLg.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-Daod6tOS.js → diagram-UB23O5K3-les_djBQ.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BvvmazGv.js → ebnfDiagram-BXEA7PRR-BEm2n7qJ.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DeN_vfV4.js → erDiagram-JOGREHBK-BI7vHHVm.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-D6dq1t9z.js → flowDiagram-UKHOOZJN-CUFCERrB.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DziIdNY4.js → ganttDiagram-PKOTCBZU-wxyLiGWt.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dy4s5CXr.js → gitGraphDiagram-DS77QQ5N-Bt897K8Z.js} +1 -1
- package/dist/worker/console/static/assets/index-BHhUOTri.css +1 -0
- package/dist/worker/console/static/assets/index-DqJbO3-p.js +407 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Czv5eYx2.js → infoDiagram-6WML65LV-DYNx_IPv.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-Bsk3nFRT.js → ishikawaDiagram-WSZJBQD7-CzpLNEu6.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DxGmNj5K.js → journeyDiagram-NVQOT4AX-97f9owNU.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CYCFqkO_.js → kanban-definition-27J2QSJJ-BxM9hyBn.js} +1 -1
- package/dist/worker/console/static/assets/{linear-B0YzRwrd.js → linear-ByuxcHvp.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-t3EZtIGh.js → mermaid.core-DwPGvpCv.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BTXpTv0T.js → mindmap-definition-FAOFIHXS-DCoqG9RT.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DU8UkKqA.js → pegDiagram-VL7TDLO6-BLSBmBMx.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DP4y8ohp.js → pieDiagram-7S7Q4E2Y-B0t_0oJW.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-B07vNaYR.js → quadrantDiagram-CIZ2JOQS-sTzDk7Nt.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-xDWktb2i.js → railroadDiagram-AXF67PYL-B3jRdATC.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BLNMKqeS.js → requirementDiagram-LRYGKXZP-D-cTtXEN.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-n0adalxH.js → sankeyDiagram-W5VNT64P-B25igJRE.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BbnS-2DL.js → sequenceDiagram-SI44F4Z6-jW_lM1_D.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4uPpCLu.js → sizeCapture-X5ZJPWSS-CrzTgsHE.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA--MMbB0fX.js → stateDiagram-OKZ733FA-B01MvlqE.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BvFKEtm2.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BXUgp2QY.js → swimlanes-SLNWSIFB-YcF12FiU.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DIB2eVg-.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CsaMxki7.js → timeline-definition-Z64GVDOM-ClnRcg2s.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-C2IuG9iH.js → vennDiagram-T6HMQDX7-B6gr_z-q.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CmEWOXCW.js → wardleyDiagram-T6FBY63Y-CvSgguGH.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-D_9LE0MG.js → xychartDiagram-ELKLHX3M-23OtSZCb.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +68 -4
- package/dist/worker/console/static-src/app/useConsoleShell.js +30 -12
- package/dist/worker/console/static-src/app/useOperatorActions.js +41 -6
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +10 -11
- package/dist/worker/console/static-src/app/useRunProgress.js +2 -1
- package/dist/worker/console/static-src/app/useTaskWizard.js +74 -2
- package/dist/worker/console/static-src/night/useNightBoard.js +8 -5
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +141 -8
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +79 -0
- package/dist/worker/console/static-src/operator-chat/details-dag-actions.js +122 -0
- package/dist/worker/console/static-src/operator-chat/refs.js +3 -0
- package/dist/worker/console/static-src/operator-chat/runtime-selection-labels.js +27 -0
- package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +25 -0
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +3 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +181 -87
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +159 -44
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +177 -8
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +63 -9
- package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
- package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -3
- package/dist/worker/console/static-src/shell/useWorkspaces.js +299 -0
- package/dist/worker/console/static-src/shell/workspace-route.js +339 -0
- package/dist/worker/console/workspace-context.js +234 -0
- package/dist/worker/console/workspace-registry.js +214 -0
- package/dist/worker/observability/init-runtime-activity.js +26 -0
- package/dist/worker/observe/node-input.js +88 -2
- package/dist/worker/observe/routes.js +188 -0
- package/dist/worker/observe/server.js +4 -0
- package/dist/worker/observe/static/api.js +29 -9
- package/dist/worker/observe/static/app.js +18 -70
- package/dist/worker/observe/static/dag-node-purpose.d.ts +6 -0
- package/dist/worker/observe/static/dag-node-purpose.js +203 -0
- package/dist/worker/observe/static/format.js +37 -0
- package/dist/worker/observe/static/index.html +6 -1
- package/dist/worker/observe/static/inspect-workspace.js +307 -0
- package/dist/worker/observe/static/operator-chrome.css +145 -20
- package/dist/worker/observe/static/operator-chrome.d.ts +20 -3
- package/dist/worker/observe/static/operator-chrome.js +330 -100
- package/dist/worker/observe/static/prompt-restart-candidates.d.ts +18 -0
- package/dist/worker/observe/static/prompt-restart-candidates.js +36 -0
- package/dist/worker/observe/static/router.d.ts +46 -0
- package/dist/worker/observe/static/router.js +25 -1
- package/dist/worker/observe/static/state.d.ts +50 -0
- package/dist/worker/observe/static/state.js +174 -2
- package/dist/worker/observe/static/styles.css +357 -35
- package/dist/worker/observe/static/views/dag-graph.js +4 -2
- package/dist/worker/observe/static/views/dag-inspector.js +533 -79
- package/dist/worker/observe/static/views/dag.d.ts +13 -0
- package/dist/worker/observe/static/views/dag.js +6 -4
- package/dist/worker/observe/static/views/dashboard.js +6 -4
- package/dist/worker/observe/static/views/night.js +2 -2
- package/dist/worker/observe/static/views/pool.js +1 -0
- package/dist/worker/observe/static/views/run.js +3 -1
- package/dist/worker/observe/static/views/task.js +56 -1
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +29 -1
- package/dist/workflows/dag/backend-test-markdown-workflow.js +176 -1
- package/dist/workflows/dag/backend-test-pytest-collection.js +98 -14
- package/dist/workflows/dag/backend-test-writer-completeness.js +17 -17
- package/dist/workflows/dag/dynamic-runtime/map.js +1 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +88 -2
- package/dist/workflows/dag/frontend-plan-render.js +24 -0
- package/dist/workflows/dag/frontend-prewrite-gate.js +197 -0
- package/dist/workflows/dag/frontend-review-context.js +92 -10
- package/dist/workflows/dag/init-hybrid.js +238 -51
- package/dist/workflows/dag/interrupt-request.js +3 -3
- package/dist/workflows/dag/lifecycle.js +3 -3
- package/dist/workflows/dag/node-execution.js +45 -0
- package/dist/workflows/dag/types.js +62 -12
- package/docs/README.md +2 -0
- package/docs/architecture/evolution.md +45 -0
- package/docs/init-surface.manifest.json +1 -0
- package/docs/operations/local-development-environment.md +21 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +18 -0
- package/docs/templates/backend-test-dag.json +265 -237
- package/docs/templates/frontend-implementation-contract.schema.json +681 -24
- package/harness.json +3 -3
- package/package.json +3 -1
- package/skills/loop-agent/references/hybrid-dag.md +7 -0
- package/dist/worker/console/static/assets/channel-TY36zt-i.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Dk7QCChM.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Dk7QCChM.js +0 -1
- package/dist/worker/console/static/assets/index-C3GgHg-g.css +0 -1
- package/dist/worker/console/static/assets/index-Ci8ksvBm.js +0 -406
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BqOigMQ4.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Caxz9UA2.js +0 -8
- package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
- package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { classifyRecoveryFact, consoleRecoveryCtas, partitionRecoveryCtas, recommendedRecoveryCtaId, } from "../../recovery-cta.js";
|
|
3
3
|
import { buildRecoveryReason, isAutoRecoveryReason, pickDefaultRecoveryNodeId, pickDefaultRecoveryRunId, sortRecoveryNodeCandidates, } from "../../recovery-selection.js";
|
|
4
|
-
import { postOperator, RECOVERY_FACT_LABEL, } from "./console-types.js";
|
|
4
|
+
import { postOperator, scopedReadUrl, RECOVERY_FACT_LABEL, } from "./console-types.js";
|
|
5
5
|
import { useRecoveryActions } from "./useRecoveryActions.js";
|
|
6
|
+
import { applyCanonicalHref, buildInspectHref, } from "../shell/workspace-route.js";
|
|
6
7
|
/** Recovery workspace: DAG-run / node candidate selection, fact-classified
|
|
7
8
|
* CTA lanes, structured reason prefill, plan preflight and gated reruns. */
|
|
8
9
|
export function useRecoveryConsole(params) {
|
|
@@ -36,12 +37,12 @@ export function useRecoveryConsole(params) {
|
|
|
36
37
|
const inspectPath = useCallback(() => {
|
|
37
38
|
if (dagRunId.trim()) {
|
|
38
39
|
// from=recovery 激活统一壳的对象上下文 chip 与显式返回路径
|
|
39
|
-
return `/
|
|
40
|
+
return buildInspectHref(`/dag/${encodeURIComponent(dagRunId.trim())}`, "from=recovery");
|
|
40
41
|
}
|
|
41
|
-
return "
|
|
42
|
+
return "/#/inspect";
|
|
42
43
|
}, [dagRunId]);
|
|
43
44
|
const openFullInspect = useCallback(() => {
|
|
44
|
-
|
|
45
|
+
applyCanonicalHref(inspectPath());
|
|
45
46
|
}, [inspectPath]);
|
|
46
47
|
/** Resolve DAG run id for inspect (side panel or full page). */
|
|
47
48
|
const resolveDagRunForInspect = useCallback(async () => {
|
|
@@ -70,7 +71,7 @@ export function useRecoveryConsole(params) {
|
|
|
70
71
|
setDagRunId(resolvedDag);
|
|
71
72
|
setObserveLinkStatus("match");
|
|
72
73
|
setObserveLinkMessage(null);
|
|
73
|
-
setObserveUrl(`/
|
|
74
|
+
setObserveUrl(buildInspectHref(`/dag/${encodeURIComponent(resolvedDag)}`));
|
|
74
75
|
return resolvedDag;
|
|
75
76
|
}
|
|
76
77
|
setOpError("尚未找到该任务的 DAG 运行,请稍后再打开检视");
|
|
@@ -97,7 +98,7 @@ export function useRecoveryConsole(params) {
|
|
|
97
98
|
const resolved = await resolveDagRunForInspect();
|
|
98
99
|
if (!resolved)
|
|
99
100
|
return;
|
|
100
|
-
|
|
101
|
+
applyCanonicalHref(buildInspectHref(`/dag/${encodeURIComponent(resolved)}`, "from=recovery"));
|
|
101
102
|
}, [resolveDagRunForInspect]);
|
|
102
103
|
const loadRecoveryRuns = useCallback(async () => {
|
|
103
104
|
setRecoveryRunsLoading(true);
|
|
@@ -107,7 +108,7 @@ export function useRecoveryConsole(params) {
|
|
|
107
108
|
if (id)
|
|
108
109
|
qs.set("taskId", id);
|
|
109
110
|
qs.set("limit", "40");
|
|
110
|
-
const res = await fetch(`/api/operator/v1/recovery/candidates?${qs.toString()}
|
|
111
|
+
const res = await fetch(scopedReadUrl(`/api/operator/v1/recovery/candidates?${qs.toString()}`), { credentials: "same-origin" });
|
|
111
112
|
if (!res.ok) {
|
|
112
113
|
setRecoveryRuns([]);
|
|
113
114
|
return;
|
|
@@ -133,7 +134,7 @@ export function useRecoveryConsole(params) {
|
|
|
133
134
|
}
|
|
134
135
|
setRecoveryNodesLoading(true);
|
|
135
136
|
try {
|
|
136
|
-
const res = await fetch(`/api/dag-runs/${encodeURIComponent(id)}
|
|
137
|
+
const res = await fetch(scopedReadUrl(`/api/dag-runs/${encodeURIComponent(id)}`), {
|
|
137
138
|
credentials: "same-origin",
|
|
138
139
|
});
|
|
139
140
|
if (!res.ok) {
|
|
@@ -253,9 +254,7 @@ export function useRecoveryConsole(params) {
|
|
|
253
254
|
if (!refreshRunStatus)
|
|
254
255
|
return;
|
|
255
256
|
try {
|
|
256
|
-
const res = await fetch(`/api/dag-runs/${encodeURIComponent(runId)}
|
|
257
|
-
credentials: "same-origin",
|
|
258
|
-
});
|
|
257
|
+
const res = await fetch(scopedReadUrl(`/api/dag-runs/${encodeURIComponent(runId)}`), { credentials: "same-origin" });
|
|
259
258
|
if (!res.ok || cancelled)
|
|
260
259
|
return;
|
|
261
260
|
const runBody = (await res.json());
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState } from "react";
|
|
2
2
|
import { isTerminalDagRun } from "../../inspect-split.js";
|
|
3
|
+
import { scopedReadUrl } from "./console-types.js";
|
|
3
4
|
import { summarizeRunProgress, } from "../pages/tasks/run-panel-progress.js";
|
|
4
5
|
const RUN_PROGRESS_POLL_INTERVAL_MS = 5000;
|
|
5
6
|
/**
|
|
@@ -18,7 +19,7 @@ export function useRunProgress(dagRunId) {
|
|
|
18
19
|
let timer;
|
|
19
20
|
const load = async () => {
|
|
20
21
|
try {
|
|
21
|
-
const response = await fetch(`/api/dag-runs/${encodeURIComponent(runId)}
|
|
22
|
+
const response = await fetch(scopedReadUrl(`/api/dag-runs/${encodeURIComponent(runId)}`), { credentials: "same-origin", signal: controller.signal });
|
|
22
23
|
if (!response.ok)
|
|
23
24
|
return;
|
|
24
25
|
const body = (await response.json());
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { ensureUniqueDocumentNames, operatorRequestFits, operatorRequestTooLargeMessage, } from "../prd-file-import.js";
|
|
3
|
-
import { requestId, WORKFLOW_OPTIONS, } from "./console-types.js";
|
|
3
|
+
import { requestId, scopedReadUrl, WORKFLOW_OPTIONS, } from "./console-types.js";
|
|
4
|
+
export async function fetchLedgerSummary(taskId, fetchImpl) {
|
|
5
|
+
const impl = fetchImpl ?? fetch;
|
|
6
|
+
const response = await impl(scopedReadUrl(`/api/tasks/${encodeURIComponent(taskId)}/ledger`), { credentials: "same-origin" });
|
|
7
|
+
if (!response.ok)
|
|
8
|
+
return { ok: false };
|
|
9
|
+
const summary = (await response.json());
|
|
10
|
+
return { ok: true, summary };
|
|
11
|
+
}
|
|
4
12
|
/** Happy Path task wizard: import-prd → interview → contract → dag-confirm →
|
|
5
13
|
* run. Owns task identity, interview projection and contract preflight/apply. */
|
|
6
14
|
export function useTaskWizard(params) {
|
|
@@ -24,16 +32,27 @@ export function useTaskWizard(params) {
|
|
|
24
32
|
const [contractDiffSummary, setContractDiffSummary] = useState(null);
|
|
25
33
|
const [contractApplied, setContractApplied] = useState(false);
|
|
26
34
|
const [spineOk, setSpineOk] = useState(null);
|
|
35
|
+
const [ledgerLoading, setLedgerLoading] = useState(false);
|
|
36
|
+
const [ledgerError, setLedgerError] = useState(null);
|
|
37
|
+
const [ledgerSummary, setLedgerSummary] = useState(null);
|
|
27
38
|
const [contractPreflightKey, setContractPreflightKey] = useState(null);
|
|
28
39
|
const contractPreflightRunning = useRef(false);
|
|
40
|
+
/** Epoch guard for ledger fetches: only the newest request may write wizard
|
|
41
|
+
* state, so a concurrent in-flight preflight response can never overwrite
|
|
42
|
+
* a fresh post-apply result (AC-001). */
|
|
43
|
+
const ledgerFetchEpoch = useRef(0);
|
|
29
44
|
const resetContractLanding = useCallback(() => {
|
|
30
45
|
setContractValidateOk(null);
|
|
31
46
|
setContractValidateSummary(null);
|
|
32
47
|
setContractDiffSummary(null);
|
|
33
48
|
setContractApplied(false);
|
|
34
49
|
setSpineOk(null);
|
|
50
|
+
setLedgerLoading(false);
|
|
51
|
+
setLedgerError(null);
|
|
52
|
+
setLedgerSummary(null);
|
|
35
53
|
setContractPreflightKey(null);
|
|
36
54
|
contractPreflightRunning.current = false;
|
|
55
|
+
ledgerFetchEpoch.current = 0;
|
|
37
56
|
}, []);
|
|
38
57
|
const enterContractStep = useCallback(() => {
|
|
39
58
|
resetContractLanding();
|
|
@@ -56,6 +75,39 @@ export function useTaskWizard(params) {
|
|
|
56
75
|
: "访谈已完成:草稿已绑定完整评估结果");
|
|
57
76
|
}
|
|
58
77
|
}, [enterContractStep, setOpMessage]);
|
|
78
|
+
/** Epoch-guarded shared ledger refresh used by both the contract-step
|
|
79
|
+
* preflight and the post-apply re-GET. `failureSemantics` keeps the
|
|
80
|
+
* preflight five-state behavior (non-ok → empty state, no error) while the
|
|
81
|
+
* post-apply refresh treats a non-ok/network failure as a safe-degrade
|
|
82
|
+
* error per AC-001. */
|
|
83
|
+
const refreshLedgerState = useCallback(async (id, failureSemantics) => {
|
|
84
|
+
const epoch = ++ledgerFetchEpoch.current;
|
|
85
|
+
setLedgerLoading(true);
|
|
86
|
+
setLedgerError(null);
|
|
87
|
+
try {
|
|
88
|
+
const outcome = await fetchLedgerSummary(id);
|
|
89
|
+
if (ledgerFetchEpoch.current !== epoch)
|
|
90
|
+
return;
|
|
91
|
+
if (!outcome.ok) {
|
|
92
|
+
setLedgerSummary(null);
|
|
93
|
+
if (failureSemantics) {
|
|
94
|
+
setLedgerError("ledger 接口不可达(安全降级)");
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
setLedgerSummary(outcome.summary);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
if (ledgerFetchEpoch.current !== epoch)
|
|
102
|
+
return;
|
|
103
|
+
setLedgerError("ledger 接口不可达(安全降级)");
|
|
104
|
+
setLedgerSummary(null);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
if (ledgerFetchEpoch.current === epoch)
|
|
108
|
+
setLedgerLoading(false);
|
|
109
|
+
}
|
|
110
|
+
}, []);
|
|
59
111
|
const runContractPreflight = useCallback(async (force = false) => {
|
|
60
112
|
const id = taskId.trim();
|
|
61
113
|
if (!id)
|
|
@@ -74,6 +126,9 @@ export function useTaskWizard(params) {
|
|
|
74
126
|
setContractDiffSummary(null);
|
|
75
127
|
setSpineOk(null);
|
|
76
128
|
setContractApplied(false);
|
|
129
|
+
// Shared epoch-guarded refresh preserves the five-state semantics:
|
|
130
|
+
// non-ok → empty state, network failure → safe-degrade error.
|
|
131
|
+
await refreshLedgerState(id, false);
|
|
77
132
|
try {
|
|
78
133
|
const validateBody = await runAction("contractValidate", {
|
|
79
134
|
taskId: id,
|
|
@@ -101,6 +156,7 @@ export function useTaskWizard(params) {
|
|
|
101
156
|
contractPreflightKey,
|
|
102
157
|
contractValidateOk,
|
|
103
158
|
isActionSuccess,
|
|
159
|
+
refreshLedgerState,
|
|
104
160
|
runAction,
|
|
105
161
|
setOpMessage,
|
|
106
162
|
summarizeEnvelope,
|
|
@@ -116,6 +172,11 @@ export function useTaskWizard(params) {
|
|
|
116
172
|
return;
|
|
117
173
|
}
|
|
118
174
|
setContractApplied(true);
|
|
175
|
+
// AC-001: re-GET the read-only ledger right after apply succeeds and
|
|
176
|
+
// write the latest summary BEFORE spineCheck/setStep, so the first
|
|
177
|
+
// dag-confirm frame shows post-apply statistics (never a loading/empty
|
|
178
|
+
// flash); refresh failure safe-degrades via ledgerError.
|
|
179
|
+
await refreshLedgerState(id, true);
|
|
119
180
|
const spineBody = await runAction("spineCheck", { taskId: id });
|
|
120
181
|
if (isActionSuccess(spineBody)) {
|
|
121
182
|
setSpineOk(true);
|
|
@@ -125,7 +186,15 @@ export function useTaskWizard(params) {
|
|
|
125
186
|
}
|
|
126
187
|
setSpineOk(false);
|
|
127
188
|
setOpMessage("契约已应用,但 Spine 审计未通过:可重试 Spine");
|
|
128
|
-
}, [
|
|
189
|
+
}, [
|
|
190
|
+
assessmentId,
|
|
191
|
+
isActionSuccess,
|
|
192
|
+
refreshLedgerState,
|
|
193
|
+
runAction,
|
|
194
|
+
setOpMessage,
|
|
195
|
+
setStep,
|
|
196
|
+
taskId,
|
|
197
|
+
]);
|
|
129
198
|
const retrySpineOnly = useCallback(async () => {
|
|
130
199
|
const id = taskId.trim();
|
|
131
200
|
if (!id)
|
|
@@ -282,6 +351,9 @@ export function useTaskWizard(params) {
|
|
|
282
351
|
contractValidateOk,
|
|
283
352
|
contractValidateSummary,
|
|
284
353
|
contractDiffSummary,
|
|
354
|
+
ledgerLoading,
|
|
355
|
+
ledgerError,
|
|
356
|
+
ledgerSummary,
|
|
285
357
|
contractApplied,
|
|
286
358
|
spineOk,
|
|
287
359
|
applyInterviewPayload,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import { scopedReadUrl } from "../app/console-types.js";
|
|
2
3
|
/** Night board data plane: schedules list + doctor report + task pool +
|
|
3
4
|
* foreground aux-ticker projection, loaded together on mount/refresh. */
|
|
4
5
|
export function useNightBoard(params) {
|
|
@@ -16,10 +17,12 @@ export function useNightBoard(params) {
|
|
|
16
17
|
setLoadError(null);
|
|
17
18
|
try {
|
|
18
19
|
const [jobsRes, doctorRes, tasksRes, tickerRes] = await Promise.all([
|
|
19
|
-
fetch("/api/night-jobs", { credentials: "same-origin" }),
|
|
20
|
-
fetch("/api/night-jobs/doctor", {
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
fetch(scopedReadUrl("/api/night-jobs"), { credentials: "same-origin" }),
|
|
21
|
+
fetch(scopedReadUrl("/api/night-jobs/doctor"), {
|
|
22
|
+
credentials: "same-origin",
|
|
23
|
+
}),
|
|
24
|
+
fetch(scopedReadUrl("/api/tasks"), { credentials: "same-origin" }),
|
|
25
|
+
fetch(scopedReadUrl("/api/operator/v1/night-aux-ticker"), {
|
|
23
26
|
credentials: "same-origin",
|
|
24
27
|
}),
|
|
25
28
|
]);
|
|
@@ -59,7 +62,7 @@ export function useNightBoard(params) {
|
|
|
59
62
|
}, [refreshBoard]);
|
|
60
63
|
const onMorningReport = useCallback(async () => {
|
|
61
64
|
try {
|
|
62
|
-
const res = await fetch("/api/night-jobs/morning", {
|
|
65
|
+
const res = await fetch(scopedReadUrl("/api/night-jobs/morning"), {
|
|
63
66
|
credentials: "same-origin",
|
|
64
67
|
});
|
|
65
68
|
if (!res.ok) {
|
|
@@ -3,6 +3,7 @@ import { humanGateCardFromEventPayload, mergeHumanGateCardState, } from "../../c
|
|
|
3
3
|
import { mergeOperationCardState, operationFromEventPayload, } from "../../chat/operation-card.js";
|
|
4
4
|
import { requestId } from "./format.js";
|
|
5
5
|
import { parseAssistantContent } from "../../chat/assistant-content.js";
|
|
6
|
+
import { upsertCompactionMessage } from "./compaction-message.js";
|
|
6
7
|
/** Shared handler for a single parsed Chat SSE event (M1 reconnect).
|
|
7
8
|
*
|
|
8
9
|
* Used by BOTH the inline prompt POST SSE and the background reconnect
|
|
@@ -10,7 +11,9 @@ import { parseAssistantContent } from "../../chat/assistant-content.js";
|
|
|
10
11
|
* original request would have. `assistantId` is the in-flight assistant
|
|
11
12
|
* message id to patch for streaming text; reconnect creates one if none. */
|
|
12
13
|
export function createChatSseEventApplier(deps) {
|
|
13
|
-
const { refs, onReconcile, setError, setInterview, setHumanGateCards, setArtifactCards, setStreaming, setTurnPhase = () => undefined, setDeferredTurn = () => undefined, setStreamingAssistantId, setUnread, setOperationCards, setUsage, setMessages, setToolCalls, } = deps;
|
|
14
|
+
const { refs, onReconcile, setError, setInterview, setHumanGateCards, setArtifactCards, setStreaming, setTurnPhase = () => undefined, setDeferredTurn = () => undefined, setDeferredTurns = () => undefined, setStreamingAssistantId, setUnread, setOperationCards, setUsage, onTelemetryRefresh, setMessages, setToolCalls, setPendingQuestion = () => undefined, setTodos = () => undefined, } = deps;
|
|
15
|
+
let queueOwner;
|
|
16
|
+
let queueRevision = -1;
|
|
14
17
|
return (eventName, data, assistantId, options) => {
|
|
15
18
|
if (options?.generation !== undefined &&
|
|
16
19
|
refs.sessionGenerationRef.current !== options.generation)
|
|
@@ -42,6 +45,26 @@ export function createChatSseEventApplier(deps) {
|
|
|
42
45
|
const payload = data.data && typeof data.data === "object"
|
|
43
46
|
? data.data
|
|
44
47
|
: data;
|
|
48
|
+
if (eventName === "todo_updated" || eventName === "todo_cleared") {
|
|
49
|
+
const todos = Array.isArray(payload.todos) ? payload.todos : [];
|
|
50
|
+
setTodos(todos.filter((item) => Boolean(item) && typeof item === "object" && typeof item.content === "string" && (item.status === "pending" || item.status === "in_progress" || item.status === "completed")));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if ((eventName === "question_requested" ||
|
|
54
|
+
eventName === "question_resolved" ||
|
|
55
|
+
eventName === "question_cancelled") &&
|
|
56
|
+
payload.question &&
|
|
57
|
+
typeof payload.question === "object") {
|
|
58
|
+
const question = payload.question;
|
|
59
|
+
if (typeof question.requestId === "string" &&
|
|
60
|
+
typeof question.sessionId === "string" &&
|
|
61
|
+
question.sessionId === refs.sessionRef.current?.sessionId) {
|
|
62
|
+
setPendingQuestion(eventName === "question_requested" && question.status === "pending"
|
|
63
|
+
? question
|
|
64
|
+
: null);
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
45
68
|
const turnId = typeof data.turnId === "string" ? data.turnId : undefined;
|
|
46
69
|
const eventSeq = typeof data.seq === "number" && Number.isFinite(data.seq)
|
|
47
70
|
? data.seq
|
|
@@ -51,13 +74,20 @@ export function createChatSseEventApplier(deps) {
|
|
|
51
74
|
: Date.now();
|
|
52
75
|
if (eventName === "queue_update") {
|
|
53
76
|
const revision = typeof payload.revision === "number" ? payload.revision : -1;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
77
|
+
const owner = refs.sessionRef.current?.sessionId;
|
|
78
|
+
if (owner !== queueOwner) {
|
|
79
|
+
queueOwner = owner;
|
|
80
|
+
queueRevision = -1;
|
|
81
|
+
}
|
|
82
|
+
if (revision < queueRevision)
|
|
83
|
+
return;
|
|
84
|
+
queueRevision = revision;
|
|
85
|
+
setDeferredTurn(payload.deferredTurn && typeof payload.deferredTurn === "object"
|
|
86
|
+
? payload.deferredTurn
|
|
87
|
+
: null);
|
|
88
|
+
if (Array.isArray(payload.deferredTurns)) {
|
|
89
|
+
setDeferredTurns(payload.deferredTurns);
|
|
90
|
+
}
|
|
61
91
|
return;
|
|
62
92
|
}
|
|
63
93
|
if (eventName === "user_message") {
|
|
@@ -84,6 +114,17 @@ export function createChatSseEventApplier(deps) {
|
|
|
84
114
|
}
|
|
85
115
|
return;
|
|
86
116
|
}
|
|
117
|
+
if (eventName === "compact" ||
|
|
118
|
+
eventName === "compaction_start" ||
|
|
119
|
+
eventName === "compaction_end") {
|
|
120
|
+
setMessages((messages) => upsertCompactionMessage(messages, {
|
|
121
|
+
eventId: typeof data.eventId === "string" ? data.eventId : undefined,
|
|
122
|
+
kind: eventName,
|
|
123
|
+
at: new Date(eventAt).toISOString(),
|
|
124
|
+
data: payload,
|
|
125
|
+
}));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
87
128
|
if (turnId &&
|
|
88
129
|
(eventName === "agent_start" || eventName === "message_update") &&
|
|
89
130
|
refs.settledTurnIdsRef.current.has(turnId)) {
|
|
@@ -186,6 +227,16 @@ export function createChatSseEventApplier(deps) {
|
|
|
186
227
|
refs.settledTurnIdsRef.current.delete(turnId);
|
|
187
228
|
refs.activeTurnIdRef.current = turnId;
|
|
188
229
|
}
|
|
230
|
+
if (refs.stopRequestedRef.current &&
|
|
231
|
+
(!turnId ||
|
|
232
|
+
turnId === refs.stoppingTurnIdRef.current ||
|
|
233
|
+
turnId === refs.activeTurnIdRef.current)) {
|
|
234
|
+
refs.streamingRef.current = false;
|
|
235
|
+
setStreaming(false);
|
|
236
|
+
setTurnPhase("idle");
|
|
237
|
+
setStreamingAssistantId(null);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
189
240
|
refs.streamingRef.current = true;
|
|
190
241
|
setStreaming(true);
|
|
191
242
|
setTurnPhase("running");
|
|
@@ -200,9 +251,22 @@ export function createChatSseEventApplier(deps) {
|
|
|
200
251
|
// (M4.4). It must not prematurely close the visible turn.
|
|
201
252
|
if (eventName === "agent_end")
|
|
202
253
|
return;
|
|
254
|
+
const pending = refs.pendingSubmissionRef.current;
|
|
255
|
+
if (pending && turnId && pending.acceptedTurnId === turnId) {
|
|
256
|
+
refs.releasedClientRequestIdsRef.current.add(pending.clientRequestId);
|
|
257
|
+
refs.pendingSubmissionRef.current = null;
|
|
258
|
+
refs.submitInFlightRef.current = false;
|
|
259
|
+
}
|
|
260
|
+
if (!turnId ||
|
|
261
|
+
turnId === refs.stoppingTurnIdRef.current ||
|
|
262
|
+
turnId === refs.activeTurnIdRef.current) {
|
|
263
|
+
refs.stoppingTurnIdRef.current = null;
|
|
264
|
+
refs.stopRequestedRef.current = false;
|
|
265
|
+
}
|
|
203
266
|
if (turnId === refs.activeTurnIdRef.current)
|
|
204
267
|
refs.activeTurnIdRef.current = null;
|
|
205
268
|
refs.streamingRef.current = false;
|
|
269
|
+
refs.assistantIdRef.current = null;
|
|
206
270
|
setStreaming(false);
|
|
207
271
|
setTurnPhase("idle");
|
|
208
272
|
setStreamingAssistantId(null);
|
|
@@ -236,10 +300,21 @@ export function createChatSseEventApplier(deps) {
|
|
|
236
300
|
return true;
|
|
237
301
|
return assistantOwnsTools;
|
|
238
302
|
}));
|
|
303
|
+
if (!replay)
|
|
304
|
+
onTelemetryRefresh?.();
|
|
239
305
|
return;
|
|
240
306
|
}
|
|
241
307
|
if (eventName === "operation-ref" || eventName === "operation-status") {
|
|
242
308
|
const operation = operationFromEventPayload(payload);
|
|
309
|
+
// R9: tombstones are session-scoped. The same operation may be linked
|
|
310
|
+
// to another session, but delayed frames cannot resurrect it here.
|
|
311
|
+
const currentSessionId = refs.sessionRef.current?.sessionId;
|
|
312
|
+
if (operation &&
|
|
313
|
+
currentSessionId &&
|
|
314
|
+
refs.unlinkedOperationIdsRef.current
|
|
315
|
+
.get(currentSessionId)
|
|
316
|
+
?.has(operation.operationId))
|
|
317
|
+
return;
|
|
243
318
|
if (operation) {
|
|
244
319
|
const toolCallId = typeof payload.toolCallId === "string"
|
|
245
320
|
? payload.toolCallId
|
|
@@ -256,6 +331,23 @@ export function createChatSseEventApplier(deps) {
|
|
|
256
331
|
}
|
|
257
332
|
return;
|
|
258
333
|
}
|
|
334
|
+
if (eventName === "operation-unlinked") {
|
|
335
|
+
// R8 AC-001/AC-R8-001: durable tombstone — the server persisted an
|
|
336
|
+
// operation-unlinked event that OVERSHADOWS any earlier operation-ref.
|
|
337
|
+
// Drop the card from the projection; replay/recovery can never
|
|
338
|
+
// resurrect it because the tombstone is appended after the ref in
|
|
339
|
+
// the same ring.
|
|
340
|
+
const operationId = typeof payload.operationId === "string" ? payload.operationId : "";
|
|
341
|
+
const currentSessionId = refs.sessionRef.current?.sessionId;
|
|
342
|
+
if (operationId && currentSessionId) {
|
|
343
|
+
const sessionTombstones = refs.unlinkedOperationIdsRef.current.get(currentSessionId) ??
|
|
344
|
+
new Set();
|
|
345
|
+
sessionTombstones.add(operationId);
|
|
346
|
+
refs.unlinkedOperationIdsRef.current.set(currentSessionId, sessionTombstones);
|
|
347
|
+
setOperationCards((current) => current.filter((card) => card.operationId !== operationId));
|
|
348
|
+
}
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
259
351
|
if (eventName === "usage") {
|
|
260
352
|
if (replay)
|
|
261
353
|
return;
|
|
@@ -451,6 +543,47 @@ export function createChatSseEventApplier(deps) {
|
|
|
451
543
|
});
|
|
452
544
|
}
|
|
453
545
|
else if (eventName === "error") {
|
|
546
|
+
const candidate = payload.providerError && typeof payload.providerError === "object"
|
|
547
|
+
? payload.providerError
|
|
548
|
+
: undefined;
|
|
549
|
+
if (candidate &&
|
|
550
|
+
typeof candidate.message === "string" &&
|
|
551
|
+
typeof candidate.raw === "string" &&
|
|
552
|
+
(candidate.category === "authentication" ||
|
|
553
|
+
candidate.category === "context-overflow" ||
|
|
554
|
+
candidate.category === "rate-limit" ||
|
|
555
|
+
candidate.category === "provider")) {
|
|
556
|
+
// Session hydration already restores the durable card; retained replay
|
|
557
|
+
// must not add a second synthetic row on top of it.
|
|
558
|
+
if (replay)
|
|
559
|
+
return;
|
|
560
|
+
const providerError = candidate;
|
|
561
|
+
const errorContent = parseAssistantContent(payload.content);
|
|
562
|
+
const id = turnId && eventSeq !== undefined
|
|
563
|
+
? `provider-error-${turnId}-${eventSeq}`
|
|
564
|
+
: requestId("provider-error");
|
|
565
|
+
setMessages((messages) => {
|
|
566
|
+
if (messages.some((message) => message.id === id))
|
|
567
|
+
return messages;
|
|
568
|
+
const activeAlreadyOwnsContent = messages.some((message) => message.id === resolvedAssistantId &&
|
|
569
|
+
Boolean(message.content?.length));
|
|
570
|
+
return [
|
|
571
|
+
...messages,
|
|
572
|
+
{
|
|
573
|
+
id,
|
|
574
|
+
role: "assistant",
|
|
575
|
+
kind: "provider-error",
|
|
576
|
+
text: providerError.message,
|
|
577
|
+
createdAt: eventAt,
|
|
578
|
+
providerError,
|
|
579
|
+
...(!activeAlreadyOwnsContent && errorContent
|
|
580
|
+
? { content: errorContent }
|
|
581
|
+
: {}),
|
|
582
|
+
},
|
|
583
|
+
];
|
|
584
|
+
});
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
454
587
|
setError(typeof payload.message === "string" ? payload.message : "chat error");
|
|
455
588
|
}
|
|
456
589
|
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
function finiteToken(value) {
|
|
2
|
+
return typeof value === "number" && Number.isFinite(value)
|
|
3
|
+
? Math.max(0, Math.floor(value))
|
|
4
|
+
: undefined;
|
|
5
|
+
}
|
|
6
|
+
/** Project legacy final snapshots and lifecycle events to one stable row. */
|
|
7
|
+
export function compactionMessageFromEvent(item) {
|
|
8
|
+
const data = item.data && typeof item.data === "object"
|
|
9
|
+
? item.data
|
|
10
|
+
: {};
|
|
11
|
+
const eventId = typeof item.eventId === "string" ? item.eventId : undefined;
|
|
12
|
+
const compactionId = typeof data.compactionId === "string" && data.compactionId
|
|
13
|
+
? data.compactionId
|
|
14
|
+
: eventId;
|
|
15
|
+
if (!compactionId)
|
|
16
|
+
return undefined;
|
|
17
|
+
const status = data.status === "running" ||
|
|
18
|
+
data.status === "completed" ||
|
|
19
|
+
data.status === "failed" ||
|
|
20
|
+
data.status === "aborted"
|
|
21
|
+
? data.status
|
|
22
|
+
: "completed";
|
|
23
|
+
const reason = data.reason === "manual" ||
|
|
24
|
+
data.reason === "threshold" ||
|
|
25
|
+
data.reason === "overflow"
|
|
26
|
+
? data.reason
|
|
27
|
+
: "manual";
|
|
28
|
+
const summary = typeof data.summary === "string" ? data.summary : "";
|
|
29
|
+
const errorMessage = typeof data.errorMessage === "string" ? data.errorMessage : undefined;
|
|
30
|
+
return {
|
|
31
|
+
id: `compaction-${compactionId}`,
|
|
32
|
+
role: "assistant",
|
|
33
|
+
kind: "compaction",
|
|
34
|
+
text: summary ||
|
|
35
|
+
errorMessage ||
|
|
36
|
+
(status === "completed"
|
|
37
|
+
? "历史上下文已精简,关键决策与当前任务已保留。"
|
|
38
|
+
: ""),
|
|
39
|
+
createdAt: Date.parse(item.at ?? "") || Date.now(),
|
|
40
|
+
compaction: {
|
|
41
|
+
compactionId,
|
|
42
|
+
status,
|
|
43
|
+
reason,
|
|
44
|
+
willRetry: data.willRetry === true,
|
|
45
|
+
...(finiteToken(data.tokensBefore) === undefined
|
|
46
|
+
? {}
|
|
47
|
+
: { tokensBefore: finiteToken(data.tokensBefore) }),
|
|
48
|
+
...(finiteToken(data.estimatedTokensAfter) === undefined
|
|
49
|
+
? {}
|
|
50
|
+
: { estimatedTokensAfter: finiteToken(data.estimatedTokensAfter) }),
|
|
51
|
+
...(typeof data.firstKeptEntryId === "string" && data.firstKeptEntryId
|
|
52
|
+
? { firstKeptEntryId: data.firstKeptEntryId }
|
|
53
|
+
: {}),
|
|
54
|
+
...(errorMessage ? { errorMessage } : {}),
|
|
55
|
+
...(eventId ? { eventId } : {}),
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function upsertCompactionMessage(messages, item) {
|
|
60
|
+
const next = compactionMessageFromEvent(item);
|
|
61
|
+
if (!next)
|
|
62
|
+
return messages;
|
|
63
|
+
const existing = messages.find((message) => message.id === next.id);
|
|
64
|
+
const result = existing
|
|
65
|
+
? messages.map((message) => message.id === next.id
|
|
66
|
+
? {
|
|
67
|
+
...message,
|
|
68
|
+
...next,
|
|
69
|
+
createdAt: Math.min(message.createdAt, next.createdAt),
|
|
70
|
+
text: next.text || message.text,
|
|
71
|
+
compaction: {
|
|
72
|
+
...message.compaction,
|
|
73
|
+
...next.compaction,
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
: message)
|
|
77
|
+
: [...messages, next];
|
|
78
|
+
return result.sort((a, b) => a.createdAt - b.createdAt);
|
|
79
|
+
}
|