@tea-agent/loop-agent 0.42.0-next.1 → 0.42.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/AGENTS.md +1 -1
- package/CHANGELOG.md +321 -1
- package/dist/application/dag/generate-task-dag.js +75 -19
- package/dist/application/dag/run-dag.js +41 -0
- package/dist/application/task-lifecycle/advance.js +24 -5
- package/dist/application/task-lifecycle/observe.js +171 -17
- package/dist/application/task-lifecycle/plan-transitions.js +42 -7
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +7 -0
- package/dist/cli/program.js +6 -1
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/dag-rerun-task.js +2 -0
- package/dist/commands/init.js +27 -1
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +4010 -78
- package/dist/executors/pi-executor.js +15 -4
- package/dist/executors/pi-extension-resolver.js +14 -2
- package/dist/executors/pi-read-budget-policy.js +239 -0
- package/dist/executors/shell-executor.js +978 -188
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/infrastructure/console/operation-store.js +226 -6
- package/dist/shared/dag-failure-category.js +12 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +7 -0
- package/dist/task/config-types.js +105 -5
- package/dist/task/contract/adopt.js +4 -0
- package/dist/task/contract/import-revision.js +4 -0
- package/dist/task/contract/project.js +3 -0
- package/dist/task/contract/schema.js +2 -1
- package/dist/task/frontend-project-capability.js +203 -20
- package/dist/task/runtime.js +5 -2
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +64 -25
- package/dist/task/source-prepare/prepare.js +126 -21
- package/dist/task/source-prepare/semantic-intake.js +6 -2
- package/dist/task/source-references.js +22 -1
- package/dist/worker/console/chat/chat-ui-policy.js +4 -2
- package/dist/worker/console/chat/model-resolver.js +7 -4
- package/dist/worker/console/chat/pi-runtime.js +258 -11
- package/dist/worker/console/chat/repo-browser.js +10 -3
- package/dist/worker/console/chat/routes.js +165 -71
- package/dist/worker/console/chat/sdd-data-alignment.js +222 -0
- package/dist/worker/console/chat/session-catalog.js +2 -0
- package/dist/worker/console/chat/session-store.js +159 -70
- package/dist/worker/console/chat/shortcuts.js +19 -3
- package/dist/worker/console/chat/turn-process.js +111 -56
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/index.js +3 -0
- package/dist/worker/console/operator-actions.js +169 -7
- package/dist/worker/console/pi-readiness.js +3 -2
- package/dist/worker/console/server.js +23 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-DZZ8m3PO.js → abnfDiagram-N423BO3Z-CcS17TBr.js} +1 -1
- package/dist/worker/console/static/assets/{arc-D6PvaVd-.js → arc-COptKq2S.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-B_OTOiI8.js → architectureDiagram-T3A2C74G-h4LKMHjP.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Bv6rqHBg.js → blockDiagram-VBNYF7ZC-COA1MOH0.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B8eHr0oz.js → c4Diagram-5PPSVZJV-DJUf0QPm.js} +1 -1
- package/dist/worker/console/static/assets/channel-DdBCaOJ6.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DYwR0im2.js → chunk-2GRJ4B5K-mtWfKrUX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-D2WPGqXt.js → chunk-2Q5K7J3B-B8pXsxDQ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CQISJ_I7.js → chunk-5RXB4S5H-ipKzByl1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C0o2Du1e.js → chunk-5VM5RSS4-DYi3Ald_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-4f8kr-U7.js → chunk-6Q2QTUOP-DQtGYoty.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D_OWvXzX.js → chunk-GF5L2VYU-BU0qS2YV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CasdPz5X.js → chunk-JWPE2WC7-DVH9dIGE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Cj8lRrla.js → chunk-KBJHAD2P-Dt60SmgM.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CUvD1FSd.js → chunk-RYQCIY6F-BoaUGuEY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CqLw_eqB.js → chunk-XXDRQBXY-_WHFDTp2.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DZFra1GO.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DZFra1GO.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-ku-WqJPx.js → cose-bilkent-JH36ORCC-BxkbTIRd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-Bq_PYDhl.js → cynefin-VYW2F7L2-Bf2UVnoG.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BiGZV641.js → cynefinDiagram-MW4NZA55-Bo23q1J_.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-C4JRlglF.js → dagre-VZM6K2ZE-Dd_UU49i.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-B0dNeWYG.js → diagram-7IWD3JNH-ebUa1a9y.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Cj35YvYM.js → diagram-B4RE2ZJO-BucthU8r.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-uzQoJ2-8.js → diagram-LBJQPF4R-BgqNpAkm.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D1a-Buoz.js → diagram-Q27KOJAE-DC4q5NGa.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-XjRrLRSs.js → diagram-UB23O5K3-CBqCaMeb.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Da_O24cW.js → ebnfDiagram-BXEA7PRR-CGtnbQ3-.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BLJ8jrYU.js → erDiagram-JOGREHBK-CG3LUao5.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-B9GrLjM0.js → flowDiagram-UKHOOZJN-D2ZVgoFS.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CeJ0TqiK.js → ganttDiagram-PKOTCBZU-DoYFZiKt.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js → gitGraphDiagram-DS77QQ5N-CKoP1s6j.js} +1 -1
- package/dist/worker/console/static/assets/index-CAZ2fC_X.css +1 -0
- package/dist/worker/console/static/assets/index-vbTcFnFs.js +449 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DB26i3d8.js → infoDiagram-6WML65LV-Duofv8p2.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BOGRyeZT.js → ishikawaDiagram-WSZJBQD7-D2nlCkA1.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CZRoBO_6.js → journeyDiagram-NVQOT4AX-Dd4IHdus.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-qlWhJyoB.js → kanban-definition-27J2QSJJ-Bi20AOdt.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CfUiDDB3.js → linear-D59YJ9kB.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BQe6fpqj.js → mermaid.core-Bl13LpNa.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-zFzWHw64.js → mindmap-definition-FAOFIHXS-C6DLP5eY.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DFGUqjZO.js → pegDiagram-VL7TDLO6-BjO4cy64.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BB5i0l8Z.js → pieDiagram-7S7Q4E2Y-D7i2qZTG.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-obbVZ_X5.js → quadrantDiagram-CIZ2JOQS-Dx8o_h0y.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BwdaNzc1.js → railroadDiagram-AXF67PYL-B55orI_t.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CkTJaugh.js → requirementDiagram-LRYGKXZP-zWaehp6f.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BwJ-hIgq.js → sankeyDiagram-W5VNT64P-CcA-pjvD.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BzSgmm7w.js → sequenceDiagram-SI44F4Z6-BOFbzNHI.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-Bn3oj5Q1.js → sizeCapture-X5ZJPWSS-BjAejah1.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-dxhGtL8K.js → stateDiagram-OKZ733FA-DXYwxJwZ.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Clg3V9t1.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BLpEInya.js → swimlanes-SLNWSIFB-OYiOch8n.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DX1dxAAW.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ChJGYcXy.js → timeline-definition-Z64GVDOM-cZfH3nmU.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DK-qjRer.js → vennDiagram-T6HMQDX7-BDE7b3E1.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-3qeaWAg-.js → wardleyDiagram-T6FBY63Y-kyyGy9WJ.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CootlyP9.js → xychartDiagram-ELKLHX3M-CJj6VTog.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +19 -8
- package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +15 -7
- package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +258 -23
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +112 -4
- package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -4
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
- package/dist/worker/console/static-src/shell/console-update-reload.js +36 -9
- package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
- package/dist/worker/console/workspace-context.js +115 -1
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/observe/routes.js +4 -0
- package/dist/worker/observe/static/constants.js +22 -22
- package/dist/worker/observe/static/dag-context-reason-labels.js +19 -0
- package/dist/worker/observe/static/dag-helpers.js +4 -2
- package/dist/worker/observe/static/dag-history-labels.js +1 -0
- package/dist/worker/observe/static/dag-inspector-humanize.d.ts +16 -0
- package/dist/worker/observe/static/dag-inspector-humanize.js +339 -0
- package/dist/worker/observe/static/dag-node-purpose.js +13 -8
- package/dist/worker/observe/static/index.html +4 -4
- package/dist/worker/observe/static/inspect-workspace.js +23 -0
- package/dist/worker/observe/static/kpi.js +2 -2
- package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
- package/dist/worker/observe/static/operator-chrome.js +37 -23
- package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
- package/dist/worker/observe/static/relations.js +7 -7
- package/dist/worker/observe/static/router.d.ts +12 -1
- package/dist/worker/observe/static/router.js +48 -6
- package/dist/worker/observe/static/run-processing.js +4 -2
- package/dist/worker/observe/static/shell-chrome.js +2 -2
- package/dist/worker/observe/static/state.js +5 -0
- package/dist/worker/observe/static/styles.css +231 -0
- package/dist/worker/observe/static/views/dag-graph.js +1 -1
- package/dist/worker/observe/static/views/dag-inspector.js +336 -181
- package/dist/worker/observe/static/views/dag.js +26 -14
- package/dist/worker/observe/static/views/dags.js +3 -1
- package/dist/worker/observe/static/views/dashboard.js +14 -6
- package/dist/worker/observe/static/views/pool.js +7 -2
- package/dist/worker/observe/static/views/run.js +12 -3
- package/dist/worker/observe/static/views/session-timeline.js +74 -13
- package/dist/worker/observe/static/views/task.js +7 -2
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +215 -28
- package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
- package/dist/workflows/dag/backend-test-plan-protocol.js +210 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +172 -25
- package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
- package/dist/workflows/dag/backend-test-writer-completeness.js +104 -12
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/dag-retry-schema.js +138 -0
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +400 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +1237 -192
- package/dist/workflows/dag/frontend-plan-render.js +2 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +256 -350
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +226 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +21 -10
- package/dist/workflows/dag/frontend-recovery-run.js +166 -34
- package/dist/workflows/dag/frontend-repair.js +1 -432
- package/dist/workflows/dag/frontend-review-context.js +261 -15
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +941 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +419 -0
- package/dist/workflows/dag/frontend-shape.js +435 -0
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +252 -25
- package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
- package/dist/workflows/dag/frontend-writer-admission.js +319 -0
- package/dist/workflows/dag/frontend-writer-status.js +256 -0
- package/dist/workflows/dag/init-hybrid.js +1031 -564
- package/dist/workflows/dag/interrupt-request.js +7 -0
- package/dist/workflows/dag/node-execution.js +854 -4
- package/dist/workflows/dag/prompt.js +70 -3
- package/dist/workflows/dag/recovery-lease.js +170 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +315 -1
- package/dist/workflows/dag/rerun-plan.js +17 -6
- package/dist/workflows/dag/rerun-run.js +44 -6
- package/dist/workflows/dag/rerun-task.js +251 -13
- package/dist/workflows/dag/retry-policy.js +230 -104
- package/dist/workflows/dag/runner.js +580 -124
- package/dist/workflows/dag/scheduler.js +133 -20
- package/dist/workflows/dag/types.js +244 -16
- package/dist/workflows/dag/validate.js +28 -12
- package/docs/examples/README.md +5 -0
- package/docs/init-surface.manifest.json +30 -12
- package/docs/skills/vetted-skill-registry.md +4 -2
- package/docs/templates/README.md +2 -0
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +1 -1
- package/docs/templates/backend-test-dag.json +25 -20
- package/docs/templates/frontend-implementation-contract.schema.json +4 -1
- package/docs/templates/frontend-implementation-dag.json +89 -0
- package/docs/templates/spec-registry.schema.json +45 -0
- package/harness.json +1 -1
- package/package.json +4 -3
- package/skills/frontend-bounded-implement/SKILL.md +15 -14
- package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
- package/skills/frontend-contract/SKILL.md +23 -0
- package/skills/frontend-contract/references/contract-protocol.md +34 -0
- package/skills/frontend-design-review/SKILL.md +22 -41
- package/skills/frontend-plan/SKILL.md +26 -0
- package/skills/frontend-plan/references/decision-contract.md +37 -0
- package/skills/frontend-plan/references/design-decisions.md +17 -0
- package/skills/frontend-review/SKILL.md +20 -15
- package/skills/frontend-review/references/review-findings.md +6 -7
- package/skills/frontend-scout/SKILL.md +25 -0
- package/skills/frontend-scout/references/design-evidence.md +16 -0
- package/skills/frontend-scout/references/scout-evidence.md +23 -0
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +1 -0
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/worker/console/static/assets/channel-BU5gOilw.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +0 -1
- package/dist/worker/console/static/assets/index-H9rFJiGL.css +0 -1
- package/dist/worker/console/static/assets/index-xwu9GxEc.js +0 -451
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +0 -8
- package/skills/frontend-implementation/SKILL.md +0 -52
- package/skills/frontend-implementation/references/code-standards.md +0 -33
- package/skills/frontend-implementation/references/design-spec.md +0 -56
- package/skills/frontend-implementation/references/node-contracts.md +0 -31
|
@@ -7,7 +7,7 @@ import { attachRuntimeSnapshotController } from "./useRuntimeSnapshot.js";
|
|
|
7
7
|
/** Composer state: input text, @file palette, image attachments, /shortcuts,
|
|
8
8
|
* IME composition guards, auto-grow and server-side draft sync. */
|
|
9
9
|
export function useComposer(params) {
|
|
10
|
-
const { origin, sessionId, refs, setError } = params;
|
|
10
|
+
const { origin, sessionId, refs, setError, onUnrecoverableSession } = params;
|
|
11
11
|
const [input, setInputState] = useState("");
|
|
12
12
|
const [fileOptions, setFileOptions] = useState([]);
|
|
13
13
|
const [pendingImages, setPendingImages] = useState([]);
|
|
@@ -28,6 +28,7 @@ export function useComposer(params) {
|
|
|
28
28
|
}, []);
|
|
29
29
|
const [shortcutIndex, setShortcutIndex] = useState(0);
|
|
30
30
|
const [dynamicCommands, setDynamicCommands] = useState([]);
|
|
31
|
+
const [commandCapabilities, setCommandCapabilities] = useState({ sddBingo: false });
|
|
31
32
|
const [dynamicCommandsLoading, setDynamicCommandsLoading] = useState(false);
|
|
32
33
|
const [dynamicCommandsError, setDynamicCommandsError] = useState(null);
|
|
33
34
|
const [commandsEpoch, setCommandsEpoch] = useState(0);
|
|
@@ -74,6 +75,7 @@ export function useComposer(params) {
|
|
|
74
75
|
commandsSessionRef.current = sessionId;
|
|
75
76
|
commandsGenerationRef.current += 1;
|
|
76
77
|
setDynamicCommands([]);
|
|
78
|
+
setCommandCapabilities({ sddBingo: false });
|
|
77
79
|
setDynamicCommandsError(null);
|
|
78
80
|
setDynamicCommandsLoading(false);
|
|
79
81
|
}, [sessionId]);
|
|
@@ -122,6 +124,7 @@ export function useComposer(params) {
|
|
|
122
124
|
if (!isCurrent())
|
|
123
125
|
return;
|
|
124
126
|
setDynamicCommands(items);
|
|
127
|
+
setCommandCapabilities({ sddBingo: body?.capabilities?.sddBingo === true });
|
|
125
128
|
// Compact server fail-soft / partial-source warning; local Operator/Web still merge.
|
|
126
129
|
const warning = typeof body?.warning === "string" ? body.warning.trim() : "";
|
|
127
130
|
setDynamicCommandsError(warning ? warning.slice(0, 160) : null);
|
|
@@ -131,6 +134,7 @@ export function useComposer(params) {
|
|
|
131
134
|
return;
|
|
132
135
|
setDynamicCommandsError("动态命令加载失败");
|
|
133
136
|
setDynamicCommands([]);
|
|
137
|
+
setCommandCapabilities({ sddBingo: false });
|
|
134
138
|
}
|
|
135
139
|
finally {
|
|
136
140
|
if (isCurrent()) {
|
|
@@ -227,7 +231,7 @@ export function useComposer(params) {
|
|
|
227
231
|
}, 400);
|
|
228
232
|
return () => window.clearTimeout(timer);
|
|
229
233
|
}, [origin, sessionId, input]);
|
|
230
|
-
const mergedCommands = useMemo(() => mergeSlashCommands(dynamicCommands), [dynamicCommands]);
|
|
234
|
+
const mergedCommands = useMemo(() => mergeSlashCommands(dynamicCommands, commandCapabilities), [dynamicCommands, commandCapabilities]);
|
|
231
235
|
const filteredShortcuts = useMemo(() => input.startsWith("/")
|
|
232
236
|
? filterSlashCommands(mergedCommands, input)
|
|
233
237
|
: [], [input, mergedCommands]);
|
|
@@ -258,8 +262,13 @@ export function useComposer(params) {
|
|
|
258
262
|
// origin + sessionId + refs in a writable file; the snapshot store is
|
|
259
263
|
// consumed by ChatComposer / panels / runtime-context popover).
|
|
260
264
|
useEffect(() => {
|
|
261
|
-
attachRuntimeSnapshotController({
|
|
262
|
-
|
|
265
|
+
attachRuntimeSnapshotController({
|
|
266
|
+
origin,
|
|
267
|
+
sessionId,
|
|
268
|
+
refs,
|
|
269
|
+
onUnrecoverableSession,
|
|
270
|
+
});
|
|
271
|
+
}, [origin, sessionId, refs, onUnrecoverableSession]);
|
|
263
272
|
return {
|
|
264
273
|
input,
|
|
265
274
|
setInput,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { formatModelRef, mergeSessionRuntime, sessionRuntimeMatches, } from "../../chat/runtime-selection.js";
|
|
3
3
|
import { confirmationToken } from "./format.js";
|
|
4
|
-
import { mergeRuntimeContextWithSnapshot, runtimeSelectionFromSnapshot, } from "./runtime-snapshot-store.js";
|
|
4
|
+
import { isLostChatSessionError, mergeRuntimeContextWithSnapshot, runtimeSelectionFromSnapshot, } from "./runtime-snapshot-store.js";
|
|
5
5
|
import { buildToolCatalog } from "./tools-catalog.js";
|
|
6
6
|
import { adoptMutationSnapshot, dispatchRuntimeSnapshot, getRuntimeSnapshotState, refreshRuntimeSnapshot, settleMutationLifecycle, useRuntimeSnapshot, } from "./useRuntimeSnapshot.js";
|
|
7
7
|
/**
|
|
@@ -14,7 +14,7 @@ let mutationSeq = 0;
|
|
|
14
14
|
/** Capabilities catalog, model / thinking-level switcher, runtime-context
|
|
15
15
|
* popover and the outside-click dismissal for both transient menus. */
|
|
16
16
|
export function useRuntimeControls(params) {
|
|
17
|
-
const { origin, session, setSession, refs, setError } = params;
|
|
17
|
+
const { origin, session, setSession, refs, setError, onUnrecoverableSession } = params;
|
|
18
18
|
const [capabilities, setCapabilities] = useState(null);
|
|
19
19
|
const [models, setModels] = useState([]);
|
|
20
20
|
const [modelsStatus, setModelsStatus] = useState("idle");
|
|
@@ -84,9 +84,20 @@ export function useRuntimeControls(params) {
|
|
|
84
84
|
if (refs.sessionRef.current?.sessionId !== sessionId)
|
|
85
85
|
return;
|
|
86
86
|
if (!res.ok || !body) {
|
|
87
|
+
const error = body
|
|
88
|
+
?.error;
|
|
89
|
+
if (isLostChatSessionError({
|
|
90
|
+
status: res.status,
|
|
91
|
+
code: error?.code,
|
|
92
|
+
message: error?.message,
|
|
93
|
+
})) {
|
|
94
|
+
setModelsStatus("idle");
|
|
95
|
+
setModelsError(null);
|
|
96
|
+
onUnrecoverableSession?.(sessionId);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
87
99
|
setModelsStatus("error");
|
|
88
|
-
setModelsError(
|
|
89
|
-
?.error?.message ?? `模型列表读取失败(HTTP ${res.status})`);
|
|
100
|
+
setModelsError(error?.message ?? `模型列表读取失败(HTTP ${res.status})`);
|
|
90
101
|
return;
|
|
91
102
|
}
|
|
92
103
|
setModels(body.models ?? []);
|
|
@@ -99,7 +110,7 @@ export function useRuntimeControls(params) {
|
|
|
99
110
|
setModelsStatus("error");
|
|
100
111
|
setModelsError(reason instanceof Error ? reason.message : String(reason));
|
|
101
112
|
}
|
|
102
|
-
}, [origin, session, refs]);
|
|
113
|
+
}, [origin, session, refs, onUnrecoverableSession]);
|
|
103
114
|
useEffect(() => {
|
|
104
115
|
void loadModels();
|
|
105
116
|
}, [loadModels]);
|
|
@@ -126,6 +137,18 @@ export function useRuntimeControls(params) {
|
|
|
126
137
|
if (refs.sessionRef.current?.sessionId !== sessionId)
|
|
127
138
|
return;
|
|
128
139
|
if (!body?.context) {
|
|
140
|
+
if (!r.ok &&
|
|
141
|
+
isLostChatSessionError({
|
|
142
|
+
status: r.status,
|
|
143
|
+
code: body
|
|
144
|
+
?.error?.code,
|
|
145
|
+
message: body
|
|
146
|
+
?.error?.message,
|
|
147
|
+
})) {
|
|
148
|
+
setRuntimeContextLoading(false);
|
|
149
|
+
onUnrecoverableSession?.(sessionId);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
129
152
|
if (!r.ok)
|
|
130
153
|
setRuntimeContextError("注入上下文读取失败,请重试");
|
|
131
154
|
return;
|
|
@@ -158,7 +181,7 @@ export function useRuntimeControls(params) {
|
|
|
158
181
|
return;
|
|
159
182
|
setRuntimeContextError("注入上下文读取失败,请重试");
|
|
160
183
|
});
|
|
161
|
-
}, [origin, session?.sessionId, refs]);
|
|
184
|
+
}, [origin, session?.sessionId, refs, onUnrecoverableSession]);
|
|
162
185
|
// AC-01/RF3-01: hydrate the Composer from the unified snapshot — real
|
|
163
186
|
// model and thinking selection; `auto` displays the Pi actual effective
|
|
164
187
|
// level. Derived session/runtimeContext state ONLY comes from a snapshot
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { useSyncExternalStore } from "react";
|
|
13
13
|
import { confirmationToken } from "./format.js";
|
|
14
|
-
import { createRuntimeSnapshotStore, parseRuntimeSnapshotError, } from "./runtime-snapshot-store.js";
|
|
14
|
+
import { createRuntimeSnapshotStore, isLostChatSessionError, parseRuntimeSnapshotError, } from "./runtime-snapshot-store.js";
|
|
15
15
|
const store = createRuntimeSnapshotStore();
|
|
16
16
|
let controller;
|
|
17
17
|
function snapshotUrl(sessionId) {
|
|
@@ -49,10 +49,16 @@ async function fetchSnapshot(sessionId, origin) {
|
|
|
49
49
|
if (controller?.sessionId !== sessionId)
|
|
50
50
|
return; // stale session reply
|
|
51
51
|
if (!res.ok) {
|
|
52
|
+
const error = { ...parseRuntimeSnapshotError(body), status: res.status };
|
|
53
|
+
if (isLostChatSessionError(error)) {
|
|
54
|
+
store.dispatch({ type: "reset" });
|
|
55
|
+
controller.onUnrecoverableSession?.(sessionId);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
52
58
|
store.dispatch({
|
|
53
59
|
type: "load-failure",
|
|
54
60
|
sessionId,
|
|
55
|
-
error
|
|
61
|
+
error,
|
|
56
62
|
});
|
|
57
63
|
return;
|
|
58
64
|
}
|
|
@@ -1,19 +1,46 @@
|
|
|
1
|
+
export function isReplacementConsoleHealth(health, expectedConsoleInstanceId, previousConsoleInstanceId) {
|
|
2
|
+
if (!health || typeof health !== "object")
|
|
3
|
+
return false;
|
|
4
|
+
const instanceId = typeof health.consoleInstanceId ===
|
|
5
|
+
"string"
|
|
6
|
+
? health.consoleInstanceId.trim()
|
|
7
|
+
: "";
|
|
8
|
+
if (!instanceId)
|
|
9
|
+
return false;
|
|
10
|
+
const expected = expectedConsoleInstanceId?.trim();
|
|
11
|
+
if (expected)
|
|
12
|
+
return instanceId === expected;
|
|
13
|
+
return previousConsoleInstanceId !== null && instanceId !== previousConsoleInstanceId;
|
|
14
|
+
}
|
|
1
15
|
/**
|
|
2
|
-
* Poll /api/health until
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* port and reads as "the page is gone".
|
|
16
|
+
* Poll /api/health until the replacement Console answers, then reload. A
|
|
17
|
+
* handoff deliberately leaves the old process alive briefly, so HTTP 200 alone
|
|
18
|
+
* is insufficient: the response must carry the new instance identity.
|
|
6
19
|
*/
|
|
7
|
-
export async function reloadWhenConsoleReady(
|
|
20
|
+
export async function reloadWhenConsoleReady(options = {}) {
|
|
21
|
+
const timeoutMs = options.timeoutMs ?? 30_000;
|
|
22
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
23
|
+
const reload = options.reload ?? (() => window.location.reload());
|
|
8
24
|
const started = Date.now();
|
|
25
|
+
let previousConsoleInstanceId = null;
|
|
9
26
|
while (Date.now() - started < timeoutMs) {
|
|
10
27
|
try {
|
|
11
|
-
const res = await
|
|
28
|
+
const res = await fetchImpl(`/api/health?ts=${Date.now()}`, {
|
|
12
29
|
cache: "no-store",
|
|
13
30
|
});
|
|
14
31
|
if (res.ok) {
|
|
15
|
-
|
|
16
|
-
|
|
32
|
+
const health = await res.json().catch(() => null);
|
|
33
|
+
if (isReplacementConsoleHealth(health, options.expectedConsoleInstanceId, previousConsoleInstanceId)) {
|
|
34
|
+
reload();
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
const healthRecord = health;
|
|
38
|
+
const instanceId = typeof healthRecord?.consoleInstanceId === "string"
|
|
39
|
+
? healthRecord.consoleInstanceId.trim()
|
|
40
|
+
: "";
|
|
41
|
+
if (instanceId && previousConsoleInstanceId === null) {
|
|
42
|
+
previousConsoleInstanceId = instanceId;
|
|
43
|
+
}
|
|
17
44
|
}
|
|
18
45
|
}
|
|
19
46
|
catch {
|
|
@@ -21,5 +48,5 @@ export async function reloadWhenConsoleReady(timeoutMs = 30_000) {
|
|
|
21
48
|
}
|
|
22
49
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
23
50
|
}
|
|
24
|
-
|
|
51
|
+
return false;
|
|
25
52
|
}
|
|
@@ -170,6 +170,17 @@ export function withWorkspaceId(href, workspaceId) {
|
|
|
170
170
|
const qs = query.toString();
|
|
171
171
|
return `/#${path}${qs ? `?${qs}` : ""}`;
|
|
172
172
|
}
|
|
173
|
+
/** Preserve the selected scope for a deep link emitted inside an Operate page.
|
|
174
|
+
* Components should prefer this to hand-writing `/#/inspect/...`: it reads the
|
|
175
|
+
* canonical hash at click/render time and intentionally leaves bare Chat URLs
|
|
176
|
+
* unchanged because Chat selection is client state. */
|
|
177
|
+
export function withCurrentWorkspaceId(href, locationLike = typeof globalThis.location === "undefined" ? undefined : globalThis.location) {
|
|
178
|
+
const { query } = splitHash(locationLike?.hash ?? "");
|
|
179
|
+
const workspaceId = query.get("workspaceId")?.trim() ||
|
|
180
|
+
new URLSearchParams(locationLike?.search ?? "").get("workspaceId")?.trim() ||
|
|
181
|
+
undefined;
|
|
182
|
+
return withWorkspaceId(href, workspaceId);
|
|
183
|
+
}
|
|
173
184
|
export function buildCanonicalHref(input) {
|
|
174
185
|
if (input.workspace === "chat")
|
|
175
186
|
return buildChatHref(input);
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { randomBytes } from "node:crypto";
|
|
21
21
|
import { existsSync } from "node:fs";
|
|
22
|
+
import { rm } from "node:fs/promises";
|
|
22
23
|
import path from "node:path";
|
|
23
24
|
import { LoopAgentClient } from "../loop-agent/loop-agent-client.js";
|
|
24
|
-
import { openConsoleAppData, } from "../../infrastructure/console/app-data.js";
|
|
25
|
+
import { openConsoleAppData, sha256Json, writeSecureJson, } from "../../infrastructure/console/app-data.js";
|
|
25
26
|
import { DagConfirmationStore } from "./dag-confirmation.js";
|
|
26
27
|
import { DagExecutionReceiptStore } from "./dag-execution-receipt.js";
|
|
27
28
|
import { DraftStore } from "./draft-store.js";
|
|
@@ -31,6 +32,7 @@ import { ChatSessionStore } from "./chat/session-store.js";
|
|
|
31
32
|
import { createChatEventStore, createChatOperationLinker, } from "./chat/chat-event-store.js";
|
|
32
33
|
import { createChatTurnExecutionRegistry } from "./chat/turn-execution-registry.js";
|
|
33
34
|
import { ConsolePiRuntime } from "./chat/pi-runtime.js";
|
|
35
|
+
import { AimaxAuthReader, SddDataAlignmentService } from "./chat/sdd-data-alignment.js";
|
|
34
36
|
import { UserQuestionRegistry } from "./chat/user-questions.js";
|
|
35
37
|
import { TodoListRegistry } from "./chat/todos.js";
|
|
36
38
|
import { GoalRegistry } from "./chat/goals.js";
|
|
@@ -38,6 +40,9 @@ import { GoalRoundDriver } from "./chat/goal-round-driver.js";
|
|
|
38
40
|
import { driveGoalRound, recoverDeferredTurns } from "./chat/routes.js";
|
|
39
41
|
import { createOperationEventStore, } from "./operation-sse.js";
|
|
40
42
|
import { OperationStore } from "../../infrastructure/console/operation-store.js";
|
|
43
|
+
import { createFrontendSplitOperationAdapter, } from "./frontend-split-operation-adapter.js";
|
|
44
|
+
import { createFrontendHumanDecisionAdapter } from "./frontend-human-decision-adapter.js";
|
|
45
|
+
import { FrontendSplitTaskMaterializer, } from "../materialize/frontend-split-task-materializer.js";
|
|
41
46
|
import { reconcileContextExports } from "../../infrastructure/console/context-export-store.js";
|
|
42
47
|
import { reconcileArtifactRevisions } from "../../infrastructure/console/artifact-revision-store.js";
|
|
43
48
|
import { recordContextTransferEvent } from "./context-transfer-diagnostics.js";
|
|
@@ -76,6 +81,106 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
76
81
|
const events = createOperationEventStore({
|
|
77
82
|
persistenceDir: path.join(appData.operations, "events"),
|
|
78
83
|
});
|
|
84
|
+
// Frontend split runtime is repo-scoped: materialization, durable schedule
|
|
85
|
+
// intents, and aggregate closeout must use this workspace's app-data and
|
|
86
|
+
// OperationStore rather than the process startup workspace.
|
|
87
|
+
const splitScheduleDir = path.join(appData.root, "frontend-split-schedule");
|
|
88
|
+
const splitAuditDir = path.join(appData.root, "frontend-split-audit");
|
|
89
|
+
const splitAggregateDir = path.join(appData.root, "frontend-split-aggregate");
|
|
90
|
+
const splitMaterializationBackend = {
|
|
91
|
+
async ensureTask({ taskId }) {
|
|
92
|
+
return { taskId, complete: true };
|
|
93
|
+
},
|
|
94
|
+
async ensureOperation({ taskId, operationId, payloadDigest, request }) {
|
|
95
|
+
const action = request.kind === "child"
|
|
96
|
+
? "frontendSplitChild"
|
|
97
|
+
: "frontendSplitIntegration";
|
|
98
|
+
const { operation } = await operations.ensureSplitMaterializedOperation({
|
|
99
|
+
operationId,
|
|
100
|
+
taskId,
|
|
101
|
+
action,
|
|
102
|
+
actionParams: request,
|
|
103
|
+
payloadHash: payloadDigest,
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
taskId: operation.taskId ?? taskId,
|
|
107
|
+
operationId: operation.operationId,
|
|
108
|
+
complete: true,
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
const materializer = new FrontendSplitTaskMaterializer(splitMaterializationBackend);
|
|
113
|
+
const schedule = async (input) => {
|
|
114
|
+
const fact = {
|
|
115
|
+
schemaVersion: 1,
|
|
116
|
+
kind: "frontend-split-schedule-intent",
|
|
117
|
+
operationId: input.operationId,
|
|
118
|
+
generation: input.generation,
|
|
119
|
+
bindingDigest: input.bindingDigest,
|
|
120
|
+
recordedAt: new Date().toISOString(),
|
|
121
|
+
};
|
|
122
|
+
const name = `${sha256Json({
|
|
123
|
+
operationId: input.operationId,
|
|
124
|
+
generation: input.generation,
|
|
125
|
+
bindingDigest: input.bindingDigest,
|
|
126
|
+
})}.json`;
|
|
127
|
+
await writeSecureJson(path.join(splitScheduleDir, name), fact);
|
|
128
|
+
};
|
|
129
|
+
const auditStale = async (input) => {
|
|
130
|
+
const fact = {
|
|
131
|
+
schemaVersion: 1,
|
|
132
|
+
kind: "frontend-split-stale-audit",
|
|
133
|
+
operationId: input.operationId,
|
|
134
|
+
reason: input.reason,
|
|
135
|
+
fact: input.fact,
|
|
136
|
+
recordedAt: new Date().toISOString(),
|
|
137
|
+
};
|
|
138
|
+
await writeSecureJson(path.join(splitAuditDir, `${sha256Json(fact)}.json`), fact);
|
|
139
|
+
};
|
|
140
|
+
const prepareAggregate = async ({ operationId, aggregate, }) => {
|
|
141
|
+
const before = await operations.get(operationId);
|
|
142
|
+
if (!before) {
|
|
143
|
+
throw new Error(`cannot prepare aggregate: parent operation not found: ${operationId}`);
|
|
144
|
+
}
|
|
145
|
+
const aggregateFile = path.join(splitAggregateDir, `${aggregate.digest}.json`);
|
|
146
|
+
let parentRecordPath = "";
|
|
147
|
+
let wroteAggregate = false;
|
|
148
|
+
let wroteParent = false;
|
|
149
|
+
return {
|
|
150
|
+
committedRef: {
|
|
151
|
+
ref: aggregateFile,
|
|
152
|
+
digest: aggregate.digest,
|
|
153
|
+
commitState: "committed",
|
|
154
|
+
},
|
|
155
|
+
async commit(input) {
|
|
156
|
+
parentRecordPath = input.parentRecordPath;
|
|
157
|
+
await writeSecureJson(aggregateFile, {
|
|
158
|
+
schemaVersion: 1,
|
|
159
|
+
kind: "frontend-split-aggregate-closeout",
|
|
160
|
+
operationId,
|
|
161
|
+
aggregate,
|
|
162
|
+
});
|
|
163
|
+
wroteAggregate = true;
|
|
164
|
+
await writeSecureJson(input.parentRecordPath, input.parentRecord);
|
|
165
|
+
wroteParent = true;
|
|
166
|
+
},
|
|
167
|
+
async rollback() {
|
|
168
|
+
if (wroteAggregate)
|
|
169
|
+
await rm(aggregateFile, { force: true });
|
|
170
|
+
if (wroteParent && parentRecordPath) {
|
|
171
|
+
await writeSecureJson(parentRecordPath, before);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
const splitOrchestrator = createFrontendSplitOperationAdapter({
|
|
177
|
+
store: operations,
|
|
178
|
+
materializer,
|
|
179
|
+
schedule,
|
|
180
|
+
auditStale,
|
|
181
|
+
prepareAggregate,
|
|
182
|
+
});
|
|
183
|
+
const humanDecisionAdapter = createFrontendHumanDecisionAdapter(operations);
|
|
79
184
|
const drafts = new DraftStore(appData);
|
|
80
185
|
const confirmations = new DagConfirmationStore(appData, deps.bootToken.confirmationToken);
|
|
81
186
|
const dagExecutionReceipts = new DagExecutionReceiptStore(appData);
|
|
@@ -123,6 +228,9 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
123
228
|
client,
|
|
124
229
|
operations,
|
|
125
230
|
events,
|
|
231
|
+
splitOrchestrator,
|
|
232
|
+
humanDecisionAdapter,
|
|
233
|
+
materializer,
|
|
126
234
|
drafts,
|
|
127
235
|
confirmations,
|
|
128
236
|
dagExecutionReceipts,
|
|
@@ -146,6 +254,7 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
146
254
|
}
|
|
147
255
|
},
|
|
148
256
|
});
|
|
257
|
+
const sddDataAlignment = new SddDataAlignmentService(chatStore, new AimaxAuthReader(), deps.fetchImpl);
|
|
149
258
|
const chatEventEpoch = randomBytes(12).toString("hex");
|
|
150
259
|
const chatEvents = createChatEventStore({
|
|
151
260
|
appData,
|
|
@@ -184,6 +293,7 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
184
293
|
questionRegistry,
|
|
185
294
|
todoRegistry,
|
|
186
295
|
goalRegistry,
|
|
296
|
+
sddDataAlignment,
|
|
187
297
|
});
|
|
188
298
|
let chatRouteDeps;
|
|
189
299
|
const goalRoundDriver = new GoalRoundDriver((sessionId) => driveGoalRound(chatRouteDeps, sessionId));
|
|
@@ -195,6 +305,7 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
195
305
|
questionRegistry,
|
|
196
306
|
todoRegistry,
|
|
197
307
|
goalRegistry,
|
|
308
|
+
sddDataAlignment,
|
|
198
309
|
goalRoundDriver,
|
|
199
310
|
operationLinker: chatOperationLinker,
|
|
200
311
|
appData,
|
|
@@ -218,6 +329,9 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
218
329
|
client,
|
|
219
330
|
operations,
|
|
220
331
|
events,
|
|
332
|
+
splitOrchestrator,
|
|
333
|
+
humanDecisionAdapter,
|
|
334
|
+
materializer,
|
|
221
335
|
drafts,
|
|
222
336
|
confirmations,
|
|
223
337
|
dagExecutionReceipts,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { computeFrontendSplitDigest } from "../../workflows/dag/frontend-split-orchestrator.js";
|
|
3
|
+
const idSchema = z.string().trim().min(1);
|
|
4
|
+
const digestSchema = z.string().regex(/^[a-f0-9]{64}$/);
|
|
5
|
+
const positiveIntegerSchema = z.number().int().positive();
|
|
6
|
+
const pathSchema = z.string().trim().min(1);
|
|
7
|
+
export const frontendSplitTaskMaterializationRequestSchema = z.object({
|
|
8
|
+
kind: z.enum(["child", "integration"]),
|
|
9
|
+
parentOperationId: idSchema,
|
|
10
|
+
manifestDigest: digestSchema,
|
|
11
|
+
generation: positiveIntegerSchema,
|
|
12
|
+
taskId: idSchema,
|
|
13
|
+
operationId: idSchema,
|
|
14
|
+
payloadDigest: digestSchema,
|
|
15
|
+
allowedPaths: z.array(pathSchema),
|
|
16
|
+
writeSet: z.array(pathSchema),
|
|
17
|
+
verifyRefs: z.array(idSchema),
|
|
18
|
+
}).strict();
|
|
19
|
+
export const frontendSplitTaskMaterializationResultSchema = z.object({
|
|
20
|
+
idempotencyKey: digestSchema,
|
|
21
|
+
requestDigest: digestSchema,
|
|
22
|
+
taskId: idSchema,
|
|
23
|
+
operationId: idSchema,
|
|
24
|
+
}).strict();
|
|
25
|
+
function identityPayload(request) {
|
|
26
|
+
return { kind: request.kind, parentOperationId: request.parentOperationId, manifestDigest: request.manifestDigest, generation: request.generation, taskId: request.taskId, operationId: request.operationId };
|
|
27
|
+
}
|
|
28
|
+
export class FrontendSplitTaskMaterializer {
|
|
29
|
+
backend;
|
|
30
|
+
completed = new Map();
|
|
31
|
+
inFlight = new Map();
|
|
32
|
+
constructor(backend) {
|
|
33
|
+
this.backend = backend;
|
|
34
|
+
}
|
|
35
|
+
async materialize(raw) {
|
|
36
|
+
const request = frontendSplitTaskMaterializationRequestSchema.parse(raw);
|
|
37
|
+
const idempotencyKey = computeFrontendSplitDigest(identityPayload(request));
|
|
38
|
+
const requestDigest = computeFrontendSplitDigest(request);
|
|
39
|
+
const existing = this.completed.get(idempotencyKey);
|
|
40
|
+
if (existing) {
|
|
41
|
+
if (existing.requestDigest !== requestDigest)
|
|
42
|
+
throw new Error("frontend split materialization payload drift");
|
|
43
|
+
return existing;
|
|
44
|
+
}
|
|
45
|
+
const pending = this.inFlight.get(idempotencyKey);
|
|
46
|
+
if (pending) {
|
|
47
|
+
const result = await pending;
|
|
48
|
+
if (result.requestDigest !== requestDigest)
|
|
49
|
+
throw new Error("frontend split materialization payload drift");
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
const promise = this.perform(request, idempotencyKey, requestDigest);
|
|
53
|
+
this.inFlight.set(idempotencyKey, promise);
|
|
54
|
+
try {
|
|
55
|
+
const result = await promise;
|
|
56
|
+
this.completed.set(idempotencyKey, result);
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
this.inFlight.delete(idempotencyKey);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async perform(request, idempotencyKey, requestDigest) {
|
|
64
|
+
const task = await this.backend.ensureTask({ idempotencyKey, taskId: request.taskId, payloadDigest: requestDigest, request });
|
|
65
|
+
if (!task.complete || task.taskId !== request.taskId)
|
|
66
|
+
throw new Error("frontend split task materialization incomplete or drifted");
|
|
67
|
+
const operation = await this.backend.ensureOperation({ idempotencyKey, taskId: request.taskId, operationId: request.operationId, payloadDigest: requestDigest, request });
|
|
68
|
+
if (!operation.complete || operation.taskId !== request.taskId || operation.operationId !== request.operationId)
|
|
69
|
+
throw new Error("frontend split operation materialization incomplete or drifted");
|
|
70
|
+
return frontendSplitTaskMaterializationResultSchema.parse({ idempotencyKey, requestDigest, taskId: request.taskId, operationId: request.operationId });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -278,6 +278,10 @@ export async function serveObserveStatic(req, res, staticDir, options = {}) {
|
|
|
278
278
|
"Content-Length": content.byteLength,
|
|
279
279
|
"X-Content-Type-Options": "nosniff",
|
|
280
280
|
"X-Frame-Options": "SAMEORIGIN",
|
|
281
|
+
// Filenames are unhashed and Console/observe builds change in place;
|
|
282
|
+
// heuristic caching would let a stale operator-chrome.js (old nav,
|
|
283
|
+
// old capabilities) survive a Console restart/update.
|
|
284
|
+
"Cache-Control": "no-store",
|
|
281
285
|
};
|
|
282
286
|
if (contentType.startsWith("text/html") && options.htmlEnhancer) {
|
|
283
287
|
const enhanced = options.htmlEnhancer(Buffer.from(content));
|
|
@@ -20,20 +20,20 @@ export const OUTPUT_FOLLOW_LATEST_PX = 48;
|
|
|
20
20
|
|
|
21
21
|
export const UI_TEXT = {
|
|
22
22
|
dashboard: "总览",
|
|
23
|
-
dagRuns: "
|
|
24
|
-
dagHistory: "
|
|
23
|
+
dagRuns: "运行流程",
|
|
24
|
+
dagHistory: "运行历史",
|
|
25
25
|
viewAllDags: "查看全部 →",
|
|
26
|
-
failures: "
|
|
26
|
+
failures: "异常任务",
|
|
27
27
|
nightJobs: "夜间任务",
|
|
28
28
|
batchDetail: "批次详情",
|
|
29
29
|
runDetail: "运行详情",
|
|
30
|
-
dagDetail: "
|
|
31
|
-
activeDags: "
|
|
32
|
-
recentDags: "
|
|
33
|
-
batches: "
|
|
34
|
-
noActiveDags: "
|
|
35
|
-
noRecentDags: "
|
|
36
|
-
noFormalDagHistory: "
|
|
30
|
+
dagDetail: "运行详情",
|
|
31
|
+
activeDags: "进行中的流程",
|
|
32
|
+
recentDags: "最近运行",
|
|
33
|
+
batches: "任务批次",
|
|
34
|
+
noActiveDags: "当前没有进行中的流程,启动后将出现在这里。",
|
|
35
|
+
noRecentDags: "暂无运行记录。",
|
|
36
|
+
noFormalDagHistory: "暂无正式运行记录",
|
|
37
37
|
noFormalDagHistoryHint:
|
|
38
38
|
"dry-run / init-only 预检记录不会出现在此列表中。",
|
|
39
39
|
dagHistoryRefreshFailed: "刷新失败,当前展示上一次结果",
|
|
@@ -41,34 +41,34 @@ export const UI_TEXT = {
|
|
|
41
41
|
dagHistoryCopiedAnnounce: "已复制运行 ID",
|
|
42
42
|
dagHistoryCopyFailed: "复制失败,请手动选择运行 ID",
|
|
43
43
|
dagHistoryCopyTooltip: "复制运行 ID",
|
|
44
|
-
noBatches: "
|
|
44
|
+
noBatches: "暂无任务批次,运行后将出现在这里。",
|
|
45
45
|
noNodes: "暂无节点。",
|
|
46
46
|
noOutput: "暂无节点输出。",
|
|
47
47
|
noInput: "暂无节点输入。",
|
|
48
48
|
inputLoading: "正在加载节点输入…",
|
|
49
|
-
inputUnavailable: "
|
|
49
|
+
inputUnavailable: "没有在本次运行保存的执行计划中找到该节点设置。",
|
|
50
50
|
inputDynamicUnsupported:
|
|
51
|
-
"
|
|
52
|
-
inputRunSpecUnavailable: "
|
|
51
|
+
"该节点是在运行过程中动态生成的,暂时无法展示完整的运行前设置。",
|
|
52
|
+
inputRunSpecUnavailable: "无法读取本次运行开始前保存的执行计划(run.json)。",
|
|
53
53
|
inputError: "加载节点输入失败。",
|
|
54
|
-
inputFrozenLabel: "
|
|
55
|
-
inputFingerprintNote: "
|
|
56
|
-
noSessionEvents: "该
|
|
57
|
-
noShellProcess: "
|
|
54
|
+
inputFrozenLabel: "本次运行开始前保存的执行设置",
|
|
55
|
+
inputFingerprintNote: "这里只保存最终提示内容的校验信息,没有保存完整文本。",
|
|
56
|
+
noSessionEvents: "该 AI 节点暂无过程日志(可能尚未执行、通过命令行执行,或来自较早的运行)。",
|
|
57
|
+
noShellProcess: "该命令节点尚未留下执行记录。",
|
|
58
58
|
noPresetProcess: "该内部处理器尚未留下过程回执。",
|
|
59
|
-
staticNoProcess: "
|
|
59
|
+
staticNoProcess: "系统直接生成的节点没有逐步执行过程,请查看节点输出。",
|
|
60
60
|
processLoading: "正在加载执行过程…",
|
|
61
61
|
processError: "加载执行过程失败。",
|
|
62
|
-
shellCommandsHeading: "
|
|
62
|
+
shellCommandsHeading: "命令执行记录",
|
|
63
63
|
shellPresetHeading: "内部处理器",
|
|
64
64
|
noArtifacts: "暂无产物记录。",
|
|
65
65
|
noActiveCommand: "当前没有正在执行的命令。",
|
|
66
|
-
noFailures: "
|
|
66
|
+
noFailures: "当前没有异常任务。",
|
|
67
67
|
enterDetail: "查看详情 →",
|
|
68
68
|
repoRoot: "仓库根目录",
|
|
69
69
|
generatedAt: "生成时间",
|
|
70
70
|
refreshStatus: "更新",
|
|
71
|
-
failureInbox: "
|
|
71
|
+
failureInbox: "异常任务",
|
|
72
72
|
nearRealtime: "轮询刷新",
|
|
73
73
|
manualRefresh: "手动刷新",
|
|
74
74
|
autoRefresh: "自动刷新",
|
|
@@ -30,6 +30,25 @@ const CONTEXT_TRANSFER_REASON_LABELS = {
|
|
|
30
30
|
WORKER_CONTINUATION_AUTHORITY_MISMATCH: "Worker 派生运行的归属事实不一致",
|
|
31
31
|
WORKER_CONTINUATION_NEEDS_RECONCILE: "Worker 派生运行已存在,需要先恢复核对",
|
|
32
32
|
WORKER_CONTINUATION_LEASE_CONFLICT: "该 Worker Task 已有运行占用",
|
|
33
|
+
"parent-lifecycle-ineligible": "原运行尚未完整结束,只有已完成的运行才能从这里继续",
|
|
34
|
+
"convergence-rerun-unsupported": "原运行使用自动收敛流程,暂不支持替换产出后继续",
|
|
35
|
+
"evaluation-rerun-unsupported": "评测运行暂不支持替换产出后继续",
|
|
36
|
+
"budget-lineage-missing": "缺少原运行的额度使用记录",
|
|
37
|
+
"budget-lineage-breached": "原运行已经用完或超出额度",
|
|
38
|
+
"budget-lineage-drift": "原运行的额度规则已经变化",
|
|
39
|
+
"sourceBindingMatched-drift": "任务来源与原运行不一致",
|
|
40
|
+
"taskContractBindingMatched-drift": "任务约定与原运行不一致",
|
|
41
|
+
"controllerIdentityMatched-drift": "运行控制器版本与原运行不一致",
|
|
42
|
+
"skillSnapshotVerified-drift": "节点使用的技能版本无法与原运行核对",
|
|
43
|
+
"workspaceMatched-drift": "当前工作区内容与原运行结束时不一致",
|
|
44
|
+
"restart-subgraph-contains-writer": "受影响范围里有写入节点,但它的文件边界不够明确",
|
|
45
|
+
"restart-subgraph-contains-decision-gate": "受影响范围里包含需要人工重新决定的关卡",
|
|
46
|
+
"restart-subgraph-contains-unsafe-shell": "受影响范围里有不能安全自动重跑的命令",
|
|
47
|
+
"dynamic-rerun-unsupported": "受影响范围里有无法安全重新生成的动态节点",
|
|
48
|
+
"parent-facts-invalid": "准备沿用的节点没有完整的成功记录",
|
|
49
|
+
"workspace-checkpoint-missing": "缺少原运行结束时的工作区校验记录",
|
|
50
|
+
"workspace-drift": "当前工作区已经发生变化,不能安全接着写",
|
|
51
|
+
HIGH_RISK_AUTHORITY_MISSING: "高风险重跑缺少完整的写入、动态节点或额度校验信息",
|
|
33
52
|
INELIGIBLE: "当前不满足继续运行的条件",
|
|
34
53
|
PARENT_IMMUTABILITY_VIOLATION: "父运行事实发生变化,已阻止派生",
|
|
35
54
|
PARENT_LIFECYCLE: "父运行状态不允许派生继续",
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
formatDuration,
|
|
7
7
|
formatAgo,
|
|
8
8
|
} from "./format.js";
|
|
9
|
-
import { navigate } from "./router.js";
|
|
9
|
+
import { inspectPathToCanonicalHref, navigate } from "./router.js";
|
|
10
10
|
|
|
11
11
|
export function isNodeActive(status) {
|
|
12
12
|
if (!status) return false;
|
|
@@ -151,7 +151,9 @@ export function frontendLintSummary(frontendLint) {
|
|
|
151
151
|
card.appendChild(meta);
|
|
152
152
|
|
|
153
153
|
const link = el("a", "active-dag-link", UI_TEXT.enterDetail);
|
|
154
|
-
link.href =
|
|
154
|
+
link.href = inspectPathToCanonicalHref(
|
|
155
|
+
`/dag/${encodeURIComponent(dag.dagRunId)}`,
|
|
156
|
+
);
|
|
155
157
|
link.addEventListener("click", (e) => {
|
|
156
158
|
e.preventDefault();
|
|
157
159
|
navigate(`/dag/${encodeURIComponent(dag.dagRunId)}`);
|