minimal-agent 0.12.0 → 0.14.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/README.md +6 -5
- package/package.json +62 -5
- package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
- package/plugins/cron-runner/src/commands.js +4 -4
- package/plugins/cron-runner/src/commands.ts +4 -4
- package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
- package/plugins/workflow-runner/commands/workflow.md +57 -90
- package/plugins/workflow-runner/commands/workflows.md +4 -4
- package/plugins/workflow-runner/plugin.js +24 -13
- package/plugins/workflow-runner/plugin.ts +28 -13
- package/plugins/workflow-runner/src/discovery.js +160 -0
- package/plugins/workflow-runner/src/discovery.ts +194 -0
- package/plugins/workflow-runner/src/goalAnchor.js +53 -0
- package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
- package/plugins/workflow-runner/src/index.js +204 -185
- package/plugins/workflow-runner/src/index.ts +249 -224
- package/plugins/workflow-runner/src/inputCoercion.js +40 -0
- package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
- package/plugins/workflow-runner/src/journal.js +224 -0
- package/plugins/workflow-runner/src/journal.ts +274 -0
- package/plugins/workflow-runner/src/jsonSchema.js +132 -0
- package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
- package/plugins/workflow-runner/src/moduleResolution.js +64 -0
- package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
- package/plugins/workflow-runner/src/resourceInputs.js +220 -0
- package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
- package/plugins/workflow-runner/src/runner.js +404 -467
- package/plugins/workflow-runner/src/runner.ts +491 -535
- package/plugins/workflow-runner/src/runtime/config.js +46 -0
- package/plugins/workflow-runner/src/runtime/config.ts +69 -0
- package/plugins/workflow-runner/src/runtime/context.js +1304 -0
- package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
- package/plugins/workflow-runner/src/runtime/exec.js +34 -0
- package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
- package/plugins/workflow-runner/src/runtime/flow.js +8 -0
- package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
- package/plugins/workflow-runner/src/runtime/fs.js +27 -0
- package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
- package/plugins/workflow-runner/src/runtime/model.js +6 -0
- package/plugins/workflow-runner/src/runtime/model.ts +8 -0
- package/plugins/workflow-runner/src/runtime/output.js +7 -0
- package/plugins/workflow-runner/src/runtime/output.ts +21 -0
- package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
- package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
- package/plugins/workflow-runner/src/runtime/state.js +16 -0
- package/plugins/workflow-runner/src/runtime/state.ts +19 -0
- package/plugins/workflow-runner/src/runtime/tools.js +3 -0
- package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
- package/plugins/workflow-runner/src/sandbox.js +141 -0
- package/plugins/workflow-runner/src/sandbox.ts +182 -0
- package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
- package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
- package/plugins/workflow-runner/src/taskIdentity.js +172 -152
- package/plugins/workflow-runner/src/taskIdentity.ts +233 -200
- package/plugins/workflow-runner/src/toolRuntime.js +29 -8
- package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
- package/plugins/workflow-runner/src/types.js +7 -105
- package/plugins/workflow-runner/src/types.ts +155 -520
- package/plugins/workflow-runner/src/util.js +47 -0
- package/plugins/workflow-runner/src/util.ts +51 -0
- package/plugins/workflow-runner/src/workflowContext.js +20 -0
- package/plugins/workflow-runner/src/workflowContext.ts +58 -0
- package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
- package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
- package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
- package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
- package/plugins/workflow-runner/src/workflowState.js +542 -469
- package/plugins/workflow-runner/src/workflowState.ts +639 -589
- package/plugins/workflow-runner/test/api.test.ts +223 -0
- package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
- package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
- package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
- package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
- package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
- package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
- package/plugins/workflow-runner/test/discovery.test.ts +234 -0
- package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
- package/plugins/workflow-runner/test/goalController.test.ts +317 -0
- package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
- package/plugins/workflow-runner/test/listResources.test.ts +106 -0
- package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
- package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
- package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
- package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
- package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
- package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
- package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
- package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
- package/skills/pptx/examples/assets/cover-bg.svg +23 -0
- package/skills/pptx/examples/assets/icon-spark.svg +5 -0
- package/skills/skill-creator/scripts/run_eval.py +3 -2
- package/skills/ui-design/SKILL.md +5 -2
- package/skills/ui-design/references/layout-composition.md +1 -1
- package/skills/workflow-creator/SKILL.md +344 -0
- package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
- package/skills/workflow-creator/references/api.md +216 -0
- package/skills/workflow-creator/references/migration.md +103 -0
- package/skills/workflow-creator/references/patterns.md +779 -0
- package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
- package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
- package/skills/workflow-creator/scripts/validate.mjs +275 -0
- package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
- package/src/advisor/advisorCoordinator.js +503 -0
- package/src/advisor/advisorCoordinator.ts +657 -0
- package/src/cli/configMenu.tsx +0 -1
- package/src/cli/print.js +44 -6
- package/src/cli/print.ts +73 -4
- package/src/cli/streamJson.js +5 -6
- package/src/cli/streamJson.ts +8 -4
- package/src/cli/wizards/advisorWizard.js +4 -2
- package/src/cli/wizards/advisorWizard.tsx +8 -2
- package/src/cli/wizards/contextAdvisorWizard.js +5 -4
- package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
- package/src/collaboration/taskFlow.js +115 -0
- package/src/collaboration/taskFlow.ts +169 -0
- package/src/config/configDefaults.js +41 -1
- package/src/config/configDefaults.ts +43 -1
- package/src/config/configDiagnostics.ts +2 -2
- package/src/config/configFile.js +40 -9
- package/src/config/configFile.ts +60 -11
- package/src/config/configService.js +443 -74
- package/src/config/configService.ts +723 -142
- package/src/config/numericLimits.js +22 -0
- package/src/config/numericLimits.ts +24 -0
- package/src/config.js +1 -0
- package/src/config.ts +3 -2
- package/src/context/budgetPlan.js +1 -1
- package/src/context/budgetPlan.ts +1 -1
- package/src/context/compact.js +7 -2
- package/src/context/compact.ts +9 -2
- package/src/context/microCompactLite.js +212 -13
- package/src/context/microCompactLite.ts +261 -12
- package/src/context/sessionRegistry.js +8 -1
- package/src/context/sessionRegistry.ts +18 -5
- package/src/context/toolResultArchive.js +72 -0
- package/src/context/toolResultArchive.ts +95 -0
- package/src/goal/errors.js +8 -0
- package/src/goal/errors.ts +18 -0
- package/src/goal/goalRuntime.js +344 -0
- package/src/goal/goalRuntime.ts +466 -0
- package/src/goal/goalStore.js +200 -0
- package/src/goal/goalStore.ts +219 -0
- package/src/goal/index.js +4 -0
- package/src/goal/index.ts +4 -0
- package/src/goal/statusMapping.js +71 -0
- package/src/goal/statusMapping.ts +95 -0
- package/src/goal/types.js +7 -0
- package/src/goal/types.ts +179 -0
- package/src/goal/workflowController.js +306 -0
- package/src/goal/workflowController.ts +360 -0
- package/src/harness/evidenceLedger.js +2 -0
- package/src/harness/evidenceLedger.ts +1 -0
- package/src/harness/stallGuard.js +9 -3
- package/src/harness/stallGuard.ts +7 -3
- package/src/images/imageResolver.js +161 -0
- package/src/images/imageResolver.ts +189 -0
- package/src/infra/verification.js +56 -20
- package/src/infra/verification.ts +87 -20
- package/src/llm/client.js +79 -21
- package/src/llm/client.ts +182 -35
- package/src/llm/gatewayClient.js +201 -0
- package/src/llm/gatewayClient.ts +299 -0
- package/src/llm/messageProvenance.js +46 -0
- package/src/llm/messageProvenance.ts +58 -0
- package/src/llm/modelProvider.js +1 -0
- package/src/llm/modelProvider.ts +22 -0
- package/src/llm/providers/anthropicFormat.js +108 -1
- package/src/llm/providers/anthropicFormat.ts +115 -4
- package/src/llm/providers/openaiFormat.js +9 -0
- package/src/llm/providers/openaiFormat.ts +12 -3
- package/src/loop.js +319 -54
- package/src/loop.ts +373 -57
- package/src/main.js +4 -1
- package/src/main.tsx +4 -1
- package/src/managedExecution/index.js +2 -0
- package/src/managedExecution/index.ts +2 -0
- package/src/managedExecution/registry.js +15 -0
- package/src/managedExecution/registry.ts +20 -0
- package/src/managedExecution/types.js +1 -0
- package/src/managedExecution/types.ts +86 -0
- package/src/modelRef.js +9 -0
- package/src/modelRef.ts +31 -0
- package/src/multimodal.js +14 -28
- package/src/multimodal.ts +17 -33
- package/src/net/lanDiscovery.js +65 -0
- package/src/net/lanDiscovery.ts +82 -0
- package/src/plugin-sdk.js +28 -3
- package/src/plugin-sdk.ts +39 -6
- package/src/plugins/pluginApi.ts +16 -1
- package/src/plugins/pluginLoader.js +4 -0
- package/src/plugins/pluginLoader.ts +6 -0
- package/src/plugins/pluginRunner.js +152 -9
- package/src/plugins/pluginRunner.ts +186 -8
- package/src/plugins/slashFallback.js +113 -0
- package/src/plugins/slashFallback.ts +137 -0
- package/src/prompts/policyRegistry.js +15 -8
- package/src/prompts/policyRegistry.ts +18 -8
- package/src/prompts/promptAssembly.js +103 -12
- package/src/prompts/promptAssembly.ts +114 -12
- package/src/prompts/skillList.js +21 -3
- package/src/prompts/skillList.ts +27 -2
- package/src/prompts/stableSystemPrompt.js +18 -0
- package/src/prompts/stableSystemPrompt.ts +33 -0
- package/src/runtime/agentRuntimeContext.js +8 -2
- package/src/runtime/agentRuntimeContext.ts +17 -4
- package/src/session/runAgentTurn.js +78 -12
- package/src/session/runAgentTurn.ts +105 -23
- package/src/session/runTurn.js +10 -9
- package/src/session/runTurn.ts +17 -8
- package/src/session/sessionState.ts +2 -0
- package/src/team/auth.js +186 -0
- package/src/team/auth.ts +298 -0
- package/src/team/cli.js +105 -0
- package/src/team/cli.ts +122 -0
- package/src/team/onlineToolContract.js +64 -0
- package/src/team/onlineToolContract.ts +109 -0
- package/src/team/toolPolicy.js +36 -0
- package/src/team/toolPolicy.ts +75 -0
- package/src/tools/advisor/advisor.js +148 -37
- package/src/tools/advisor/advisor.ts +161 -38
- package/src/tools/advisor/advisorDecision.js +71 -0
- package/src/tools/advisor/advisorDecision.ts +85 -0
- package/src/tools/agent/agent.js +18 -5
- package/src/tools/agent/agent.ts +21 -7
- package/src/tools/agent/agentControl.js +246 -0
- package/src/tools/agent/agentControl.ts +284 -0
- package/src/tools/agent/agentTaskService.js +40 -1
- package/src/tools/agent/agentTaskService.ts +44 -1
- package/src/tools/agent/agentTypes.js +14 -2
- package/src/tools/agent/agentTypes.ts +15 -2
- package/src/tools/applypatch/applyPatch.js +362 -0
- package/src/tools/applypatch/applyPatch.ts +428 -0
- package/src/tools/bash/bash.js +70 -18
- package/src/tools/bash/bash.ts +75 -20
- package/src/tools/capabilities.js +10 -5
- package/src/tools/capabilities.ts +10 -5
- package/src/tools/computeruse/computerIntent.js +32 -0
- package/src/tools/computeruse/computerIntent.ts +46 -0
- package/src/tools/computeruse/telemetryLog.js +12 -0
- package/src/tools/computeruse/telemetryLog.ts +16 -0
- package/src/tools/computeruse/vision/telemetry.js +1 -0
- package/src/tools/computeruse/vision/telemetry.ts +1 -0
- package/src/tools/effectiveToolSet.js +4 -0
- package/src/tools/effectiveToolSet.ts +4 -0
- package/src/tools/index.js +21 -2
- package/src/tools/index.ts +33 -2
- package/src/tools/powershell/powershell.js +18 -1
- package/src/tools/powershell/powershell.ts +18 -2
- package/src/tools/presentresult/presentresult.js +6 -15
- package/src/tools/presentresult/presentresult.ts +6 -15
- package/src/tools/read/read.js +69 -11
- package/src/tools/read/read.ts +70 -11
- package/src/tools/shared/fileUtils.js +1 -2
- package/src/tools/shared/fileUtils.ts +1 -2
- package/src/tools/shared/permanentDeleteGuard.js +34 -3
- package/src/tools/shared/permanentDeleteGuard.ts +43 -6
- package/src/tools/shared/toolOutputStore.js +23 -1
- package/src/tools/shared/toolOutputStore.ts +22 -1
- package/src/tools/skill/skill.js +9 -3
- package/src/tools/skill/skill.ts +8 -4
- package/src/tools/toolPromptGuides.js +117 -0
- package/src/tools/toolPromptGuides.ts +137 -0
- package/src/tools/webbrowser/agentBrowserPath.js +19 -9
- package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
- package/src/tools/webbrowser/browser.js +27 -11
- package/src/tools/webbrowser/browser.ts +68 -24
- package/src/tools/webbrowser/browserAutomation.js +329 -0
- package/src/tools/webbrowser/browserAutomation.ts +467 -0
- package/src/tools/webbrowser/browserAxState.js +97 -0
- package/src/tools/webbrowser/browserAxState.ts +142 -0
- package/src/tools/webbrowser/browserBatch.js +1578 -0
- package/src/tools/webbrowser/browserBatch.ts +2382 -0
- package/src/tools/webbrowser/browserCdp.js +208 -0
- package/src/tools/webbrowser/browserCdp.ts +255 -0
- package/src/tools/webbrowser/browserConditions.js +24 -0
- package/src/tools/webbrowser/browserConditions.ts +45 -0
- package/src/tools/webbrowser/browserContent.js +281 -0
- package/src/tools/webbrowser/browserContent.ts +438 -0
- package/src/tools/webbrowser/browserControl.js +131 -30
- package/src/tools/webbrowser/browserControl.ts +210 -50
- package/src/tools/webbrowser/browserDaemon.js +22 -14
- package/src/tools/webbrowser/browserDaemon.ts +54 -21
- package/src/tools/webbrowser/browserFrames.js +47 -0
- package/src/tools/webbrowser/browserFrames.ts +54 -0
- package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
- package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
- package/src/tools/webbrowser/browserLocator.js +140 -0
- package/src/tools/webbrowser/browserLocator.ts +194 -0
- package/src/tools/webbrowser/browserOutcome.js +10 -17
- package/src/tools/webbrowser/browserOutcome.ts +48 -31
- package/src/tools/webbrowser/browserRecovery.js +58 -0
- package/src/tools/webbrowser/browserRecovery.ts +83 -0
- package/src/tools/webbrowser/browserRunRegistry.js +35 -3
- package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
- package/src/tools/webbrowser/browserService.js +28 -2
- package/src/tools/webbrowser/browserService.ts +49 -9
- package/src/tools/webbrowser/browserStream.js +132 -33
- package/src/tools/webbrowser/browserStream.ts +224 -50
- package/src/tools/webbrowser/browserTabs.js +48 -15
- package/src/tools/webbrowser/browserTabs.ts +178 -96
- package/src/tools/webbrowser/webbrowser.js +683 -144
- package/src/tools/webbrowser/webbrowser.ts +979 -236
- package/src/tools/webfetch/preapproved.js +0 -2
- package/src/tools/webfetch/preapproved.ts +0 -2
- package/src/tools/websearch/websearch.js +129 -8
- package/src/tools/websearch/websearch.ts +138 -6
- package/src/types.ts +450 -42
- package/src/ui/App.js +377 -58
- package/src/ui/App.tsx +576 -111
- package/src/ui/LiveArea.js +14 -8
- package/src/ui/LiveArea.tsx +14 -10
- package/src/ui/MessageList.js +63 -20
- package/src/ui/MessageList.tsx +101 -16
- package/src/ui/ModelPicker.js +7 -4
- package/src/ui/ModelPicker.tsx +55 -8
- package/src/ui/hooks/useChat.js +152 -163
- package/src/ui/hooks/useChat.ts +396 -334
- package/src/ui/loginCommand.js +56 -0
- package/src/ui/loginCommand.ts +63 -0
- package/src/ui/toolProgress.js +41 -0
- package/src/ui/toolProgress.ts +44 -0
- package/src/web/browserBridge.js +20 -2
- package/src/web/browserBridge.ts +20 -2
- package/src/web/protocol.ts +1897 -194
- package/workflows/adaptive-research/workflow.mjs +86 -0
- package/workflows/e2e-capabilities/workflow.mjs +102 -0
- package/workflows/e2e-llm/workflow.mjs +52 -0
- package/workflows/e2e-smoke/workflow.mjs +59 -0
- package/workflows/e2e-vision-schema/README.md +48 -0
- package/workflows/e2e-vision-schema/workflow.mjs +149 -0
- package/workflows/e2e-write-greet/workflow.mjs +37 -0
- package/workflows/file-census/README.md +14 -0
- package/workflows/file-census/scripts/.gitkeep +2 -0
- package/workflows/file-census/workflow.mjs +110 -0
- package/workflows/goal/README.md +166 -0
- package/workflows/goal/prompts/audit.md +57 -0
- package/workflows/goal/prompts/contract.md +56 -0
- package/workflows/goal/prompts/iterate.md +37 -0
- package/workflows/goal/workflow.mjs +501 -0
- package/workflows/illustrate/README.md +90 -0
- package/workflows/illustrate/assets/engines.json +20 -0
- package/workflows/illustrate/assets/extract-baidu.js +21 -0
- package/workflows/illustrate/assets/extract-bing.js +33 -0
- package/workflows/illustrate/assets/extract-google.js +31 -0
- package/workflows/illustrate/workflow.mjs +301 -0
- package/workflows/plan/workflow.mjs +389 -0
- package/workflows/question-crops/README.md +35 -0
- package/workflows/question-crops/prompts/inventory.md +23 -0
- package/workflows/question-crops/scripts/image-ops.mjs +305 -0
- package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
- package/workflows/question-crops/workflow.mjs +172 -0
- package/workflows/ralph-loop/workflow.mjs +162 -0
- package/workflows/research/DESIGN.md +449 -0
- package/workflows/research/README.md +74 -0
- package/workflows/research/prompts/s0-goal.md +43 -0
- package/workflows/research/prompts/s1-early-exit.md +22 -0
- package/workflows/research/prompts/s1-map.md +58 -0
- package/workflows/research/prompts/s2-options.md +53 -0
- package/workflows/research/prompts/s3-deep-dive.md +56 -0
- package/workflows/research/prompts/s4-verify.md +74 -0
- package/workflows/research/prompts/s5-synthesis.md +81 -0
- package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
- package/workflows/research/prompts/s6-package.md +32 -0
- package/workflows/research/scripts/build-report.mjs +98 -0
- package/workflows/research/workflow.mjs +288 -0
- package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
- package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
- package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
- package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
- package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
- package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
- package/workflows/wechat-reading-shots/workflow.mjs +266 -0
- package/workflows/wrong-question-docx/README.md +55 -0
- package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
- package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
- package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
- package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
- package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
- package/workflows/wrong-question-docx/workflow.mjs +485 -0
- package/plugins/workflow-runner/src/agentCommon.js +0 -354
- package/plugins/workflow-runner/src/agentCommon.ts +0 -435
- package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
- package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
- package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
- package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
- package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
- package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
- package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
- package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
- package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
- package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
- package/plugins/workflow-runner/src/dslManifest.js +0 -72
- package/plugins/workflow-runner/src/dslManifest.ts +0 -127
- package/plugins/workflow-runner/src/expressions.js +0 -618
- package/plugins/workflow-runner/src/expressions.ts +0 -622
- package/plugins/workflow-runner/src/loader.js +0 -573
- package/plugins/workflow-runner/src/loader.ts +0 -721
- package/plugins/workflow-runner/src/multimodal.js +0 -38
- package/plugins/workflow-runner/src/multimodal.ts +0 -64
- package/plugins/workflow-runner/src/recovery.js +0 -119
- package/plugins/workflow-runner/src/recovery.ts +0 -149
- package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
- package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
- package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -41
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -50
- package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
- package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
- package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
- package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
- package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
- package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
- package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
- package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -208
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -256
- package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -58
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -68
- package/plugins/workflow-runner/src/stepPipeline.js +0 -277
- package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
- package/plugins/workflow-runner/test/agent.test.ts +0 -587
- package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
- package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
- package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
- package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -147
- package/plugins/workflow-runner/test/context-files.test.ts +0 -320
- package/plugins/workflow-runner/test/e2e.test.ts +0 -370
- package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -226
- package/plugins/workflow-runner/test/expressions.test.ts +0 -397
- package/plugins/workflow-runner/test/foundation.test.ts +0 -126
- package/plugins/workflow-runner/test/graceful.test.ts +0 -139
- package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
- package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
- package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
- package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
- package/plugins/workflow-runner/test/loader.test.ts +0 -617
- package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
- package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
- package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
- package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -87
- package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
- package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
- package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -220
- package/plugins/workflow-runner/test/recovery.test.ts +0 -265
- package/plugins/workflow-runner/test/runner.test.ts +0 -911
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -379
- package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -150
- package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
- package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -338
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -270
- package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
- package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
- package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
- package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
- package/plugins/workflow-runner/test/word.load.test.ts +0 -208
- package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
- package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
- package/src/advisorState.js +0 -22
- package/src/cli/configWizard.js +0 -293
- package/src/tools/toolProgressBridge.js +0 -48
- package/workflows/adaptive-research.yaml +0 -50
- package/workflows/book-review-short.yaml +0 -125
- package/workflows/e2e-write-greet.yaml +0 -31
- package/workflows/illustrate.yaml +0 -227
- package/workflows/plan.yaml +0 -470
- package/workflows/ppt.yaml +0 -1670
- package/workflows/ralph-loop.yaml +0 -199
- package/workflows/schema.json +0 -176
- package/workflows/webppt.yaml +0 -496
- package/workflows/wechat-reading-shots.yaml +0 -315
- package/workflows/word.yaml +0 -720
- package/workflows/youtube-shorts.yaml +0 -202
|
@@ -1,514 +1,451 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ============================================================
|
|
3
|
-
* plugins/workflow-runner/src/runner.ts ——
|
|
3
|
+
* plugins/workflow-runner/src/runner.ts —— 一次工作流运行的编排
|
|
4
4
|
* ------------------------------------------------------------
|
|
5
|
-
*
|
|
6
|
-
* - when: 不满足 → yield workflow_step_skipped
|
|
7
|
-
* - 命中动作字段 → 调对应 step executor
|
|
8
|
-
* - capture: 把 result.raw 上的字段绑到 VarStack
|
|
9
|
-
* - onError: halt(默认)→ yield error 后 return;continue → 记录后跳过
|
|
10
|
-
* - 信号 abort → yield interrupted 后 return
|
|
5
|
+
* 串起:任务身份判定 → 账本装载 → 目标锚构造 → 组装 WorkflowContext → 执行 ESM → 收尾归档。
|
|
11
6
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* - branch / loop / pause / echo —— 控制流,直接在 runSteps 内处理(不走 executor)
|
|
17
|
-
* - break / continue —— 循环信号(可带 target 穿透到指定外层循环)
|
|
18
|
-
*
|
|
19
|
-
* 控制流嵌套通过 runSteps 递归实现:branch.then/else / loop.do 子数组
|
|
20
|
-
* 是 StepDef[],调用方语义与顶层 def.steps 一致;变量栈用 VarStack.push/pop
|
|
21
|
-
* 在 loop 迭代间隔离 `${as}` / `${as}_idx`。
|
|
7
|
+
* ⚠️ **为什么需要事件队列**:ESM 的 run(wf) 是一个普通 async 函数,它执行期间
|
|
8
|
+
* 没有任何机制能「yield 一个事件出去」;而宿主要的是一条实时事件流(否则
|
|
9
|
+
* 整条工作流跑完之前 UI 一片死寂)。所以 `wf` 上下文把事件推进队列,本函数
|
|
10
|
+
* 作为 async generator 一边 await 脚本的 promise、一边把队列排空往外 yield。
|
|
22
11
|
* ============================================================
|
|
23
12
|
*/
|
|
24
|
-
import { existsSync } from 'node:fs';
|
|
25
13
|
import { resolve } from 'node:path';
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import { execSetStep } from './stepExecutors/set.js';
|
|
37
|
-
import { execBranchStep } from './controlFlow/branch.js';
|
|
38
|
-
import { execLoopStep } from './controlFlow/loop.js';
|
|
39
|
-
import { execPauseStep } from './controlFlow/pause.js';
|
|
40
|
-
import { execSignalStep } from './controlFlow/signal.js';
|
|
41
|
-
import { runStepWithRetry } from './stepPipeline.js';
|
|
42
|
-
import { VarStack } from './types.js';
|
|
43
|
-
import { WorkflowState, } from './workflowState.js';
|
|
44
|
-
/**
|
|
45
|
-
* 插件 ID:把私有 workflow_* 事件镜像成框架通用 plugin_progress 时挂这个 id。
|
|
46
|
-
* LiveArea 状态行会渲染成 "[plugin:workflow-runner] i/total <message>"
|
|
47
|
-
* (src/ui/LiveArea.tsx:123),让 TUI 里跑 workflow 不再"无声"。
|
|
48
|
-
* (-p 非交互模式另由 src/cli/print.ts 把通用 warning 事件落 stderr。)
|
|
49
|
-
*/
|
|
14
|
+
import { ExecutionScope, createWorkflowContext } from './runtime/context.js';
|
|
15
|
+
import { buildGoalAnchor } from './goalAnchor.js';
|
|
16
|
+
import { coerceInputs } from './inputCoercion.js';
|
|
17
|
+
import { Journal, fingerprint } from './journal.js';
|
|
18
|
+
import { bindResourceInputs } from './resourceInputs.js';
|
|
19
|
+
import { WorkflowScriptError, runScript } from './sandbox.js';
|
|
20
|
+
import { DEFAULT_SESSION_ID, buildTaskArbitrator, sanitizeWorkflowDirName, } from './taskIdentity.js';
|
|
21
|
+
import { effectiveWorkflowCwd } from './toolRuntime.js';
|
|
22
|
+
import { resolveWorkflowBundle, } from './workflowDependencies.js';
|
|
23
|
+
import { WorkflowState } from './workflowState.js';
|
|
50
24
|
const WF_PLUGIN_ID = 'workflow-runner';
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// 策略 1:优先 "goal"
|
|
66
|
-
const goalInput = inputDefs.find((d) => d.name === 'goal' && d.type === 'string');
|
|
67
|
-
if (goalInput) {
|
|
68
|
-
const v = inputValues[goalInput.name];
|
|
69
|
-
if (typeof v === 'string' && v.trim()) {
|
|
70
|
-
goalValue = v.trim();
|
|
71
|
-
}
|
|
25
|
+
/** 事件队列:生产者随时 push,消费者按序 async 迭代,close 后排空即结束。 */
|
|
26
|
+
class EventQueue {
|
|
27
|
+
items = [];
|
|
28
|
+
waiter = null;
|
|
29
|
+
closed = false;
|
|
30
|
+
push(item) {
|
|
31
|
+
this.items.push(item);
|
|
32
|
+
this.waiter?.();
|
|
33
|
+
this.waiter = null;
|
|
34
|
+
}
|
|
35
|
+
close() {
|
|
36
|
+
this.closed = true;
|
|
37
|
+
this.waiter?.();
|
|
38
|
+
this.waiter = null;
|
|
72
39
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const v = inputValues[firstRequired.name];
|
|
78
|
-
if (typeof v === 'string' && v.trim()) {
|
|
79
|
-
goalValue = v.trim();
|
|
40
|
+
async *drain() {
|
|
41
|
+
while (true) {
|
|
42
|
+
while (this.items.length > 0) {
|
|
43
|
+
yield this.items.shift();
|
|
80
44
|
}
|
|
45
|
+
if (this.closed)
|
|
46
|
+
return;
|
|
47
|
+
await new Promise((resolve) => {
|
|
48
|
+
this.waiter = resolve;
|
|
49
|
+
});
|
|
81
50
|
}
|
|
82
51
|
}
|
|
83
|
-
if (!goalValue)
|
|
84
|
-
return '';
|
|
85
|
-
return `> 🎯 用户原始目标:${truncateGoal(goalValue)}\n> ⚠️ 所有操作必须直接回应此目标,不可偏离或遗忘。`;
|
|
86
52
|
}
|
|
87
|
-
/**
|
|
88
|
-
function
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const timer = setTimeout(() => {
|
|
99
|
-
signal?.removeEventListener('abort', onAbort);
|
|
100
|
-
resolve();
|
|
101
|
-
}, ms);
|
|
102
|
-
const onAbort = () => {
|
|
103
|
-
clearTimeout(timer);
|
|
104
|
-
resolve();
|
|
105
|
-
};
|
|
106
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
/** 把一段用户输入并入贯穿目标(内存 + 状态目录持久化,resume 不丢)。 */
|
|
110
|
-
async function addGoalSupplement(ctx, state, text) {
|
|
111
|
-
const t = text.trim();
|
|
112
|
-
if (!t)
|
|
113
|
-
return;
|
|
114
|
-
// 锚定必须紧凑(注入每步 prompt),逐条截断;完整原文在 capture 变量 / history 里
|
|
115
|
-
const compact = t.length > 240 ? `${t.slice(0, 240)}…` : t;
|
|
116
|
-
ctx.goalSupplements ??= [];
|
|
117
|
-
ctx.goalSupplements.push(compact);
|
|
118
|
-
await state.appendSupplement(compact);
|
|
119
|
-
}
|
|
120
|
-
function stepKind(step) {
|
|
121
|
-
if (step.type === 'break')
|
|
122
|
-
return 'break';
|
|
123
|
-
if (step.type)
|
|
124
|
-
return step.type;
|
|
125
|
-
if (step.tool)
|
|
126
|
-
return 'tool';
|
|
127
|
-
if (step.skill)
|
|
128
|
-
return 'skill';
|
|
129
|
-
if (step.agent)
|
|
130
|
-
return 'agent';
|
|
131
|
-
if (step.llm)
|
|
132
|
-
return 'llm';
|
|
133
|
-
return 'assert'; // 不会到这里(loader 已校验),兜底
|
|
134
|
-
}
|
|
135
|
-
function resolvePositiveIterationLimit(raw, vars, defaultValue, label) {
|
|
136
|
-
if (raw === undefined)
|
|
137
|
-
return { ok: true, value: defaultValue };
|
|
138
|
-
const interpolated = typeof raw === 'number' ? raw : interpolate(String(raw), vars);
|
|
139
|
-
const n = typeof raw === 'number' ? raw : Number(String(interpolated).trim());
|
|
140
|
-
if (!Number.isFinite(n) || n <= 0) {
|
|
141
|
-
return {
|
|
142
|
-
ok: false,
|
|
143
|
-
error: `${label} max_iterations parse failed: ${JSON.stringify(raw)} -> "${String(interpolated)}" is not a positive number`,
|
|
53
|
+
/** 执行一个代码形态工作流,产出 LoopEvent 流。 */
|
|
54
|
+
export async function* runWorkflowScript(script, opts) {
|
|
55
|
+
try {
|
|
56
|
+
const bundle = await resolveWorkflowBundle(script, effectiveWorkflowCwd(opts.ctx));
|
|
57
|
+
opts.ctx.resourceLedger ??= {
|
|
58
|
+
inputTokens: 0,
|
|
59
|
+
outputTokens: 0,
|
|
60
|
+
limit: null,
|
|
61
|
+
trustworthy: true,
|
|
144
62
|
};
|
|
63
|
+
const scope = new ExecutionScope();
|
|
64
|
+
const lease = await scope.scheduler.acquire('root', scope.rootRunId, opts.ctx.signal);
|
|
65
|
+
try {
|
|
66
|
+
yield* executeWorkflowScript(script, {
|
|
67
|
+
...opts,
|
|
68
|
+
bundle,
|
|
69
|
+
scope,
|
|
70
|
+
runId: scope.rootRunId,
|
|
71
|
+
depth: 0,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
lease.release();
|
|
76
|
+
}
|
|
145
77
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
* 分发"动作"型 step(tool / agent / llm / skill / assert)到对应 executor。
|
|
150
|
-
* 控制流 step(branch / loop / pause 等)由 runSteps 直接处理,不走这里。
|
|
151
|
-
*/
|
|
152
|
-
async function* dispatchActionStep(step, vars, ctx) {
|
|
153
|
-
if (step.type === 'assert')
|
|
154
|
-
return execAssertStep(step, vars, ctx);
|
|
155
|
-
if (step.type === 'verify')
|
|
156
|
-
return await execVerifyStep(step, vars);
|
|
157
|
-
// set / echo 从 runSteps 的内联块搬进执行器体系:它们不影响迭代、只产出 StepResult。
|
|
158
|
-
// 搬过来的直接收益是 `capture:` 自然生效(此前它们从不调 bindCapture —— 静默失效)。
|
|
159
|
-
if (step.type === 'set')
|
|
160
|
-
return yield* execSetStep(step, vars);
|
|
161
|
-
if (step.type === 'echo')
|
|
162
|
-
return yield* execEchoStep(step, vars);
|
|
163
|
-
if (step.tool)
|
|
164
|
-
return yield* execToolStep(step, vars, ctx);
|
|
165
|
-
if (step.agent)
|
|
166
|
-
return yield* execAgentStep(step, vars, ctx);
|
|
167
|
-
if (step.llm)
|
|
168
|
-
return yield* execLlmStep(step, vars, ctx);
|
|
169
|
-
if (step.skill)
|
|
170
|
-
return yield* execSkillStep(step, vars, ctx);
|
|
171
|
-
throw new Error(`step ${step.id}: 无可执行字段(tool/agent/llm/skill/assert/verify)`);
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* 递归执行 steps[]。返回 StepFlow(completed / halted / break|continue 信号,见上方定义)。
|
|
175
|
-
*
|
|
176
|
-
* yields 混合事件:WorkflowEvent + LoopEvent(skill 子循环转发 / error / interrupted)。
|
|
177
|
-
* index/total 字段相对**当前 block**,嵌套 block 自有计数(branch.then 内 3 步就报 total=3)。
|
|
178
|
-
*/
|
|
179
|
-
async function* runSteps(steps, vars, ctx, state,
|
|
180
|
-
// checkpoint 记录范围:顶层调用(runWorkflow → def.steps)传 { name: id=>id, descendIntoLoop:true };
|
|
181
|
-
// 顶层 loop 的 do[] 由 loop 处理器派生复合名 scope;更深嵌套传 undefined(不记录)。
|
|
182
|
-
cp,
|
|
183
|
-
// resume:本列表中索引 < startFrom 的步已在上次运行完成,本次跳过(变量已由 runWorkflow 回放)。
|
|
184
|
-
// 顶层与「loop resume 进迭代中间」(loop 处理器对 do[] 传 innerStartFrom)都用它。
|
|
185
|
-
startFrom = 0,
|
|
186
|
-
// 仅顶层传:进行中循环的恢复信息(迭代号 + 迭代内起点),由匹配 id 的 loop 消费一次。
|
|
187
|
-
loopResume) {
|
|
188
|
-
for (let i = 0; i < steps.length; i++) {
|
|
189
|
-
const step = steps[i];
|
|
190
|
-
if (ctx.signal?.aborted) {
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (opts.ctx.signal?.aborted ||
|
|
80
|
+
(error instanceof Error && error.name === 'AbortError'))
|
|
191
81
|
yield { type: 'interrupted' };
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
// resume:跳过已完成的步(变量已回放,无需重跑)。
|
|
195
|
-
if (i < startFrom) {
|
|
82
|
+
else
|
|
196
83
|
yield {
|
|
197
|
-
type: '
|
|
198
|
-
|
|
199
|
-
reason: 'resume: 上次已完成',
|
|
84
|
+
type: 'error',
|
|
85
|
+
error: error instanceof Error ? error.message : String(error),
|
|
200
86
|
};
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
// when 条件:**所有** step 类型统一在这里判。控制流处理器里不要再判一次 ——
|
|
204
|
-
// 重构前 break/continue 块自带一份,而它恒为真(false 时这里已经 continue 掉了),
|
|
205
|
-
// 连它的 catch 分支都不可达。那是纯死代码,随本次重构删除。
|
|
206
|
-
if (step.when) {
|
|
207
|
-
let pass = false;
|
|
208
|
-
try {
|
|
209
|
-
pass = Boolean(evalExpr(step.when, vars));
|
|
210
|
-
}
|
|
211
|
-
catch (e) {
|
|
212
|
-
const errMsg = `when 表达式错误: ${e.message}`;
|
|
213
|
-
yield { type: 'workflow_step_end', id: step.id, ok: false, error: errMsg };
|
|
214
|
-
yield {
|
|
215
|
-
type: 'error',
|
|
216
|
-
error: `Workflow halted at step "${step.id}": ${errMsg}`,
|
|
217
|
-
};
|
|
218
|
-
return 'halted';
|
|
219
|
-
}
|
|
220
|
-
if (!pass) {
|
|
221
|
-
yield { type: 'workflow_step_skipped', id: step.id, reason: 'when=false' };
|
|
222
|
-
await state.appendProgress(`- ${step.id} skipped (when=false)`);
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
const kind = stepKind(step);
|
|
227
|
-
dlog('wf', `step ${i + 1}/${steps.length} START ${step.id} (${kind})`);
|
|
228
|
-
// 已插值的人话进度名随 start 事件带给 UI:scrollback 进度行显示它而非块内相对
|
|
229
|
-
// 序号,与状态行(下方 plugin_progress)保持一致。未声明 title 的步省略此字段。
|
|
230
|
-
const interpolatedTitle = step.title ? interpolate(step.title, vars) : undefined;
|
|
231
|
-
yield {
|
|
232
|
-
type: 'workflow_step_start',
|
|
233
|
-
id: step.id,
|
|
234
|
-
kind,
|
|
235
|
-
index: i,
|
|
236
|
-
total: steps.length,
|
|
237
|
-
title: interpolatedTitle,
|
|
238
|
-
};
|
|
239
|
-
// P1 TUI 可见:把私有 workflow_step_start 镜像成框架通用 plugin_progress。
|
|
240
|
-
// 有 title 的步显示人话阶段名(如「阶段2/6·起草实现计划」)且不带块内序号——
|
|
241
|
-
// 嵌套块的 i/total(顶层 5、循环体 12…)会被用户误读成"跳步/结构乱";
|
|
242
|
-
// 未声明 title 的步保持 legacy 格式 "i/total step <id> (<kind>)"。
|
|
243
|
-
yield {
|
|
244
|
-
type: 'plugin_progress',
|
|
245
|
-
pluginId: WF_PLUGIN_ID,
|
|
246
|
-
current: i + 1,
|
|
247
|
-
max: steps.length,
|
|
248
|
-
message: interpolatedTitle ?? `step ${step.id} (${kind})`,
|
|
249
|
-
};
|
|
250
|
-
// ---------- 控制流:改变的是「循环怎么走」,不是产出一个 StepResult ----------
|
|
251
|
-
//
|
|
252
|
-
// 所以它们不塞进 stepExecutors 那套 (step,vars,ctx)→StepResult 的形状里,
|
|
253
|
-
// 各自住在 controlFlow/ 并共用出口 ControlOutcome —— 这里只需一个分派。
|
|
254
|
-
const controlDeps = {
|
|
255
|
-
ctx,
|
|
256
|
-
state,
|
|
257
|
-
...(cp ? { cp } : {}),
|
|
258
|
-
runBlock: (blockSteps, blockVars, blockCp, blockStart, blockResume) => runSteps(blockSteps, blockVars, ctx, state, blockCp, blockStart, blockResume),
|
|
259
|
-
index: i,
|
|
260
|
-
total: steps.length,
|
|
261
|
-
addGoalSupplement: (text) => addGoalSupplement(ctx, state, text),
|
|
262
|
-
};
|
|
263
|
-
let outcome = null;
|
|
264
|
-
if (step.type === 'branch') {
|
|
265
|
-
outcome = yield* execBranchStep(step, vars, controlDeps);
|
|
266
|
-
}
|
|
267
|
-
else if (step.type === 'loop') {
|
|
268
|
-
outcome = yield* execLoopStep(step, vars, controlDeps, loopResume);
|
|
269
|
-
// 恢复信息只消费一次:匹配到的那个 loop 用掉之后,后续 loop 不再套用。
|
|
270
|
-
if (loopResume && loopResume.loopId === step.id)
|
|
271
|
-
loopResume = undefined;
|
|
272
|
-
}
|
|
273
|
-
else if (step.type === 'pause') {
|
|
274
|
-
outcome = yield* execPauseStep(step, vars, controlDeps);
|
|
275
|
-
}
|
|
276
|
-
else if (step.type === 'break' || step.type === 'continue') {
|
|
277
|
-
outcome = yield* execSignalStep(step, controlDeps);
|
|
278
|
-
}
|
|
279
|
-
if (outcome) {
|
|
280
|
-
if (outcome.kind === 'halted')
|
|
281
|
-
return 'halted';
|
|
282
|
-
if (outcome.kind === 'flow')
|
|
283
|
-
return outcome.signal;
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
// ---------- 动作步:tool / llm / skill / agent / assert / verify / set / echo ----------
|
|
287
|
-
//
|
|
288
|
-
// 统一走 runStepWithRetry:in-run 重试 → onError 三态 → commitStep 提交。
|
|
289
|
-
// 「提交一步」只有 stepPipeline 那一份实现,这里不再自己写(见 stepPipeline 头注)。
|
|
290
|
-
const actionOutcome = yield* runStepWithRetry({
|
|
291
|
-
step,
|
|
292
|
-
kind,
|
|
293
|
-
vars,
|
|
294
|
-
ctx,
|
|
295
|
-
state,
|
|
296
|
-
...(cp ? { cp } : {}),
|
|
297
|
-
index: i,
|
|
298
|
-
total: steps.length,
|
|
299
|
-
addGoalSupplement: (text) => addGoalSupplement(ctx, state, text),
|
|
300
|
-
execute: () => dispatchActionStep(step, vars, ctx),
|
|
301
|
-
});
|
|
302
|
-
if (actionOutcome === 'halted')
|
|
303
|
-
return 'halted';
|
|
304
87
|
}
|
|
305
|
-
return 'completed';
|
|
306
88
|
}
|
|
307
|
-
/**
|
|
308
|
-
*
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
for (const a of def.artifacts ?? [])
|
|
315
|
-
out.add(a);
|
|
316
|
-
const walk = (steps) => {
|
|
317
|
-
for (const s of steps ?? []) {
|
|
318
|
-
for (const a of s.artifacts ?? [])
|
|
319
|
-
out.add(a);
|
|
320
|
-
walk(s.then);
|
|
321
|
-
walk(s.else);
|
|
322
|
-
walk(s.do);
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
walk(def.steps);
|
|
326
|
-
return [...out];
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* 运行一个 workflow。
|
|
330
|
-
*
|
|
331
|
-
* yields 混合事件:本地 WorkflowEvent(workflow_* 系列,UI 静默忽略不渲染)
|
|
332
|
-
* + 通用 LoopEvent(error / interrupted / 子 step 透传的 text/tool/stage_change 等,
|
|
333
|
-
* UI 识别并渲染)。经 plugin.ts -> pluginRunner 透传给 UI;workflow_* 由 PluginEvent
|
|
334
|
-
* 开放契约承载。
|
|
335
|
-
*
|
|
336
|
-
* 成功跑完以 `workflow_done` 收尾(携带 vars 快照);中途失败/中断只 yield error/interrupted。
|
|
337
|
-
* **对外流式协议的通用终结事件 `turn_done` 由命令层 index.ts 在成功后补发**(见那里)——
|
|
338
|
-
* 本函数自身事件序列保持不变,直测它的 runner.test.ts / 单元测试仍以 workflow_done 为最后一条。
|
|
339
|
-
*/
|
|
340
|
-
export async function* runWorkflow(def, inputs, ctx) {
|
|
341
|
-
const vars = new VarStack();
|
|
342
|
-
vars.setGlobal('inputs', inputs);
|
|
343
|
-
// onError:'recover' 用:把工作流总目标(description)透传给恢复 agent,
|
|
344
|
-
// 让它知道「这条工作流到底想干什么」。已有 goal 则不覆盖。
|
|
345
|
-
const runCtx = { ...ctx, goal: ctx.goal ?? def.description };
|
|
346
|
-
// 通用目标锚定:从 inputs 中提取用户的原始目标,
|
|
347
|
-
// 注入 RunContext.goalAnchor,让 agent/llm/skill 步自动前置到每步 prompt。
|
|
348
|
-
// 提取策略:优先 "goal" 命名的输入 → 其次第一个 required string 输入。
|
|
349
|
-
runCtx.goalAnchor = buildGoalAnchor(def.inputs ?? [], inputs);
|
|
350
|
-
// self_heal:把全局 max_retries 默认透传给每步的 in-run retry(step.max_retries 可覆盖)。
|
|
351
|
-
runCtx.selfHealMaxRetries = def.self_heal?.max_retries;
|
|
352
|
-
runCtx.retryDelayMs = def.self_heal?.retry_delay_ms;
|
|
353
|
-
runCtx.maxTurnsRetryDelayMs = def.self_heal?.max_turns_retry_delay_ms;
|
|
354
|
-
// 声明产物清单:state.init 用于 new-task 清理/completed 报产物;
|
|
355
|
-
// 挂到 runCtx 供 tool 步覆盖判定(声明产物自动 pre-Read 解 Write 守卫)。
|
|
356
|
-
const declaredArtifacts = collectDeclaredArtifacts(def);
|
|
357
|
-
runCtx.declaredArtifacts = declaredArtifacts;
|
|
358
|
-
// 任务身份语义仲裁器(hash 不等 + 旧任务未完成时的二审,见 taskIdentity.ts):
|
|
359
|
-
// 短语快路径 → LLM 单轮仲裁 → AskUserQuestion → 兜底 new-task。
|
|
360
|
-
// init 非 generator,仲裁过程的人话记录收进 notes,init 返回后统一 yield。
|
|
89
|
+
/** Same executor for roots and children. Only roots emit terminal host events. */
|
|
90
|
+
async function* executeWorkflowScript(script, opts) {
|
|
91
|
+
const { ctx, inputs } = opts;
|
|
92
|
+
ctx.signal?.throwIfAborted();
|
|
93
|
+
const sessionId = sanitizeWorkflowDirName(opts.sessionId ?? ctx.runtimeContext?.sessionId ?? DEFAULT_SESSION_ID);
|
|
94
|
+
const scriptHash = opts.bundle.hash;
|
|
95
|
+
const state = new WorkflowState(effectiveWorkflowCwd(ctx), script.name, sessionId, script.meta.inputs, opts.taskId ?? ctx.managedExecution?.runId, opts.parentStateDir);
|
|
361
96
|
const arbitrationNotes = [];
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
totalSteps: def.steps.length,
|
|
374
|
-
};
|
|
375
|
-
// 仲裁过程可见化(命中延续短语 / LLM 判定 / 用户确认 / 兜底)
|
|
376
|
-
for (const n of arbitrationNotes) {
|
|
377
|
-
yield { type: 'warning', message: n };
|
|
97
|
+
const resume = await state.init(inputs, scriptHash, script.meta.artifacts ?? [], ctx.managedExecution || opts.parentStateDir
|
|
98
|
+
? undefined
|
|
99
|
+
: buildTaskArbitrator({
|
|
100
|
+
provider: ctx.provider,
|
|
101
|
+
chat: ctx.chat,
|
|
102
|
+
executeTool: ctx.executeTool,
|
|
103
|
+
signal: ctx.signal,
|
|
104
|
+
notes: arbitrationNotes,
|
|
105
|
+
}));
|
|
106
|
+
for (const note of arbitrationNotes) {
|
|
107
|
+
yield { type: 'warning', message: note };
|
|
378
108
|
}
|
|
379
|
-
//
|
|
380
|
-
// 成功运行会 archive() 把状态目录改名 .done,init() 回退读 .done 检测到 completed。
|
|
109
|
+
// 已完成:报实际产物,不重跑。
|
|
381
110
|
if (resume.mode === 'completed') {
|
|
382
|
-
const
|
|
111
|
+
const restoredEvent = {
|
|
112
|
+
type: 'workflow_progress',
|
|
113
|
+
workflow: script.name,
|
|
114
|
+
runId: opts.runId,
|
|
115
|
+
rootRunId: opts.scope.rootRunId,
|
|
116
|
+
...(opts.parentRunId ? { parentRunId: opts.parentRunId } : {}),
|
|
117
|
+
depth: opts.depth,
|
|
118
|
+
phase: 'workflow',
|
|
119
|
+
status: 'complete',
|
|
120
|
+
message: '恢复已完成结果',
|
|
121
|
+
};
|
|
122
|
+
await state.appendEvent(restoredEvent);
|
|
123
|
+
yield restoredEvent;
|
|
124
|
+
const list = resume.artifacts?.length
|
|
125
|
+
? resume.artifacts.map((a) => ` · ${a}`).join('\n')
|
|
126
|
+
: ' (本次运行没有声明产物)';
|
|
383
127
|
yield {
|
|
384
128
|
type: 'text',
|
|
385
|
-
delta:
|
|
129
|
+
delta: `工作流「${script.name}」的这个目标此前已完成,产物:\n${list}\n换个目标即可重新执行。\n`,
|
|
130
|
+
};
|
|
131
|
+
if (!opts.parentStateDir)
|
|
132
|
+
yield { type: 'turn_done' };
|
|
133
|
+
return {
|
|
134
|
+
workflow: script.name,
|
|
135
|
+
runId: opts.runId,
|
|
136
|
+
result: await state.readResult(),
|
|
137
|
+
artifacts: (resume.artifacts ?? []).map((p) => resolve(state.workingDir, p)),
|
|
138
|
+
reused: true,
|
|
386
139
|
};
|
|
387
|
-
yield { type: 'workflow_done', name: def.name, vars: vars.snapshot() };
|
|
388
|
-
return;
|
|
389
140
|
}
|
|
390
|
-
|
|
391
|
-
// 必须明示——曾有用户在 yaml 重构后同 goal 重跑,看到「从第 2 步续跑」
|
|
392
|
-
// 困惑是 bug 还是残留;作废旧断点时不说原因同样会被当成 bug。
|
|
393
|
-
if (resume.defChanged) {
|
|
141
|
+
if (resume.scriptChanged) {
|
|
394
142
|
yield {
|
|
395
143
|
type: 'warning',
|
|
396
|
-
message:
|
|
144
|
+
message: `⚠ 工作流「${script.name}」的脚本/依赖已改变或产物/返回值缺失 —— 旧断点作废,本次全新开始。`,
|
|
397
145
|
};
|
|
398
146
|
}
|
|
399
|
-
|
|
400
|
-
// 的声明产物 + /new 后无主孤儿)。用户约定:全新 plan 一律从干净状态开始;
|
|
401
|
-
// 删除范围仅限本工作流声明的产物,同任务 resume 不清理。
|
|
402
|
-
if (resume.cleanedArtifacts?.length) {
|
|
147
|
+
if (resume.archivedTo) {
|
|
403
148
|
yield {
|
|
404
149
|
type: 'warning',
|
|
405
|
-
message:
|
|
150
|
+
message: `旧产物已归档到 ${resume.archivedTo}(不是删除,需要时可取回)。`,
|
|
406
151
|
};
|
|
407
152
|
}
|
|
408
|
-
//
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
153
|
+
// 目标锚:仲裁判定的延续要锚**旧目标**,本次输入降级成补充要求。
|
|
154
|
+
ctx.goalAnchor = ctx.managedExecution
|
|
155
|
+
? ctx.managedExecution.anchor
|
|
156
|
+
: resume.prevGoal
|
|
157
|
+
? buildGoalAnchor({ goal: resume.prevGoal }, script.meta.inputs)
|
|
158
|
+
: buildGoalAnchor(inputs, script.meta.inputs);
|
|
159
|
+
ctx.goalSupplements = await state.readSupplements();
|
|
160
|
+
ctx.declaredArtifacts = new Set(script.meta.artifacts ?? []);
|
|
161
|
+
const journal = new Journal(state.journalPath, {
|
|
162
|
+
bundleHash: scriptHash,
|
|
163
|
+
rootRunId: opts.scope.rootRunId,
|
|
164
|
+
});
|
|
165
|
+
await journal.load(script.meta.resumeBackoff ?? 0);
|
|
415
166
|
if (resume.mode === 'resume') {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
const goalKey = (def.inputs ?? []).find((d) => d.name === 'goal' && d.type === 'string')
|
|
422
|
-
?.name ??
|
|
423
|
-
(def.inputs ?? []).find((d) => d.required && d.type === 'string')?.name;
|
|
424
|
-
if (goalKey && typeof inputs[goalKey] === 'string') {
|
|
425
|
-
vars.setGlobal('inputs', { ...inputs, [goalKey]: resume.prevGoal });
|
|
426
|
-
}
|
|
427
|
-
runCtx.goalAnchor = `> 🎯 用户原始目标:${truncateGoal(resume.prevGoal)}\n> ⚠️ 所有操作必须直接回应此目标,不可偏离或遗忘。`;
|
|
428
|
-
// 新输入是带信息量的补充指令(非纯「继续」延续短语)→ 并入贯穿补充要求;
|
|
429
|
-
// 经 goalSupplements 统一渲染(与 pause 反馈/澄清结论同通道),且去重防
|
|
430
|
-
// 反复 resume 时同一句话累积成噪声。
|
|
431
|
-
const supp = resume.supplementaryGoal;
|
|
432
|
-
if (supp &&
|
|
433
|
-
supp !== resume.prevGoal &&
|
|
434
|
-
!isContinuationPhrase(supp) &&
|
|
435
|
-
!(runCtx.goalSupplements ?? []).some((s) => s.startsWith(supp.slice(0, 240)))) {
|
|
436
|
-
await addGoalSupplement(runCtx, state, supp);
|
|
437
|
-
}
|
|
167
|
+
yield {
|
|
168
|
+
type: 'warning',
|
|
169
|
+
message: `↻ 续跑工作流「${script.name}」(上次已完成 ${journal.loadedCount} 次调用` +
|
|
170
|
+
`${resume.lastError ? `,上次失败于:${resume.lastError}` : ''})。`,
|
|
171
|
+
};
|
|
438
172
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
173
|
+
const queue = new EventQueue();
|
|
174
|
+
const workflowContext = createWorkflowContext({
|
|
175
|
+
ctx,
|
|
176
|
+
state,
|
|
177
|
+
journal,
|
|
178
|
+
emit: (ev) => queue.push(ev),
|
|
179
|
+
args: inputs,
|
|
180
|
+
workflowName: script.name,
|
|
181
|
+
runId: opts.runId,
|
|
182
|
+
parentRunId: opts.parentRunId,
|
|
183
|
+
depth: opts.depth,
|
|
184
|
+
session: sessionId,
|
|
185
|
+
task: state.taskId,
|
|
186
|
+
workflowDir: script.dir,
|
|
187
|
+
inputResourceMap: ctx.inputResourceMap,
|
|
188
|
+
scope: opts.scope,
|
|
189
|
+
invokeWorkflow: async (name, rawInputs, site, label) => {
|
|
190
|
+
const bundle = opts.bundle.children.get(name);
|
|
191
|
+
if (!bundle)
|
|
192
|
+
throw new Error(`子工作流 ${name} 未声明在 meta.dependencies 中`);
|
|
193
|
+
const bound = await bindResourceInputs({
|
|
194
|
+
workflowName: name,
|
|
195
|
+
sessionId,
|
|
196
|
+
cwd: effectiveWorkflowCwd(ctx),
|
|
197
|
+
rawInputs: rawInputs,
|
|
198
|
+
defs: bundle.script.meta.inputs,
|
|
199
|
+
attachments: [],
|
|
200
|
+
});
|
|
201
|
+
const childInputs = coerceInputs(bound.inputs, bundle.script.meta.inputs);
|
|
202
|
+
if (bound.resourceIdentity.length)
|
|
203
|
+
childInputs.__resource_fingerprint = bound.resourceIdentity.join('|');
|
|
204
|
+
const taskId = fingerprint('child-instance-v1', {
|
|
205
|
+
parent: [script.name, state.taskId],
|
|
206
|
+
site,
|
|
207
|
+
name,
|
|
208
|
+
inputs: childInputs,
|
|
209
|
+
});
|
|
210
|
+
const childRunId = fingerprint('child-run-v2', {
|
|
211
|
+
rootRunId: opts.scope.rootRunId,
|
|
212
|
+
parentRunId: opts.runId,
|
|
213
|
+
site,
|
|
214
|
+
taskId,
|
|
215
|
+
});
|
|
216
|
+
const childCtx = {
|
|
217
|
+
...ctx,
|
|
218
|
+
cwd: effectiveWorkflowCwd(ctx),
|
|
219
|
+
history: structuredClone(ctx.history.filter((m) => m.role === 'system')),
|
|
220
|
+
sessionState: ctx.sessionState
|
|
221
|
+
? structuredClone(ctx.sessionState)
|
|
222
|
+
: undefined,
|
|
223
|
+
goal: undefined,
|
|
224
|
+
goalAnchor: undefined,
|
|
225
|
+
goalSupplements: undefined,
|
|
226
|
+
declaredArtifacts: undefined,
|
|
227
|
+
transientContext: undefined,
|
|
228
|
+
transientBundle: undefined,
|
|
229
|
+
managedExecution: undefined,
|
|
230
|
+
managedBridge: undefined,
|
|
231
|
+
usageRecorder: ctx.usageRecorder ??
|
|
232
|
+
ctx.managedBridge?.recordUsage?.bind(ctx.managedBridge),
|
|
233
|
+
inputResources: Object.values(bound.resourceMap).flat(),
|
|
234
|
+
inputResourceMap: bound.resourceMap,
|
|
235
|
+
};
|
|
236
|
+
const prefix = `${script.name} → ${label ?? name}`;
|
|
237
|
+
await state.appendProgress(`▶ 子流程 ${prefix} (${site}, ${taskId})`);
|
|
238
|
+
const run = executeWorkflowScript(bundle.script, {
|
|
239
|
+
ctx: childCtx,
|
|
240
|
+
inputs: childInputs,
|
|
241
|
+
sessionId,
|
|
242
|
+
bundle,
|
|
243
|
+
scope: opts.scope,
|
|
244
|
+
parentStateDir: state.dir,
|
|
245
|
+
taskId,
|
|
246
|
+
runId: childRunId,
|
|
247
|
+
parentRunId: opts.runId,
|
|
248
|
+
depth: opts.depth + 1,
|
|
249
|
+
});
|
|
250
|
+
try {
|
|
251
|
+
while (true) {
|
|
252
|
+
const next = await run.next();
|
|
253
|
+
if (next.done) {
|
|
254
|
+
if (!next.value)
|
|
255
|
+
throw new Error(`子工作流 ${name} 未返回执行结果`);
|
|
256
|
+
const { reused, ...value } = next.value;
|
|
257
|
+
await state.appendProgress(`✓ 子流程 ${prefix} (${reused ? '复用' : '执行完成'})`);
|
|
258
|
+
return { value, reused };
|
|
259
|
+
}
|
|
260
|
+
const ev = next.value;
|
|
261
|
+
if (ev.type === 'plugin_progress' ||
|
|
262
|
+
ev.type === 'warning' ||
|
|
263
|
+
ev.type === 'text' ||
|
|
264
|
+
ev.type === 'assistant_message') {
|
|
265
|
+
const message = ev.type === 'text'
|
|
266
|
+
? ev.delta
|
|
267
|
+
: ev.type === 'assistant_message'
|
|
268
|
+
? typeof ev.message.content === 'string'
|
|
269
|
+
? ev.message.content
|
|
270
|
+
: JSON.stringify(ev.message.content)
|
|
271
|
+
: ev.message;
|
|
272
|
+
queue.push({
|
|
273
|
+
type: 'plugin_progress',
|
|
274
|
+
pluginId: WF_PLUGIN_ID,
|
|
275
|
+
message: `[${prefix}] ${message}`,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
else if (ev.type !== 'turn_done' &&
|
|
279
|
+
ev.type !== 'interrupted' &&
|
|
280
|
+
ev.type !== 'error')
|
|
281
|
+
queue.push(ev);
|
|
282
|
+
}
|
|
451
283
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
if (resume.loopResume?.lastVarsSnapshot) {
|
|
456
|
-
for (const [k, v] of Object.entries(resume.loopResume.lastVarsSnapshot)) {
|
|
457
|
-
if (k === 'inputs')
|
|
458
|
-
continue;
|
|
459
|
-
vars.setGlobal(k, v);
|
|
284
|
+
catch (error) {
|
|
285
|
+
await state.appendProgress(`✗ 子流程 ${prefix}: ${String(error)}`);
|
|
286
|
+
throw error;
|
|
460
287
|
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const msg = `♻ 检测到同名未完成任务,从第 ${startFrom + 1} 步续跑(已回放 ${resume.completedStepIds?.length ?? 0} 个完成步变量${loopNote})。要全新开始请先 /new。`;
|
|
466
|
-
yield { type: 'warning', message: msg };
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
if (resume.supplementaryGoal) {
|
|
291
|
+
await workflowContext.state.addGoalSupplement(resume.supplementaryGoal);
|
|
467
292
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
293
|
+
const startEvent = {
|
|
294
|
+
type: 'workflow_progress',
|
|
295
|
+
workflow: script.name,
|
|
296
|
+
runId: opts.runId,
|
|
297
|
+
rootRunId: opts.scope.rootRunId,
|
|
298
|
+
...(opts.parentRunId ? { parentRunId: opts.parentRunId } : {}),
|
|
299
|
+
depth: opts.depth,
|
|
300
|
+
phase: 'workflow',
|
|
301
|
+
status: 'start',
|
|
302
|
+
message: `开始 ${script.name}`,
|
|
475
303
|
};
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
304
|
+
await state.appendEvent(startEvent);
|
|
305
|
+
yield startEvent;
|
|
306
|
+
let failure = null;
|
|
307
|
+
let scriptResult;
|
|
308
|
+
const done = runScript(script, workflowContext, scriptHash)
|
|
309
|
+
.then(async (result) => {
|
|
310
|
+
ctx.signal?.throwIfAborted();
|
|
311
|
+
scriptResult = await state.writeResult(result);
|
|
312
|
+
})
|
|
313
|
+
.catch((e) => {
|
|
314
|
+
failure = e;
|
|
315
|
+
})
|
|
316
|
+
.finally(() => queue.close());
|
|
317
|
+
for await (const ev of queue.drain())
|
|
318
|
+
yield ev;
|
|
319
|
+
await done;
|
|
320
|
+
if (failure) {
|
|
321
|
+
// 中断(用户 ESC)与脚本出错语义完全不同:前者保留现场可 resume。
|
|
322
|
+
if (ctx.signal?.aborted ||
|
|
323
|
+
(failure instanceof Error && failure.name === 'AbortError')) {
|
|
324
|
+
await state.recordFailure('用户中断', [...(ctx.declaredArtifacts ?? [])]);
|
|
325
|
+
await state.appendProgress('■ 用户中断');
|
|
326
|
+
if (opts.parentStateDir)
|
|
327
|
+
throw Object.assign(new Error('用户中断'), { name: 'AbortError' });
|
|
328
|
+
const interruptedEvent = {
|
|
329
|
+
type: 'workflow_progress',
|
|
330
|
+
workflow: script.name,
|
|
331
|
+
runId: opts.runId,
|
|
332
|
+
rootRunId: opts.scope.rootRunId,
|
|
333
|
+
depth: opts.depth,
|
|
334
|
+
phase: 'workflow',
|
|
335
|
+
status: 'warning',
|
|
336
|
+
message: '用户中断,现场已保留',
|
|
337
|
+
};
|
|
338
|
+
await state.appendEvent(interruptedEvent);
|
|
339
|
+
yield interruptedEvent;
|
|
340
|
+
await ctx.managedBridge?.finish({
|
|
341
|
+
status: 'stopped',
|
|
342
|
+
artifacts: [...(ctx.declaredArtifacts ?? [])],
|
|
343
|
+
blocker: '用户停止,现场已保留',
|
|
344
|
+
});
|
|
345
|
+
yield { type: 'interrupted' };
|
|
490
346
|
return;
|
|
491
347
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
348
|
+
const message = failure instanceof WorkflowScriptError
|
|
349
|
+
? failure.message
|
|
350
|
+
: `工作流「${script.name}」执行失败:${failure instanceof Error ? failure.message : String(failure)}`;
|
|
351
|
+
// 工作流可以在 Error 上附带通用的 workflowOutcome。Runner 只透传这个
|
|
352
|
+
// 生命周期标签,不解释其业务含义。
|
|
353
|
+
const workflowOutcome = workflowOutcomeFromFailure(failure);
|
|
354
|
+
await state.recordFailure(message, [...(ctx.declaredArtifacts ?? [])]);
|
|
355
|
+
await state.appendProgress(`■ 失败:${message}`);
|
|
356
|
+
if (opts.parentStateDir)
|
|
357
|
+
throw failure;
|
|
358
|
+
const failureEvent = {
|
|
359
|
+
type: 'workflow_progress',
|
|
360
|
+
workflow: script.name,
|
|
361
|
+
runId: opts.runId,
|
|
362
|
+
rootRunId: opts.scope.rootRunId,
|
|
363
|
+
...(opts.parentRunId ? { parentRunId: opts.parentRunId } : {}),
|
|
364
|
+
depth: opts.depth,
|
|
365
|
+
phase: 'workflow',
|
|
366
|
+
status: 'warning',
|
|
367
|
+
message,
|
|
500
368
|
};
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
369
|
+
await state.appendEvent(failureEvent);
|
|
370
|
+
yield failureEvent;
|
|
371
|
+
await ctx.managedBridge?.finish({
|
|
372
|
+
status: 'failed',
|
|
373
|
+
artifacts: [...(ctx.declaredArtifacts ?? [])],
|
|
374
|
+
blocker: message,
|
|
375
|
+
...(workflowOutcome ? { outcome: workflowOutcome } : {}),
|
|
376
|
+
});
|
|
377
|
+
yield { type: 'error', error: message };
|
|
378
|
+
return;
|
|
508
379
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
380
|
+
// 把运行时经 declareArtifact() 登记的产物一并落盘 —— 只写 meta.artifacts
|
|
381
|
+
// 的话,循环体里的逐迭代产物会静默丢失(见 markDone 的注释)。
|
|
382
|
+
await state.markDone([...(ctx.declaredArtifacts ?? [])]);
|
|
383
|
+
await state.appendProgress(`✓ 完成(重放 ${journal.replayedCount} 次 / 上次账本 ${journal.loadedCount} 条)`);
|
|
384
|
+
const completeEvent = {
|
|
385
|
+
type: 'workflow_progress',
|
|
386
|
+
workflow: script.name,
|
|
387
|
+
runId: opts.runId,
|
|
388
|
+
rootRunId: opts.scope.rootRunId,
|
|
389
|
+
...(opts.parentRunId ? { parentRunId: opts.parentRunId } : {}),
|
|
390
|
+
depth: opts.depth,
|
|
391
|
+
phase: 'workflow',
|
|
392
|
+
status: 'complete',
|
|
393
|
+
message: '执行完成',
|
|
394
|
+
};
|
|
395
|
+
await state.appendEvent(completeEvent);
|
|
396
|
+
yield completeEvent;
|
|
397
|
+
await state.archive();
|
|
398
|
+
const artifacts = (await state.allArtifacts()).map((p) => resolve(state.workingDir, p));
|
|
399
|
+
if (opts.parentStateDir)
|
|
400
|
+
return {
|
|
401
|
+
workflow: script.name,
|
|
402
|
+
runId: opts.runId,
|
|
403
|
+
result: scriptResult,
|
|
404
|
+
artifacts,
|
|
405
|
+
reused: false,
|
|
406
|
+
};
|
|
407
|
+
const result = scriptResult && typeof scriptResult === 'object'
|
|
408
|
+
? scriptResult
|
|
409
|
+
: undefined;
|
|
410
|
+
const evidence = Array.isArray(result?.evidence)
|
|
411
|
+
? result.evidence.map((item) => String(item))
|
|
412
|
+
: [];
|
|
413
|
+
const finished = await ctx.managedBridge?.finish({
|
|
414
|
+
status: 'verified',
|
|
415
|
+
artifacts,
|
|
416
|
+
evidence,
|
|
417
|
+
});
|
|
418
|
+
// ⚠️ 收尾状态必须过进程边界。传进去 'verified' 拿回来的很可能是
|
|
419
|
+
// 'ready_for_acceptance' —— 机器自检通过但还没人验收,这两者对宿主是天差地别。
|
|
420
|
+
// 就地丢弃会让 `-p --output-format json` 的宿主把「等待验收」读成「办完了」。
|
|
421
|
+
yield { type: 'turn_done', ...managedStatusOf(finished) };
|
|
422
|
+
return {
|
|
423
|
+
workflow: script.name,
|
|
424
|
+
runId: opts.runId,
|
|
425
|
+
result: scriptResult,
|
|
426
|
+
artifacts,
|
|
427
|
+
reused: false,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Read the structured outcome after the ESM loader has wrapped a workflow
|
|
432
|
+
* exception. The wrapper is deliberately treated as an implementation detail:
|
|
433
|
+
* both its own field and a bounded `cause` chain are accepted so the
|
|
434
|
+
* workflow → loader → runner path cannot silently downgrade budget_limited to
|
|
435
|
+
* a generic failed result.
|
|
436
|
+
*/
|
|
437
|
+
export function workflowOutcomeFromFailure(failure) {
|
|
438
|
+
let current = failure;
|
|
439
|
+
for (let depth = 0; depth < 8 && current && typeof current === 'object'; depth++) {
|
|
440
|
+
const outcome = current.workflowOutcome;
|
|
441
|
+
if (typeof outcome === 'string' && outcome.trim())
|
|
442
|
+
return outcome.trim();
|
|
443
|
+
current = current.cause;
|
|
513
444
|
}
|
|
445
|
+
return undefined;
|
|
446
|
+
}
|
|
447
|
+
/** 从 bridge.finish 的返回值里取生命周期状态;拿不到就不带这个字段。 */
|
|
448
|
+
function managedStatusOf(finished) {
|
|
449
|
+
const status = finished?.status;
|
|
450
|
+
return typeof status === 'string' ? { managedStatus: status } : {};
|
|
514
451
|
}
|