@tea-agent/loop-agent 0.39.0-next.8 → 0.39.0
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 +4 -1
- package/CHANGELOG.md +264 -67
- package/README.md +11 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/dag/generate-task-dag.js +9 -0
- package/dist/application/task-lifecycle/advance.js +4 -0
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +428 -27
- package/dist/commands/inspect-next.js +8 -0
- package/dist/commands/task-advance.js +31 -0
- package/dist/executors/dag-pi-executor.js +124 -18
- package/dist/executors/pi-event-serializer.js +42 -1
- package/dist/executors/pi-executor.js +51 -0
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +74 -28
- package/dist/executors/pi-sdk-executor.js +187 -59
- package/dist/executors/shell-executor.js +554 -33
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/shared/dag-failure-category.js +138 -0
- package/dist/shared/dag-prompt-override.js +28 -0
- package/dist/shared/operator/capabilities.js +181 -27
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +28 -0
- package/dist/task/contract/constants.js +1 -0
- package/dist/task/contract/diff.js +26 -0
- package/dist/task/contract/project.js +5 -0
- package/dist/task/contract/schema.js +6 -1
- package/dist/task/source-prepare/build-draft.js +15 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/deferred-turn.js +91 -0
- package/dist/worker/console/chat/mcp-inventory.js +221 -0
- package/dist/worker/console/chat/operation-card.js +8 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/pi-runtime.js +165 -83
- package/dist/worker/console/chat/repo-walk.js +10 -1
- package/dist/worker/console/chat/routes.js +480 -55
- package/dist/worker/console/chat/semantic-activity.js +20 -8
- package/dist/worker/console/chat/session-store.js +146 -2
- package/dist/worker/console/chat/turn-process.js +10 -10
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/inspect-split.js +18 -0
- package/dist/worker/console/operation-run-facts.js +19 -3
- package/dist/worker/console/operator-actions.js +195 -4
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/pi-readiness.js +5 -4
- package/dist/worker/console/recovery-cta.js +4 -4
- package/dist/worker/console/recovery-error-copy.js +4 -4
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +42 -6
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BLXFe4qI.js → abnfDiagram-N423BO3Z-Br92EGb7.js} +1 -1
- package/dist/worker/console/static/assets/{arc-7iOJeqho.js → arc-oulImtQq.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CYoyj5K7.js → architectureDiagram-T3A2C74G-tyHc6aqO.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-fK01JNlR.js → blockDiagram-VBNYF7ZC-CY86BSpT.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-bzbw5D_Z.js → c4Diagram-5PPSVZJV-BKKsDmNs.js} +1 -1
- package/dist/worker/console/static/assets/channel-TY36zt-i.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Cf6hJFFG.js → chunk-2GRJ4B5K-CXZdjl3B.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DSFXR-et.js → chunk-2Q5K7J3B-B-aIFDQ5.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BagJechl.js → chunk-5RXB4S5H-BDgAW_B2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-E7QNMFaW.js → chunk-5VM5RSS4-DABa4UPR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BG_eOBrC.js → chunk-6Q2QTUOP-aGbysVOx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bo42dqol.js → chunk-GF5L2VYU-B8MSZDVs.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-dibM-87v.js → chunk-JWPE2WC7-CpelV73g.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Bytrp3Oe.js → chunk-KBJHAD2P-BE04ty3z.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DBABILrr.js → chunk-RYQCIY6F-BUxTkaaR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DCUk5Pf9.js → chunk-XXDRQBXY-CYYBVKfX.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Dk7QCChM.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Dk7QCChM.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BlGE9v6E.js → cose-bilkent-JH36ORCC-CEQF_xmQ.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DGl5ewph.js → cynefin-VYW2F7L2-CqpxN5IT.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DsJ_SVH_.js → cynefinDiagram-MW4NZA55-B3PzanVi.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BIhjFdzv.js → dagre-VZM6K2ZE-BSUGCNx1.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dmy1ctTb.js → diagram-7IWD3JNH-CdLevU5T.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DYCtWUF3.js → diagram-B4RE2ZJO-BjpF_Kok.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BMZFHVd2.js → diagram-LBJQPF4R-C4I6NJny.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-b_U8gfaO.js → diagram-Q27KOJAE-C8hecCr5.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dzy_bn-I.js → diagram-UB23O5K3-Daod6tOS.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DJCiMNEI.js → ebnfDiagram-BXEA7PRR-BvvmazGv.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-28lD016B.js → erDiagram-JOGREHBK-DeN_vfV4.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DGyHJmIw.js → flowDiagram-UKHOOZJN-D6dq1t9z.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CzdwUjrO.js → ganttDiagram-PKOTCBZU-DziIdNY4.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-D9bGwuo8.js → gitGraphDiagram-DS77QQ5N-Dy4s5CXr.js} +1 -1
- package/dist/worker/console/static/assets/index-C3GgHg-g.css +1 -0
- package/dist/worker/console/static/assets/index-Ci8ksvBm.js +406 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-LJgJUQFC.js → infoDiagram-6WML65LV-Czv5eYx2.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-98LaB8zb.js → ishikawaDiagram-WSZJBQD7-Bsk3nFRT.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-D-ISzwlQ.js → journeyDiagram-NVQOT4AX-DxGmNj5K.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-gfYOI-EL.js → kanban-definition-27J2QSJJ-CYCFqkO_.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CyntZgKV.js → linear-B0YzRwrd.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BBOR7dg0.js → mermaid.core-t3EZtIGh.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CQAgwPMk.js → mindmap-definition-FAOFIHXS-BTXpTv0T.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DkMiZFuV.js → pegDiagram-VL7TDLO6-DU8UkKqA.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DLNXa_0J.js → pieDiagram-7S7Q4E2Y-DP4y8ohp.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Dc7bdzjm.js → quadrantDiagram-CIZ2JOQS-B07vNaYR.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BxMB0QqW.js → railroadDiagram-AXF67PYL-xDWktb2i.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-FjumoKn4.js → requirementDiagram-LRYGKXZP-BLNMKqeS.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CkhKc_E3.js → sankeyDiagram-W5VNT64P-n0adalxH.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DoBWqTTH.js → sequenceDiagram-SI44F4Z6-BbnS-2DL.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CQeh_YeE.js → sizeCapture-X5ZJPWSS-B4uPpCLu.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-LuHmFW-I.js → stateDiagram-OKZ733FA--MMbB0fX.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BqOigMQ4.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CHwbDctY.js → swimlanes-SLNWSIFB-BXUgp2QY.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Caxz9UA2.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CoMngk0X.js → timeline-definition-Z64GVDOM-CsaMxki7.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CURYbtbO.js → vennDiagram-T6HMQDX7-C2IuG9iH.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BdqSECjU.js → wardleyDiagram-T6FBY63Y-CmEWOXCW.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DbMUYuAg.js → xychartDiagram-ELKLHX3M-D_9LE0MG.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +1 -1
- package/dist/worker/console/static-src/app/useRecoveryActions.js +2 -2
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +38 -2
- package/dist/worker/console/static-src/app/useRunProgress.js +46 -0
- package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +74 -26
- package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +328 -0
- package/dist/worker/console/static-src/operator-chat/resource-diagnostics.js +204 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +8 -1
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +54 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +3 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +237 -11
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +50 -22
- package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +26 -1
- package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
- package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/harness-task-lineage.js +5 -2
- package/dist/worker/observability/read-model.js +22 -0
- package/dist/worker/observe/node-input.js +74 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +13 -2
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +612 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +827 -53
- package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
- package/dist/workflows/dag/backend-test-execution-contract.js +18 -9
- package/dist/workflows/dag/backend-test-gap-fill.js +205 -0
- package/dist/workflows/dag/backend-test-intake-context.js +32 -0
- package/dist/workflows/dag/backend-test-layout.js +133 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +167 -32
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +368 -33
- package/dist/workflows/dag/backend-test-result-contract.js +1 -1
- package/dist/workflows/dag/backend-test-scenario-param.js +884 -140
- package/dist/workflows/dag/backend-test-scenario-partitions.js +321 -0
- package/dist/workflows/dag/backend-test-writer-completeness.js +100 -56
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +3 -1
- package/dist/workflows/dag/convergence/controller.js +141 -0
- package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
- package/dist/workflows/dag/dynamic-runtime/map.js +25 -9
- package/dist/workflows/dag/dynamic-runtime/shared.js +1 -1
- package/dist/workflows/dag/failure-category.js +7 -116
- package/dist/workflows/dag/frontend-implementation-contract.js +377 -4
- package/dist/workflows/dag/frontend-plan-render.js +1 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +96 -84
- package/dist/workflows/dag/frontend-repair.js +14 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
- package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
- package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
- package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
- package/dist/workflows/dag/frontend-test-html-report.js +3 -1
- package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
- package/dist/workflows/dag/frontend-test-layout.js +159 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
- package/dist/workflows/dag/frontend-verification-trace.js +29 -4
- package/dist/workflows/dag/init-hybrid.js +894 -205
- package/dist/workflows/dag/node-execution.js +183 -15
- package/dist/workflows/dag/prompt.js +60 -1
- package/dist/workflows/dag/rerun-feedback.js +212 -0
- package/dist/workflows/dag/rerun-plan.js +224 -27
- package/dist/workflows/dag/rerun-run.js +61 -6
- package/dist/workflows/dag/rerun-task.js +61 -2
- package/dist/workflows/dag/retry-policy.js +109 -0
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +143 -1
- package/dist/workflows/dag/validate.js +64 -20
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/operations/local-development-environment.md +1 -5
- package/docs/skills/vetted-skill-registry.md +14 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/backend-test-dag.json +60 -35
- package/docs/templates/frontend-implementation-contract.schema.json +1 -1
- package/docs/templates/frontend-test-case-checklist.md +2 -2
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +6 -4
- package/package.json +12 -4
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/analyze-product-requirements/SKILL.md +8 -5
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +1 -1
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +10 -6
- package/skills/analyze-product-requirements/references/example.md +50 -0
- package/skills/analyze-product-requirements/references/forward-test-cases.md +11 -7
- package/skills/analyze-product-requirements/references/kb-integration.md +5 -5
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +8 -4
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +2 -2
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +1 -1
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +11 -1
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +20 -1
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +9 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-implementation/references/node-contracts.md +2 -2
- package/skills/improve-codebase-architecture/SKILL.md +81 -0
- package/skills/improve-codebase-architecture/deepening.md +37 -0
- package/skills/improve-codebase-architecture/html-report.md +123 -0
- package/skills/improve-codebase-architecture/interface-design.md +44 -0
- package/skills/improve-codebase-architecture/language.md +53 -0
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/playwright-cli/SKILL.md +23 -1
- package/skills/playwright-cli-case-generator/SKILL.md +29 -8
- package/dist/worker/console/static/assets/channel-BBOTJHvD.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cm8Yf8tD.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cm8Yf8tD.js +0 -1
- package/dist/worker/console/static/assets/index-B-qWGThd.js +0 -325
- package/dist/worker/console/static/assets/index-C-xRgY_0.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DZihfdKc.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CKnXCjHX.js +0 -8
|
@@ -17,6 +17,7 @@ import { dispatchOperatorAction } from "../operator-actions.js";
|
|
|
17
17
|
import { authorizeOperatorChatTool } from "./tools.js";
|
|
18
18
|
import { projectOperationForChat } from "./chat-event-store.js";
|
|
19
19
|
import { scrubSecrets } from "./explore-tools.js";
|
|
20
|
+
import { hashDeferredTurnPayload, parseDeferredTurn, } from "./deferred-turn.js";
|
|
20
21
|
const CHAT_TITLE_MAX_CHARS = 80;
|
|
21
22
|
/** Safe, one-line bounded title used for placeholders and model output. */
|
|
22
23
|
export function cleanChatTitle(value) {
|
|
@@ -53,6 +54,7 @@ export class ChatSessionStore {
|
|
|
53
54
|
const raw = await readJsonIfExists(recordPath(this.appData, sessionId));
|
|
54
55
|
if (!raw || raw.sessionId !== sessionId)
|
|
55
56
|
return undefined;
|
|
57
|
+
const deferredTurn = parseDeferredTurn(raw.deferredTurn);
|
|
56
58
|
return {
|
|
57
59
|
schemaVersion: 2,
|
|
58
60
|
sessionId,
|
|
@@ -67,6 +69,7 @@ export class ChatSessionStore {
|
|
|
67
69
|
...(typeof raw.modelProvider === "string" ? { modelProvider: raw.modelProvider } : {}),
|
|
68
70
|
...(typeof raw.modelId === "string" ? { modelId: raw.modelId } : {}),
|
|
69
71
|
...(typeof raw.thinkingLevel === "string" ? { thinkingLevel: raw.thinkingLevel } : {}),
|
|
72
|
+
...(deferredTurn ? { deferredTurn } : {}),
|
|
70
73
|
messages: Array.isArray(raw.messages) ? raw.messages : [],
|
|
71
74
|
toolInvocations: Array.isArray(raw.toolInvocations) ? raw.toolInvocations : [],
|
|
72
75
|
};
|
|
@@ -147,6 +150,7 @@ export class ChatSessionStore {
|
|
|
147
150
|
...(input.targetSessionFile ? { sessionFile: input.targetSessionFile } : { sessionFile: undefined }),
|
|
148
151
|
createdAt: now,
|
|
149
152
|
updatedAt: now,
|
|
153
|
+
deferredTurn: undefined,
|
|
150
154
|
messages: structuredClone(source.messages),
|
|
151
155
|
toolInvocations: structuredClone(source.toolInvocations),
|
|
152
156
|
};
|
|
@@ -216,6 +220,146 @@ export class ChatSessionStore {
|
|
|
216
220
|
return true;
|
|
217
221
|
});
|
|
218
222
|
}
|
|
223
|
+
async putDeferredTurn(sessionId, input) {
|
|
224
|
+
return this.serialize(sessionId, async () => {
|
|
225
|
+
const record = await this.get(sessionId);
|
|
226
|
+
if (!record)
|
|
227
|
+
return { ok: false, code: "NOT_FOUND" };
|
|
228
|
+
const clientRequestId = input.clientRequestId.trim();
|
|
229
|
+
const blockedByTurnId = input.blockedByTurnId.trim();
|
|
230
|
+
const text = input.text.trim() || (input.images.length > 0 ? "请查看附件图片。" : "");
|
|
231
|
+
if (!clientRequestId || !blockedByTurnId || !text) {
|
|
232
|
+
return { ok: false, code: "INVALID_INPUT" };
|
|
233
|
+
}
|
|
234
|
+
const payloadHash = hashDeferredTurnPayload({ text, images: input.images });
|
|
235
|
+
const existing = record.deferredTurn;
|
|
236
|
+
if (existing) {
|
|
237
|
+
if (existing.clientRequestId === clientRequestId) {
|
|
238
|
+
if (existing.payloadHash !== payloadHash) {
|
|
239
|
+
return {
|
|
240
|
+
ok: false,
|
|
241
|
+
code: "REQUEST_ID_REUSE_CONFLICT",
|
|
242
|
+
deferredTurn: existing,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return { ok: true, deferredTurn: existing, idempotent: true };
|
|
246
|
+
}
|
|
247
|
+
if (existing.state !== "queued") {
|
|
248
|
+
return {
|
|
249
|
+
ok: false,
|
|
250
|
+
code: "NOT_REPLACEABLE",
|
|
251
|
+
deferredTurn: existing,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (input.expectedRevision !== existing.revision) {
|
|
255
|
+
return {
|
|
256
|
+
ok: false,
|
|
257
|
+
code: "REVISION_CONFLICT",
|
|
258
|
+
deferredTurn: existing,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else if (input.expectedRevision !== undefined &&
|
|
263
|
+
input.expectedRevision !== 0) {
|
|
264
|
+
return { ok: false, code: "REVISION_CONFLICT" };
|
|
265
|
+
}
|
|
266
|
+
const now = new Date().toISOString();
|
|
267
|
+
const deferredTurn = {
|
|
268
|
+
schemaVersion: 1,
|
|
269
|
+
revision: (existing?.revision ?? 0) + 1,
|
|
270
|
+
clientRequestId,
|
|
271
|
+
payloadHash,
|
|
272
|
+
blockedByTurnId,
|
|
273
|
+
text,
|
|
274
|
+
images: structuredClone(input.images),
|
|
275
|
+
state: "queued",
|
|
276
|
+
createdAt: existing?.createdAt ?? now,
|
|
277
|
+
updatedAt: now,
|
|
278
|
+
};
|
|
279
|
+
record.deferredTurn = deferredTurn;
|
|
280
|
+
record.updatedAt = now;
|
|
281
|
+
await writeSecureJson(recordPath(this.appData, sessionId), record);
|
|
282
|
+
return { ok: true, deferredTurn, idempotent: false };
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
async claimDeferredTurn(sessionId, expectedRevision) {
|
|
286
|
+
return this.serialize(sessionId, async () => {
|
|
287
|
+
const record = await this.get(sessionId);
|
|
288
|
+
const existing = record?.deferredTurn;
|
|
289
|
+
if (!record || !existing)
|
|
290
|
+
return { ok: false, code: "NOT_FOUND" };
|
|
291
|
+
if (existing.revision !== expectedRevision) {
|
|
292
|
+
return { ok: false, code: "REVISION_CONFLICT", deferredTurn: existing };
|
|
293
|
+
}
|
|
294
|
+
if (existing.state !== "queued") {
|
|
295
|
+
return { ok: false, code: "NOT_CLAIMABLE", deferredTurn: existing };
|
|
296
|
+
}
|
|
297
|
+
const next = {
|
|
298
|
+
...existing,
|
|
299
|
+
revision: existing.revision + 1,
|
|
300
|
+
state: "dispatching",
|
|
301
|
+
updatedAt: new Date().toISOString(),
|
|
302
|
+
};
|
|
303
|
+
record.deferredTurn = next;
|
|
304
|
+
record.updatedAt = next.updatedAt;
|
|
305
|
+
await writeSecureJson(recordPath(this.appData, sessionId), record);
|
|
306
|
+
return { ok: true, deferredTurn: next };
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
async transitionDeferredTurn(sessionId, input) {
|
|
310
|
+
return this.serialize(sessionId, async () => {
|
|
311
|
+
const record = await this.get(sessionId);
|
|
312
|
+
const existing = record?.deferredTurn;
|
|
313
|
+
if (!record || !existing || existing.revision !== input.expectedRevision)
|
|
314
|
+
return undefined;
|
|
315
|
+
const next = {
|
|
316
|
+
...existing,
|
|
317
|
+
revision: existing.revision + 1,
|
|
318
|
+
state: input.state,
|
|
319
|
+
updatedAt: new Date().toISOString(),
|
|
320
|
+
...(input.acceptedTurnId ? { acceptedTurnId: input.acceptedTurnId } : {}),
|
|
321
|
+
...(input.lastError ? { lastError: input.lastError } : {}),
|
|
322
|
+
};
|
|
323
|
+
if (!input.lastError)
|
|
324
|
+
delete next.lastError;
|
|
325
|
+
record.deferredTurn = next;
|
|
326
|
+
record.updatedAt = next.updatedAt;
|
|
327
|
+
await writeSecureJson(recordPath(this.appData, sessionId), record);
|
|
328
|
+
return next;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
async deleteDeferredTurn(sessionId, input) {
|
|
332
|
+
return this.serialize(sessionId, async () => {
|
|
333
|
+
const record = await this.get(sessionId);
|
|
334
|
+
const existing = record?.deferredTurn;
|
|
335
|
+
if (!record || !existing || existing.clientRequestId !== input.clientRequestId) {
|
|
336
|
+
return { ok: false, code: "NOT_FOUND" };
|
|
337
|
+
}
|
|
338
|
+
if (existing.state !== "queued" && existing.state !== "failed") {
|
|
339
|
+
return { ok: false, code: "NOT_CANCELLABLE", deferredTurn: existing };
|
|
340
|
+
}
|
|
341
|
+
if (existing.revision !== input.expectedRevision) {
|
|
342
|
+
return { ok: false, code: "REVISION_CONFLICT", deferredTurn: existing };
|
|
343
|
+
}
|
|
344
|
+
delete record.deferredTurn;
|
|
345
|
+
record.updatedAt = new Date().toISOString();
|
|
346
|
+
await writeSecureJson(recordPath(this.appData, sessionId), record);
|
|
347
|
+
return { ok: true, deferredTurn: existing };
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
async clearDeferredTurnForRequest(sessionId, clientRequestId) {
|
|
351
|
+
return this.serialize(sessionId, async () => {
|
|
352
|
+
const record = await this.get(sessionId);
|
|
353
|
+
const existing = record?.deferredTurn;
|
|
354
|
+
if (!record || !existing || existing.clientRequestId !== clientRequestId) {
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
delete record.deferredTurn;
|
|
358
|
+
record.updatedAt = new Date().toISOString();
|
|
359
|
+
await writeSecureJson(recordPath(this.appData, sessionId), record);
|
|
360
|
+
return { deferredTurn: existing, clearedRevision: existing.revision + 1 };
|
|
361
|
+
});
|
|
362
|
+
}
|
|
219
363
|
async appendMessage(sessionId, message) {
|
|
220
364
|
return this.serialize(sessionId, async () => {
|
|
221
365
|
const record = await this.get(sessionId);
|
|
@@ -340,10 +484,10 @@ export async function runChatTurn(options) {
|
|
|
340
484
|
},
|
|
341
485
|
};
|
|
342
486
|
}
|
|
343
|
-
await store.appendMessage(sessionId, { role: "user", text });
|
|
487
|
+
const userMessage = await store.appendMessage(sessionId, { role: "user", text });
|
|
344
488
|
const afterUserMessage = await store.get(sessionId);
|
|
345
489
|
if (afterUserMessage)
|
|
346
|
-
await options.onUserMessagePersisted?.(afterUserMessage);
|
|
490
|
+
await options.onUserMessagePersisted?.(afterUserMessage, userMessage);
|
|
347
491
|
let turnError;
|
|
348
492
|
// Collect persistence promises so runChatTurn does not resolve before the
|
|
349
493
|
// chat history / audit trail is durably written (also makes the outcome
|
|
@@ -216,9 +216,6 @@ export function buildTurnProcessGroups(input) {
|
|
|
216
216
|
current = startGroup(`assistant-${message.id}`);
|
|
217
217
|
current.assistants.push(message);
|
|
218
218
|
}
|
|
219
|
-
if (groups.length === 0 && input.toolCalls.length > 0) {
|
|
220
|
-
groups.push(emptyGroup("orphan-tools"));
|
|
221
|
-
}
|
|
222
219
|
const assigned = new Set();
|
|
223
220
|
const assistantToGroup = new Map();
|
|
224
221
|
for (const group of groups) {
|
|
@@ -265,17 +262,12 @@ export function buildTurnProcessGroups(input) {
|
|
|
265
262
|
const end = next?.user?.createdAt ?? Number.POSITIVE_INFINITY;
|
|
266
263
|
return tool.createdAt >= start && tool.createdAt < end;
|
|
267
264
|
});
|
|
265
|
+
// Time window only. Never dump unmatched tools onto the latest group:
|
|
266
|
+
// that is how a previous turn's bash cards jump under a new prompt.
|
|
268
267
|
if (windowGroup) {
|
|
269
268
|
windowGroup.tools.push(tool);
|
|
270
269
|
assigned.add(tool.id);
|
|
271
270
|
}
|
|
272
|
-
else {
|
|
273
|
-
const lastGroup = groups.at(-1);
|
|
274
|
-
if (lastGroup) {
|
|
275
|
-
lastGroup.tools.push(tool);
|
|
276
|
-
assigned.add(tool.id);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
271
|
}
|
|
280
272
|
for (const group of groups) {
|
|
281
273
|
group.tools.sort((a, b) => {
|
|
@@ -292,6 +284,14 @@ export function buildTurnProcessGroups(input) {
|
|
|
292
284
|
}
|
|
293
285
|
return groups;
|
|
294
286
|
}
|
|
287
|
+
/** Keep the most recent complete turns. Slice groups, never raw messages. */
|
|
288
|
+
export function selectRecentTurnGroups(groups, limit) {
|
|
289
|
+
if (limit <= 0)
|
|
290
|
+
return [];
|
|
291
|
+
if (groups.length <= limit)
|
|
292
|
+
return [...groups];
|
|
293
|
+
return groups.slice(-limit);
|
|
294
|
+
}
|
|
295
295
|
export function processGroupSummary(tools, options) {
|
|
296
296
|
const count = tools.length;
|
|
297
297
|
const thinkingCount = options?.thinkingCount ?? 0;
|
|
@@ -35,6 +35,32 @@ import { hashDagBytes, } from "./dag-confirmation.js";
|
|
|
35
35
|
* framework invocations that don't embed the word "verify".
|
|
36
36
|
*/
|
|
37
37
|
export const STRUCTURED_VERIFICATION_COMMAND_RE = /\b(vitest|jest|playwright|cypress|pytest|py-test|go\s+test|cargo\s+test|npm\s+run\s+(lint|typecheck|test|verify)|npm\s+(test|run\s+test)|check-repo\.sh|loop-agent-standard-verify)\b/i;
|
|
38
|
+
/**
|
|
39
|
+
* Deterministic shell preset keys whose executors are themselves structured
|
|
40
|
+
* verification/gate materializers: their shell.commands[] is empty and the
|
|
41
|
+
* preset alone carries the verification authority (hash-bound artifact
|
|
42
|
+
* gates, evidence validators, report finalize). Mirrors the named-shell
|
|
43
|
+
* handler registry in shell-executor.ts so preset-only workflows
|
|
44
|
+
* (frontend-test finalize/reports, backend-test pipeline, verify-evidence
|
|
45
|
+
* gates) pass the G2 autonomous execution gate without depending on node-id
|
|
46
|
+
* naming conventions.
|
|
47
|
+
*/
|
|
48
|
+
export const STRUCTURED_VERIFICATION_SHELL_PRESETS = new Set([
|
|
49
|
+
"frontendTestCaseChecklist",
|
|
50
|
+
"frontendTestCaseManifest",
|
|
51
|
+
"frontendTestEvidenceValidation",
|
|
52
|
+
"frontendTestResultFinalize",
|
|
53
|
+
"frontendTestL5Report",
|
|
54
|
+
"frontendTestHtmlReport",
|
|
55
|
+
"frontendTestReports",
|
|
56
|
+
"backendTestPipeline",
|
|
57
|
+
"verifyEvidence",
|
|
58
|
+
"repairArtifactGate",
|
|
59
|
+
"jacocoCoverage",
|
|
60
|
+
"requirementCoverageGate",
|
|
61
|
+
"jsonArtifactGate",
|
|
62
|
+
"verdictGate",
|
|
63
|
+
]);
|
|
38
64
|
export const DEFAULT_EXECUTION_RECEIPT_TTL_MS = 30 * 60 * 1000;
|
|
39
65
|
function normalizePathEntry(entry) {
|
|
40
66
|
return entry.trim().replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -157,6 +183,13 @@ export function deriveEligibilityFactsFromDagSpec(input) {
|
|
|
157
183
|
return false;
|
|
158
184
|
if (/verify|verification/i.test(task.id ?? ""))
|
|
159
185
|
return true;
|
|
186
|
+
// Preset-only verification shells (empty commands[]): the preset key is
|
|
187
|
+
// the verification authority (frontend-test finalize, backend-test
|
|
188
|
+
// pipeline, evidence/artifact gates).
|
|
189
|
+
const shellRecord = task.shell;
|
|
190
|
+
const hasVerificationPreset = Object.keys(shellRecord).some((key) => STRUCTURED_VERIFICATION_SHELL_PRESETS.has(key));
|
|
191
|
+
if (hasVerificationPreset)
|
|
192
|
+
return true;
|
|
160
193
|
const commands = stringList(task.shell.commands);
|
|
161
194
|
return commands.some((command) => STRUCTURED_VERIFICATION_COMMAND_RE.test(command));
|
|
162
195
|
});
|
|
@@ -80,3 +80,21 @@ export function pickInspectNode(nodes, preferredNodeId) {
|
|
|
80
80
|
});
|
|
81
81
|
return running ?? nodes[0];
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Terminal DagRun effective/status values (mirrors observe read-model
|
|
85
|
+
* TERMINAL_RUN_STATUSES). A run that reached one of these states no longer
|
|
86
|
+
* needs live polling in the console split panel.
|
|
87
|
+
*/
|
|
88
|
+
export const TERMINAL_DAG_RUN_STATUSES = new Set([
|
|
89
|
+
"finished",
|
|
90
|
+
"failed",
|
|
91
|
+
"partial_failed",
|
|
92
|
+
"superseded",
|
|
93
|
+
"abandoned",
|
|
94
|
+
]);
|
|
95
|
+
export function isTerminalDagRun(dag) {
|
|
96
|
+
if (!dag)
|
|
97
|
+
return false;
|
|
98
|
+
const status = String(dag.effectiveStatus ?? dag.status ?? "").toLowerCase();
|
|
99
|
+
return TERMINAL_DAG_RUN_STATUSES.has(status);
|
|
100
|
+
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - A run summary is a projection only: it can never change the terminal
|
|
14
14
|
* operation state decided by the command envelope/exit facts.
|
|
15
15
|
*/
|
|
16
|
+
import { normalizeDagFailureCategory } from "../../shared/dag-failure-category.js";
|
|
16
17
|
import { scrubSecrets } from "./chat/explore-tools.js";
|
|
17
18
|
const RUN_SUMMARY_MAX_NODES = 20;
|
|
18
19
|
const RUN_SUMMARY_MAX_TEXT_CHARS = 600;
|
|
@@ -72,7 +73,13 @@ function safeNode(value) {
|
|
|
72
73
|
const status = boundedRedactedText(rec.status, 100);
|
|
73
74
|
if (!id || !status)
|
|
74
75
|
return undefined;
|
|
75
|
-
|
|
76
|
+
const rawCategory = boundedRedactedText(rec.failureCategory, 200);
|
|
77
|
+
if (!rawCategory)
|
|
78
|
+
return { id, status };
|
|
79
|
+
const category = normalizeDagFailureCategory(rawCategory, status);
|
|
80
|
+
if (category === "success" || category === "skipped")
|
|
81
|
+
return { id, status };
|
|
82
|
+
return { id, status, failureCategory: category };
|
|
76
83
|
}
|
|
77
84
|
/** Independent allowlist projection of a structured run summary. */
|
|
78
85
|
export function safeRunSummaryForProjection(value) {
|
|
@@ -117,13 +124,22 @@ export function safeRunSummaryForProjection(value) {
|
|
|
117
124
|
}
|
|
118
125
|
}
|
|
119
126
|
const verdict = boundedRedactedText(rec.verdict);
|
|
120
|
-
const
|
|
127
|
+
const rawFailureCategory = boundedRedactedText(rec.failureCategory, 200);
|
|
128
|
+
// Normalize the run-level category too (2026-08-23): raw codes like
|
|
129
|
+
// missing-api-key / nonzero-exit never matched the browser advice table.
|
|
130
|
+
const failureCategory = rawFailureCategory !== undefined
|
|
131
|
+
? normalizeDagFailureCategory(rawFailureCategory, status)
|
|
132
|
+
: undefined;
|
|
121
133
|
return {
|
|
122
134
|
runId,
|
|
123
135
|
status,
|
|
124
136
|
nodes,
|
|
125
137
|
...(verdict !== undefined ? { verdict } : {}),
|
|
126
|
-
...(failureCategory !== undefined
|
|
138
|
+
...(failureCategory !== undefined &&
|
|
139
|
+
failureCategory !== "success" &&
|
|
140
|
+
failureCategory !== "skipped"
|
|
141
|
+
? { failureCategory }
|
|
142
|
+
: {}),
|
|
127
143
|
...(outOfBounds.length > 0 ? { outOfBounds } : {}),
|
|
128
144
|
...(next ? { next } : {}),
|
|
129
145
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { access, readFile } from "node:fs/promises";
|
|
2
|
-
import { buildOperatorCapabilitiesDocument } from "../../shared/operator/capabilities.js";
|
|
2
|
+
import { buildOperatorCapabilitiesDocument, OFFICIAL_TAIL_ACTION_IDS, } from "../../shared/operator/capabilities.js";
|
|
3
|
+
import { DAG_RERUN_PROMPT_OVERRIDE_ENV, isDagRerunPromptOverrideEnabled, MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS, } from "../../shared/dag-prompt-override.js";
|
|
3
4
|
import { buildOperatorResult, operatorFailed, operatorSucceeded, } from "../../shared/operator/envelope.js";
|
|
4
5
|
import { CANONICAL_TASK_ID_PATTERN, formatLocalBusinessDate, getTaskPaths, loadTaskConfig, } from "../../task/runtime.js";
|
|
5
6
|
import { diffTaskContract, validateDraftForTask, } from "../../task/contract/index.js";
|
|
@@ -401,6 +402,85 @@ async function runReadCli(ctx, args, command) {
|
|
|
401
402
|
});
|
|
402
403
|
}
|
|
403
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* Parse and bound the experimental promptOverrides action param. The env
|
|
407
|
+
* switch is checked separately so disabled deployments fail closed before any
|
|
408
|
+
* staging happens. `mode` defaults to "replace"; "append" keeps the frozen
|
|
409
|
+
* base prompt and only appends the operator text as a marked addendum.
|
|
410
|
+
*/
|
|
411
|
+
function parsePromptOverrideParams(raw) {
|
|
412
|
+
if (raw === undefined || raw === null)
|
|
413
|
+
return { kind: "ok", overrides: [] };
|
|
414
|
+
if (!Array.isArray(raw)) {
|
|
415
|
+
return {
|
|
416
|
+
kind: "error",
|
|
417
|
+
message: "promptOverrides must be an array of {nodeId, prompt, mode?}",
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
if (raw.length === 0)
|
|
421
|
+
return { kind: "ok", overrides: [] };
|
|
422
|
+
if (raw.length > 8) {
|
|
423
|
+
return { kind: "error", message: "promptOverrides supports at most 8 nodes" };
|
|
424
|
+
}
|
|
425
|
+
const overrides = [];
|
|
426
|
+
const seen = new Set();
|
|
427
|
+
for (const item of raw) {
|
|
428
|
+
if (!item || typeof item !== "object") {
|
|
429
|
+
return {
|
|
430
|
+
kind: "error",
|
|
431
|
+
message: "promptOverrides entries must be objects {nodeId, prompt, mode?}",
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
const record = item;
|
|
435
|
+
const nodeId = typeof record.nodeId === "string" ? record.nodeId.trim() : "";
|
|
436
|
+
const prompt = typeof record.prompt === "string" ? record.prompt : "";
|
|
437
|
+
const mode = record.mode === undefined || record.mode === null
|
|
438
|
+
? undefined
|
|
439
|
+
: typeof record.mode === "string"
|
|
440
|
+
? record.mode
|
|
441
|
+
: "invalid";
|
|
442
|
+
if (!nodeId) {
|
|
443
|
+
return { kind: "error", message: "promptOverrides nodeId is required" };
|
|
444
|
+
}
|
|
445
|
+
if (seen.has(nodeId)) {
|
|
446
|
+
return {
|
|
447
|
+
kind: "error",
|
|
448
|
+
message: `promptOverrides contains duplicate nodeId: ${nodeId}`,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
seen.add(nodeId);
|
|
452
|
+
if (mode !== undefined && mode !== "append" && mode !== "replace") {
|
|
453
|
+
return {
|
|
454
|
+
kind: "error",
|
|
455
|
+
message: `promptOverrides mode for ${nodeId} must be append or replace`,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
if (!prompt.trim() || prompt.length > MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS) {
|
|
459
|
+
return {
|
|
460
|
+
kind: "error",
|
|
461
|
+
message: `promptOverrides prompt for ${nodeId} must be 1-${MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS} characters`,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
overrides.push(mode === undefined ? { nodeId, prompt } : { nodeId, prompt, mode });
|
|
465
|
+
}
|
|
466
|
+
return { kind: "ok", overrides };
|
|
467
|
+
}
|
|
468
|
+
/** Stage override bodies under app-data and build `--prompt-override` CLI
|
|
469
|
+
* args. The flag carries the mode: `--prompt-override-append` for append. */
|
|
470
|
+
async function buildPromptOverrideCliArgs(ctx, overrides) {
|
|
471
|
+
const args = [];
|
|
472
|
+
for (const override of overrides) {
|
|
473
|
+
const staged = await stageUtf8Text(ctx.appData, override.prompt, {
|
|
474
|
+
extension: ".md",
|
|
475
|
+
label: `prompt-override-${override.nodeId}`,
|
|
476
|
+
});
|
|
477
|
+
const flag = override.mode === "append"
|
|
478
|
+
? "--prompt-override-append"
|
|
479
|
+
: "--prompt-override";
|
|
480
|
+
args.push(flag, `${override.nodeId}@${staged.path}`);
|
|
481
|
+
}
|
|
482
|
+
return args;
|
|
483
|
+
}
|
|
404
484
|
async function acceptOperation(ctx, input) {
|
|
405
485
|
const created = await ctx.operations.create({
|
|
406
486
|
clientRequestId: input.clientRequestId,
|
|
@@ -672,6 +752,80 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
672
752
|
const body = await runReadCli(ctx, ["dag", "report", "--run-id", runId, "--json"], "dag report");
|
|
673
753
|
return { kind: "sync", status: body.ok ? 200 : 400, body };
|
|
674
754
|
}
|
|
755
|
+
case "dagDoctor": {
|
|
756
|
+
const runId = str(p.runId) ?? str(p.dagRunId);
|
|
757
|
+
const args = ["dag", "doctor"];
|
|
758
|
+
if (runId)
|
|
759
|
+
args.push("--run-id", runId);
|
|
760
|
+
const body = await runReadCli(ctx, args, "dag doctor");
|
|
761
|
+
return { kind: "sync", status: body.ok ? 200 : 400, body };
|
|
762
|
+
}
|
|
763
|
+
case "dagStatus": {
|
|
764
|
+
const runId = str(p.runId) ?? str(p.dagRunId);
|
|
765
|
+
if (!runId)
|
|
766
|
+
return invalid(action, "runId is required");
|
|
767
|
+
const body = await runReadCli(ctx, ["dag", "status", "--run-id", runId], "dag status");
|
|
768
|
+
return { kind: "sync", status: body.ok ? 200 : 400, body };
|
|
769
|
+
}
|
|
770
|
+
case "workerPoolDoctor": {
|
|
771
|
+
const taskId = str(p.taskId);
|
|
772
|
+
const run = ctx.runCommand ??
|
|
773
|
+
((args, options) => ctx.client.runExternal
|
|
774
|
+
? ctx.client.runExternal(resolveSiblingAgentWorkerBin(), args, options)
|
|
775
|
+
: ctx.client.run(args, options));
|
|
776
|
+
let result;
|
|
777
|
+
try {
|
|
778
|
+
result = await run(["pool", "doctor", "--repo", ctx.repoRoot, "--json"], {
|
|
779
|
+
cwd: ctx.repoRoot,
|
|
780
|
+
artifactName: "read-pool-doctor",
|
|
781
|
+
expectJson: true,
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
catch (error) {
|
|
785
|
+
return {
|
|
786
|
+
kind: "error",
|
|
787
|
+
status: 400,
|
|
788
|
+
body: operatorFailed({
|
|
789
|
+
command: action,
|
|
790
|
+
outcome: "invalid",
|
|
791
|
+
code: "INVALID_INPUT",
|
|
792
|
+
message: `cannot read pool doctor facts: ${error instanceof Error ? error.message : String(error)}`,
|
|
793
|
+
}),
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
if (!result.ok || result.exitCode !== 0 || !result.json) {
|
|
797
|
+
return {
|
|
798
|
+
kind: "error",
|
|
799
|
+
status: 400,
|
|
800
|
+
body: operatorFailed({
|
|
801
|
+
command: action,
|
|
802
|
+
outcome: "invalid",
|
|
803
|
+
code: "INVALID_INPUT",
|
|
804
|
+
message: summarizeReadCliFailure(result),
|
|
805
|
+
}),
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
const inventory = result.json;
|
|
809
|
+
if (!taskId) {
|
|
810
|
+
return {
|
|
811
|
+
kind: "sync",
|
|
812
|
+
status: 200,
|
|
813
|
+
body: operatorSucceeded(action, inventory),
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
const v2 = (inventory.v2 ?? []).filter((entry) => entry.taskId === taskId);
|
|
817
|
+
const legacy = (inventory.legacy ?? []).filter((entry) => entry.taskId === taskId);
|
|
818
|
+
return {
|
|
819
|
+
kind: "sync",
|
|
820
|
+
status: 200,
|
|
821
|
+
body: operatorSucceeded(action, {
|
|
822
|
+
...inventory,
|
|
823
|
+
v2,
|
|
824
|
+
legacy,
|
|
825
|
+
focusedTaskId: taskId,
|
|
826
|
+
}),
|
|
827
|
+
};
|
|
828
|
+
}
|
|
675
829
|
case "status": {
|
|
676
830
|
const taskId = str(p.taskId);
|
|
677
831
|
if (!taskId) {
|
|
@@ -986,7 +1140,7 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
986
1140
|
}
|
|
987
1141
|
if (operatorAction) {
|
|
988
1142
|
const target = buildOperatorCapabilitiesDocument().actions.find((entry) => entry.action === operatorAction &&
|
|
989
|
-
entry.
|
|
1143
|
+
OFFICIAL_TAIL_ACTION_IDS.has(entry.action) &&
|
|
990
1144
|
entry.humanConfirmation === "required");
|
|
991
1145
|
if (!target)
|
|
992
1146
|
return invalid(action, "operatorAction must name a required official action");
|
|
@@ -2004,12 +2158,35 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
2004
2158
|
}
|
|
2005
2159
|
return accepted;
|
|
2006
2160
|
}
|
|
2161
|
+
case "dagPromptOverrideConfig": {
|
|
2162
|
+
// Experimental internal switch; the browser UI hides the prompt-edit
|
|
2163
|
+
// entry unless this reports enabled=true.
|
|
2164
|
+
return {
|
|
2165
|
+
kind: "sync",
|
|
2166
|
+
status: 200,
|
|
2167
|
+
body: operatorSucceeded(action, {
|
|
2168
|
+
enabled: isDagRerunPromptOverrideEnabled(),
|
|
2169
|
+
envVar: DAG_RERUN_PROMPT_OVERRIDE_ENV,
|
|
2170
|
+
}),
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2007
2173
|
case "dagRerunPlan": {
|
|
2008
2174
|
const runId = str(p.runId);
|
|
2009
2175
|
const fromNode = str(p.fromNode);
|
|
2010
2176
|
if (!runId || !fromNode) {
|
|
2011
2177
|
return invalid(action, "runId and fromNode are required");
|
|
2012
2178
|
}
|
|
2179
|
+
const parsedOverrides = parsePromptOverrideParams(p.promptOverrides);
|
|
2180
|
+
if (parsedOverrides.kind === "error") {
|
|
2181
|
+
return invalid(action, parsedOverrides.message);
|
|
2182
|
+
}
|
|
2183
|
+
if (parsedOverrides.overrides.length > 0 &&
|
|
2184
|
+
!isDagRerunPromptOverrideEnabled()) {
|
|
2185
|
+
return invalid(action, `prompt override rerun is disabled; set ${DAG_RERUN_PROMPT_OVERRIDE_ENV}=1 to enable this experimental capability`);
|
|
2186
|
+
}
|
|
2187
|
+
const overrideArgs = parsedOverrides.overrides.length > 0
|
|
2188
|
+
? await buildPromptOverrideCliArgs(ctx, parsedOverrides.overrides)
|
|
2189
|
+
: [];
|
|
2013
2190
|
const body = await runReadCli(ctx, [
|
|
2014
2191
|
"dag",
|
|
2015
2192
|
"rerun",
|
|
@@ -2018,6 +2195,7 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
2018
2195
|
"--from-node",
|
|
2019
2196
|
fromNode,
|
|
2020
2197
|
"--plan",
|
|
2198
|
+
...overrideArgs,
|
|
2021
2199
|
"--json",
|
|
2022
2200
|
], "dag rerun plan");
|
|
2023
2201
|
return { kind: "sync", status: body.ok ? 200 : 400, body };
|
|
@@ -2031,6 +2209,17 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
2031
2209
|
if (!runId || !fromNode || !planHash || !reason || !clientRequestId) {
|
|
2032
2210
|
return invalid(action, "runId, fromNode, planHash, reason, and clientRequestId are required");
|
|
2033
2211
|
}
|
|
2212
|
+
const parsedOverrides = parsePromptOverrideParams(p.promptOverrides);
|
|
2213
|
+
if (parsedOverrides.kind === "error") {
|
|
2214
|
+
return invalid(action, parsedOverrides.message);
|
|
2215
|
+
}
|
|
2216
|
+
if (parsedOverrides.overrides.length > 0 &&
|
|
2217
|
+
!isDagRerunPromptOverrideEnabled()) {
|
|
2218
|
+
return invalid(action, `prompt override rerun is disabled; set ${DAG_RERUN_PROMPT_OVERRIDE_ENV}=1 to enable this experimental capability`);
|
|
2219
|
+
}
|
|
2220
|
+
const overrideArgs = parsedOverrides.overrides.length > 0
|
|
2221
|
+
? await buildPromptOverrideCliArgs(ctx, parsedOverrides.overrides)
|
|
2222
|
+
: [];
|
|
2034
2223
|
return acceptOperation(ctx, {
|
|
2035
2224
|
action,
|
|
2036
2225
|
actionParams: p,
|
|
@@ -2048,6 +2237,7 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
2048
2237
|
clientRequestId,
|
|
2049
2238
|
"--reason",
|
|
2050
2239
|
reason,
|
|
2240
|
+
...overrideArgs,
|
|
2051
2241
|
"--json",
|
|
2052
2242
|
],
|
|
2053
2243
|
});
|
|
@@ -2536,8 +2726,9 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
2536
2726
|
// M5 official-command coverage uses one generic tail for newly registered
|
|
2537
2727
|
// CLI actions. It remains inside dispatchOperatorAction (the sole dispatch
|
|
2538
2728
|
// surface) and only accepts argv arrays declared by the capability schema.
|
|
2539
|
-
|
|
2540
|
-
|
|
2729
|
+
// 2026-08-18: high-frequency entries got real descriptions; membership is
|
|
2730
|
+
// now derived from OFFICIAL_TAIL_ACTION_IDS, not description prefixes.
|
|
2731
|
+
const capability = buildOperatorCapabilitiesDocument().actions.find((entry) => entry.action === action && OFFICIAL_TAIL_ACTION_IDS.has(entry.action));
|
|
2541
2732
|
if (capability) {
|
|
2542
2733
|
const rawArgs = p.args;
|
|
2543
2734
|
if (rawArgs !== undefined &&
|
|
@@ -3,7 +3,7 @@ import { rewriteRecoveryOperatorError } from "./recovery-error-copy.js";
|
|
|
3
3
|
const MUTATION_GATE_COPY = [
|
|
4
4
|
{
|
|
5
5
|
pattern: /invalid (boot capability|confirmation header) token/i,
|
|
6
|
-
copy: "
|
|
6
|
+
copy: "页面凭证已失效(控制台可能已重启),请刷新页面(F5)后重试。",
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
pattern: /mutation requires boot capability cookie|mutation requires confirmation header token/i,
|
|
@@ -23,7 +23,7 @@ export function formatOperatorUserError(message) {
|
|
|
23
23
|
if (recovery)
|
|
24
24
|
return recovery;
|
|
25
25
|
if (/controller identity changed|package fingerprint changed|entry binary sha256 changed/i.test(raw)) {
|
|
26
|
-
return "控制器版本已变更(Console
|
|
26
|
+
return "控制器版本已变更(Console 启动后代码被重新编译,或提交了 package.json / bin / dist / skills 等指纹范围文件)。请重启 Console 进程使其重新加载当前构建后再重试本操作;一次会话中途不要重建控制器。";
|
|
27
27
|
}
|
|
28
28
|
if (/Pi session file missing or not openable|cannot reopen chat session .* no persisted Pi session file/i.test(raw)) {
|
|
29
29
|
return "该对话的 Pi 会话文件已丢失,无法恢复上下文。请新建对话继续(历史记录无法重开)。";
|
|
@@ -85,7 +85,7 @@ function hasProviderConfig(snapshot) {
|
|
|
85
85
|
/**
|
|
86
86
|
* Discover live Pi SDK / auth / model readiness via ModelRuntime.
|
|
87
87
|
* Kept inside worker/console (dynamic import) so Worker does not depend on
|
|
88
|
-
* executors; contract matches pi-sdk-executor 0.
|
|
88
|
+
* executors; contract matches pi-sdk-executor 0.83.0.
|
|
89
89
|
*/
|
|
90
90
|
export async function discoverPiReadinessProbe(options) {
|
|
91
91
|
const repoRoot = options?.repoRoot ?? process.cwd();
|
|
@@ -118,7 +118,7 @@ export async function discoverPiReadinessProbe(options) {
|
|
|
118
118
|
modelsAvailable: false,
|
|
119
119
|
interviewSessionOk: false,
|
|
120
120
|
details: {
|
|
121
|
-
sdkMessage: "Pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.
|
|
121
|
+
sdkMessage: "Pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.83.0)",
|
|
122
122
|
authMessage: "skipped (sdk incompatible)",
|
|
123
123
|
modelsMessage: "skipped (sdk incompatible)",
|
|
124
124
|
sessionMessage: "skipped (sdk incompatible)",
|
|
@@ -159,7 +159,7 @@ export async function discoverPiReadinessProbe(options) {
|
|
|
159
159
|
modelsAvailable: false,
|
|
160
160
|
interviewSessionOk: false,
|
|
161
161
|
details: {
|
|
162
|
-
sdkMessage: "Pi SDK 0.
|
|
162
|
+
sdkMessage: "Pi SDK 0.83.0 contract available",
|
|
163
163
|
authMessage: `ModelRuntime.create failed: ${message}`,
|
|
164
164
|
modelsMessage: "skipped (runtime init failed)",
|
|
165
165
|
sessionMessage: "skipped (runtime init failed)",
|
|
@@ -222,8 +222,9 @@ export async function discoverPiReadinessProbe(options) {
|
|
|
222
222
|
authReady,
|
|
223
223
|
modelsAvailable,
|
|
224
224
|
interviewSessionOk,
|
|
225
|
+
piAvailableModels: availableModels,
|
|
225
226
|
details: {
|
|
226
|
-
sdkMessage: `Pi SDK 0.
|
|
227
|
+
sdkMessage: `Pi SDK 0.83.0 contract available (agentDir=${agentDir})`,
|
|
227
228
|
authMessage: authReady
|
|
228
229
|
? `credentials/providers ready (available=${availableCount}, providerConfig=${providerConfigured})`
|
|
229
230
|
: "no available models and no stored/configured provider credentials (set Pi auth or provider API keys)",
|