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,604 +1,560 @@
|
|
|
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
13
|
|
|
25
|
-
import { existsSync } from 'node:fs';
|
|
26
14
|
import { resolve } from 'node:path';
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import { execLoopStep } from './controlFlow/loop.js';
|
|
42
|
-
import { execPauseStep } from './controlFlow/pause.js';
|
|
43
|
-
import { execSignalStep } from './controlFlow/signal.js';
|
|
44
|
-
import type { ControlDeps, ControlOutcome } from './controlFlow/types.js';
|
|
45
|
-
import { runStepWithRetry, type CpScope } from './stepPipeline.js';
|
|
46
|
-
import type {
|
|
47
|
-
RunContext,
|
|
48
|
-
StepDef,
|
|
49
|
-
StepKind,
|
|
50
|
-
StepResult,
|
|
51
|
-
WorkflowDef,
|
|
52
|
-
WorkflowEvent,
|
|
53
|
-
} from './types.js';
|
|
54
|
-
import { normalizeCapture, VarStack } from './types.js';
|
|
15
|
+
import type { LoopEvent } from '../../../src/plugin-sdk.js';
|
|
16
|
+
import { ExecutionScope, createWorkflowContext } from './runtime/context.js';
|
|
17
|
+
import { buildGoalAnchor } from './goalAnchor.js';
|
|
18
|
+
import { coerceInputs } from './inputCoercion.js';
|
|
19
|
+
import { Journal, fingerprint } from './journal.js';
|
|
20
|
+
import { bindResourceInputs } from './resourceInputs.js';
|
|
21
|
+
import { WorkflowScriptError, runScript } from './sandbox.js';
|
|
22
|
+
import {
|
|
23
|
+
DEFAULT_SESSION_ID,
|
|
24
|
+
buildTaskArbitrator,
|
|
25
|
+
sanitizeWorkflowDirName,
|
|
26
|
+
} from './taskIdentity.js';
|
|
27
|
+
import { effectiveWorkflowCwd } from './toolRuntime.js';
|
|
28
|
+
import type { RunContext, WorkflowScript } from './types.js';
|
|
55
29
|
import {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
} from './workflowState.js';
|
|
30
|
+
type WorkflowBundle,
|
|
31
|
+
resolveWorkflowBundle,
|
|
32
|
+
} from './workflowDependencies.js';
|
|
33
|
+
import { WorkflowState } from './workflowState.js';
|
|
60
34
|
|
|
61
|
-
/**
|
|
62
|
-
* 插件 ID:把私有 workflow_* 事件镜像成框架通用 plugin_progress 时挂这个 id。
|
|
63
|
-
* LiveArea 状态行会渲染成 "[plugin:workflow-runner] i/total <message>"
|
|
64
|
-
* (src/ui/LiveArea.tsx:123),让 TUI 里跑 workflow 不再"无声"。
|
|
65
|
-
* (-p 非交互模式另由 src/cli/print.ts 把通用 warning 事件落 stderr。)
|
|
66
|
-
*/
|
|
67
35
|
const WF_PLUGIN_ID = 'workflow-runner';
|
|
68
36
|
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
* 1. 优先取名为 "goal" 的输入(plan.yaml / ralph-loop.yaml 等通用约定)
|
|
75
|
-
* 2. 其次取第一个 required: true 的 string 类型输入
|
|
76
|
-
* 3. 都没有 → 返回空字符串(executor 自动跳过)
|
|
77
|
-
*
|
|
78
|
-
* 锚定字符串格式紧凑(~60-150 字符),注入每步 prompt 前置,
|
|
79
|
-
* 确保 LLM 在任何步骤都能"看到"用户原始目标,避免长上下文稀释。
|
|
80
|
-
*/
|
|
81
|
-
function buildGoalAnchor(
|
|
82
|
-
inputDefs: { name: string; type: string; required?: boolean }[],
|
|
83
|
-
inputValues: Record<string, unknown>,
|
|
84
|
-
): string {
|
|
85
|
-
let goalValue: string | undefined;
|
|
37
|
+
/** 事件队列:生产者随时 push,消费者按序 async 迭代,close 后排空即结束。 */
|
|
38
|
+
class EventQueue<T> {
|
|
39
|
+
private items: T[] = [];
|
|
40
|
+
private waiter: (() => void) | null = null;
|
|
41
|
+
private closed = false;
|
|
86
42
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (typeof v === 'string' && v.trim()) {
|
|
92
|
-
goalValue = v.trim();
|
|
93
|
-
}
|
|
43
|
+
push(item: T): void {
|
|
44
|
+
this.items.push(item);
|
|
45
|
+
this.waiter?.();
|
|
46
|
+
this.waiter = null;
|
|
94
47
|
}
|
|
95
48
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const v = inputValues[firstRequired.name];
|
|
101
|
-
if (typeof v === 'string' && v.trim()) {
|
|
102
|
-
goalValue = v.trim();
|
|
103
|
-
}
|
|
104
|
-
}
|
|
49
|
+
close(): void {
|
|
50
|
+
this.closed = true;
|
|
51
|
+
this.waiter?.();
|
|
52
|
+
this.waiter = null;
|
|
105
53
|
}
|
|
106
54
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** 可中断的退避等待:用户 abort 时立即返回(让后续步骤检查 signal 收口,不在这里抛)。 */
|
|
118
|
-
function sleepInterruptible(ms: number, signal?: AbortSignal): Promise<void> {
|
|
119
|
-
return new Promise((resolve) => {
|
|
120
|
-
if (signal?.aborted) {
|
|
121
|
-
resolve();
|
|
122
|
-
return;
|
|
55
|
+
async *drain(): AsyncGenerator<T, void, void> {
|
|
56
|
+
while (true) {
|
|
57
|
+
while (this.items.length > 0) {
|
|
58
|
+
yield this.items.shift() as T;
|
|
59
|
+
}
|
|
60
|
+
if (this.closed) return;
|
|
61
|
+
await new Promise<void>((resolve) => {
|
|
62
|
+
this.waiter = resolve;
|
|
63
|
+
});
|
|
123
64
|
}
|
|
124
|
-
|
|
125
|
-
signal?.removeEventListener('abort', onAbort);
|
|
126
|
-
resolve();
|
|
127
|
-
}, ms);
|
|
128
|
-
const onAbort = (): void => {
|
|
129
|
-
clearTimeout(timer);
|
|
130
|
-
resolve();
|
|
131
|
-
};
|
|
132
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
133
|
-
});
|
|
65
|
+
}
|
|
134
66
|
}
|
|
135
67
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
* - FlowSignal : break / continue 循环信号(工作流不算失败)。
|
|
141
|
-
* target 缺省 = 最近一层循环消费;指定 target 时信号穿透中间层循环上抛,
|
|
142
|
-
* 直到 id 匹配的 loop 消费(break=停止迭代 / continue=跳到下一轮)。
|
|
143
|
-
* 目标不存在时冒泡到顶层按正常完成处理(与游离 break 同语义)。
|
|
144
|
-
* 关键差异:break/continue 只影响循环迭代,循环之后的兄弟步应继续;halt 才真正向上中止。
|
|
145
|
-
*/
|
|
146
|
-
interface FlowSignal {
|
|
147
|
-
signal: 'break' | 'continue';
|
|
148
|
-
target?: string;
|
|
68
|
+
export interface RunWorkflowOptions {
|
|
69
|
+
ctx: RunContext;
|
|
70
|
+
inputs: Record<string, unknown>;
|
|
71
|
+
sessionId?: string;
|
|
149
72
|
}
|
|
150
|
-
type StepFlow = 'completed' | 'halted' | FlowSignal;
|
|
151
|
-
|
|
152
73
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
): Promise<void> {
|
|
159
|
-
const t = text.trim();
|
|
160
|
-
if (!t) return;
|
|
161
|
-
// 锚定必须紧凑(注入每步 prompt),逐条截断;完整原文在 capture 变量 / history 里
|
|
162
|
-
const compact = t.length > 240 ? `${t.slice(0, 240)}…` : t;
|
|
163
|
-
ctx.goalSupplements ??= [];
|
|
164
|
-
ctx.goalSupplements.push(compact);
|
|
165
|
-
await state.appendSupplement(compact);
|
|
74
|
+
export interface WorkflowCallResult {
|
|
75
|
+
workflow: string;
|
|
76
|
+
runId: string;
|
|
77
|
+
result: unknown;
|
|
78
|
+
artifacts: string[];
|
|
166
79
|
}
|
|
167
80
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
81
|
+
interface ExecutionOptions extends RunWorkflowOptions {
|
|
82
|
+
bundle: WorkflowBundle;
|
|
83
|
+
scope: ExecutionScope;
|
|
84
|
+
parentStateDir?: string;
|
|
85
|
+
taskId?: string;
|
|
86
|
+
runId: string;
|
|
87
|
+
parentRunId?: string;
|
|
88
|
+
depth: number;
|
|
176
89
|
}
|
|
177
90
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
error: `${label} max_iterations parse failed: ${JSON.stringify(raw)} -> "${String(interpolated)}" is not a positive number`,
|
|
91
|
+
/** 执行一个代码形态工作流,产出 LoopEvent 流。 */
|
|
92
|
+
export async function* runWorkflowScript(
|
|
93
|
+
script: WorkflowScript,
|
|
94
|
+
opts: RunWorkflowOptions,
|
|
95
|
+
): AsyncGenerator<LoopEvent, void, void> {
|
|
96
|
+
try {
|
|
97
|
+
const bundle = await resolveWorkflowBundle(
|
|
98
|
+
script,
|
|
99
|
+
effectiveWorkflowCwd(opts.ctx),
|
|
100
|
+
);
|
|
101
|
+
opts.ctx.resourceLedger ??= {
|
|
102
|
+
inputTokens: 0,
|
|
103
|
+
outputTokens: 0,
|
|
104
|
+
limit: null,
|
|
105
|
+
trustworthy: true,
|
|
194
106
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
): AsyncGenerator<LoopEvent, StepResult, void> {
|
|
208
|
-
if (step.type === 'assert') return execAssertStep(step, vars, ctx);
|
|
209
|
-
if (step.type === 'verify') return await execVerifyStep(step, vars);
|
|
210
|
-
// set / echo 从 runSteps 的内联块搬进执行器体系:它们不影响迭代、只产出 StepResult。
|
|
211
|
-
// 搬过来的直接收益是 `capture:` 自然生效(此前它们从不调 bindCapture —— 静默失效)。
|
|
212
|
-
if (step.type === 'set') return yield* execSetStep(step, vars);
|
|
213
|
-
if (step.type === 'echo') return yield* execEchoStep(step, vars);
|
|
214
|
-
if (step.tool) return yield* execToolStep(step, vars, ctx);
|
|
215
|
-
if (step.agent) return yield* execAgentStep(step, vars, ctx);
|
|
216
|
-
if (step.llm) return yield* execLlmStep(step, vars, ctx);
|
|
217
|
-
if (step.skill) return yield* execSkillStep(step, vars, ctx);
|
|
218
|
-
throw new Error(`step ${step.id}: 无可执行字段(tool/agent/llm/skill/assert/verify)`);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* 递归执行 steps[]。返回 StepFlow(completed / halted / break|continue 信号,见上方定义)。
|
|
223
|
-
*
|
|
224
|
-
* yields 混合事件:WorkflowEvent + LoopEvent(skill 子循环转发 / error / interrupted)。
|
|
225
|
-
* index/total 字段相对**当前 block**,嵌套 block 自有计数(branch.then 内 3 步就报 total=3)。
|
|
226
|
-
*/
|
|
227
|
-
async function* runSteps(
|
|
228
|
-
steps: StepDef[],
|
|
229
|
-
vars: VarStack,
|
|
230
|
-
ctx: RunContext,
|
|
231
|
-
state: WorkflowState,
|
|
232
|
-
// checkpoint 记录范围:顶层调用(runWorkflow → def.steps)传 { name: id=>id, descendIntoLoop:true };
|
|
233
|
-
// 顶层 loop 的 do[] 由 loop 处理器派生复合名 scope;更深嵌套传 undefined(不记录)。
|
|
234
|
-
cp?: CpScope,
|
|
235
|
-
// resume:本列表中索引 < startFrom 的步已在上次运行完成,本次跳过(变量已由 runWorkflow 回放)。
|
|
236
|
-
// 顶层与「loop resume 进迭代中间」(loop 处理器对 do[] 传 innerStartFrom)都用它。
|
|
237
|
-
startFrom = 0,
|
|
238
|
-
// 仅顶层传:进行中循环的恢复信息(迭代号 + 迭代内起点),由匹配 id 的 loop 消费一次。
|
|
239
|
-
loopResume?: LoopResumeInfo,
|
|
240
|
-
): AsyncGenerator<WorkflowEvent | LoopEvent, StepFlow, void> {
|
|
241
|
-
for (let i = 0; i < steps.length; i++) {
|
|
242
|
-
const step = steps[i];
|
|
243
|
-
if (ctx.signal?.aborted) {
|
|
244
|
-
yield { type: 'interrupted' };
|
|
245
|
-
return 'halted';
|
|
107
|
+
const scope = new ExecutionScope();
|
|
108
|
+
const lease = await scope.scheduler.acquire('root', scope.rootRunId, opts.ctx.signal);
|
|
109
|
+
try {
|
|
110
|
+
yield* executeWorkflowScript(script, {
|
|
111
|
+
...opts,
|
|
112
|
+
bundle,
|
|
113
|
+
scope,
|
|
114
|
+
runId: scope.rootRunId,
|
|
115
|
+
depth: 0,
|
|
116
|
+
});
|
|
117
|
+
} finally {
|
|
118
|
+
lease.release();
|
|
246
119
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (
|
|
122
|
+
opts.ctx.signal?.aborted ||
|
|
123
|
+
(error instanceof Error && error.name === 'AbortError')
|
|
124
|
+
)
|
|
125
|
+
yield { type: 'interrupted' };
|
|
126
|
+
else
|
|
250
127
|
yield {
|
|
251
|
-
type: '
|
|
252
|
-
|
|
253
|
-
reason: 'resume: 上次已完成',
|
|
128
|
+
type: 'error',
|
|
129
|
+
error: error instanceof Error ? error.message : String(error),
|
|
254
130
|
};
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// when 条件:**所有** step 类型统一在这里判。控制流处理器里不要再判一次 ——
|
|
259
|
-
// 重构前 break/continue 块自带一份,而它恒为真(false 时这里已经 continue 掉了),
|
|
260
|
-
// 连它的 catch 分支都不可达。那是纯死代码,随本次重构删除。
|
|
261
|
-
if (step.when) {
|
|
262
|
-
let pass = false;
|
|
263
|
-
try {
|
|
264
|
-
pass = Boolean(evalExpr(step.when, vars));
|
|
265
|
-
} catch (e) {
|
|
266
|
-
const errMsg = `when 表达式错误: ${(e as Error).message}`;
|
|
267
|
-
yield { type: 'workflow_step_end', id: step.id, ok: false, error: errMsg };
|
|
268
|
-
yield {
|
|
269
|
-
type: 'error',
|
|
270
|
-
error: `Workflow halted at step "${step.id}": ${errMsg}`,
|
|
271
|
-
};
|
|
272
|
-
return 'halted';
|
|
273
|
-
}
|
|
274
|
-
if (!pass) {
|
|
275
|
-
yield { type: 'workflow_step_skipped', id: step.id, reason: 'when=false' };
|
|
276
|
-
await state.appendProgress(`- ${step.id} skipped (when=false)`);
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const kind = stepKind(step);
|
|
282
|
-
dlog('wf', `step ${i + 1}/${steps.length} START ${step.id} (${kind})`);
|
|
283
|
-
// 已插值的人话进度名随 start 事件带给 UI:scrollback 进度行显示它而非块内相对
|
|
284
|
-
// 序号,与状态行(下方 plugin_progress)保持一致。未声明 title 的步省略此字段。
|
|
285
|
-
const interpolatedTitle = step.title ? interpolate(step.title, vars) : undefined;
|
|
286
|
-
yield {
|
|
287
|
-
type: 'workflow_step_start',
|
|
288
|
-
id: step.id,
|
|
289
|
-
kind,
|
|
290
|
-
index: i,
|
|
291
|
-
total: steps.length,
|
|
292
|
-
title: interpolatedTitle,
|
|
293
|
-
};
|
|
294
|
-
// P1 TUI 可见:把私有 workflow_step_start 镜像成框架通用 plugin_progress。
|
|
295
|
-
// 有 title 的步显示人话阶段名(如「阶段2/6·起草实现计划」)且不带块内序号——
|
|
296
|
-
// 嵌套块的 i/total(顶层 5、循环体 12…)会被用户误读成"跳步/结构乱";
|
|
297
|
-
// 未声明 title 的步保持 legacy 格式 "i/total step <id> (<kind>)"。
|
|
298
|
-
yield {
|
|
299
|
-
type: 'plugin_progress',
|
|
300
|
-
pluginId: WF_PLUGIN_ID,
|
|
301
|
-
current: i + 1,
|
|
302
|
-
max: steps.length,
|
|
303
|
-
message: interpolatedTitle ?? `step ${step.id} (${kind})`,
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
// ---------- 控制流:改变的是「循环怎么走」,不是产出一个 StepResult ----------
|
|
307
|
-
//
|
|
308
|
-
// 所以它们不塞进 stepExecutors 那套 (step,vars,ctx)→StepResult 的形状里,
|
|
309
|
-
// 各自住在 controlFlow/ 并共用出口 ControlOutcome —— 这里只需一个分派。
|
|
310
|
-
const controlDeps: ControlDeps = {
|
|
311
|
-
ctx,
|
|
312
|
-
state,
|
|
313
|
-
...(cp ? { cp } : {}),
|
|
314
|
-
runBlock: (blockSteps, blockVars, blockCp, blockStart, blockResume) =>
|
|
315
|
-
runSteps(blockSteps, blockVars, ctx, state, blockCp, blockStart, blockResume),
|
|
316
|
-
index: i,
|
|
317
|
-
total: steps.length,
|
|
318
|
-
addGoalSupplement: (text: string) => addGoalSupplement(ctx, state, text),
|
|
319
|
-
};
|
|
320
|
-
let outcome: ControlOutcome | null = null;
|
|
321
|
-
if (step.type === 'branch') {
|
|
322
|
-
outcome = yield* execBranchStep(step, vars, controlDeps);
|
|
323
|
-
} else if (step.type === 'loop') {
|
|
324
|
-
outcome = yield* execLoopStep(step, vars, controlDeps, loopResume);
|
|
325
|
-
// 恢复信息只消费一次:匹配到的那个 loop 用掉之后,后续 loop 不再套用。
|
|
326
|
-
if (loopResume && loopResume.loopId === step.id) loopResume = undefined;
|
|
327
|
-
} else if (step.type === 'pause') {
|
|
328
|
-
outcome = yield* execPauseStep(step, vars, controlDeps);
|
|
329
|
-
} else if (step.type === 'break' || step.type === 'continue') {
|
|
330
|
-
outcome = yield* execSignalStep(step, controlDeps);
|
|
331
|
-
}
|
|
332
|
-
if (outcome) {
|
|
333
|
-
if (outcome.kind === 'halted') return 'halted';
|
|
334
|
-
if (outcome.kind === 'flow') return outcome.signal;
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// ---------- 动作步:tool / llm / skill / agent / assert / verify / set / echo ----------
|
|
339
|
-
//
|
|
340
|
-
// 统一走 runStepWithRetry:in-run 重试 → onError 三态 → commitStep 提交。
|
|
341
|
-
// 「提交一步」只有 stepPipeline 那一份实现,这里不再自己写(见 stepPipeline 头注)。
|
|
342
|
-
const actionOutcome = yield* runStepWithRetry({
|
|
343
|
-
step,
|
|
344
|
-
kind,
|
|
345
|
-
vars,
|
|
346
|
-
ctx,
|
|
347
|
-
state,
|
|
348
|
-
...(cp ? { cp } : {}),
|
|
349
|
-
index: i,
|
|
350
|
-
total: steps.length,
|
|
351
|
-
addGoalSupplement: (text: string) => addGoalSupplement(ctx, state, text),
|
|
352
|
-
execute: () => dispatchActionStep(step, vars, ctx),
|
|
353
|
-
});
|
|
354
|
-
if (actionOutcome === 'halted') return 'halted';
|
|
355
131
|
}
|
|
356
|
-
return 'completed';
|
|
357
132
|
}
|
|
358
133
|
|
|
359
|
-
/**
|
|
360
|
-
*
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
* + 通用 LoopEvent(error / interrupted / 子 step 透传的 text/tool/stage_change 等,
|
|
384
|
-
* UI 识别并渲染)。经 plugin.ts -> pluginRunner 透传给 UI;workflow_* 由 PluginEvent
|
|
385
|
-
* 开放契约承载。
|
|
386
|
-
*
|
|
387
|
-
* 成功跑完以 `workflow_done` 收尾(携带 vars 快照);中途失败/中断只 yield error/interrupted。
|
|
388
|
-
* **对外流式协议的通用终结事件 `turn_done` 由命令层 index.ts 在成功后补发**(见那里)——
|
|
389
|
-
* 本函数自身事件序列保持不变,直测它的 runner.test.ts / 单元测试仍以 workflow_done 为最后一条。
|
|
390
|
-
*/
|
|
391
|
-
export async function* runWorkflow(
|
|
392
|
-
def: WorkflowDef,
|
|
393
|
-
inputs: Record<string, unknown>,
|
|
394
|
-
ctx: RunContext,
|
|
395
|
-
): AsyncGenerator<WorkflowEvent | LoopEvent, void, void> {
|
|
396
|
-
const vars = new VarStack();
|
|
397
|
-
vars.setGlobal('inputs', inputs);
|
|
398
|
-
|
|
399
|
-
// onError:'recover' 用:把工作流总目标(description)透传给恢复 agent,
|
|
400
|
-
// 让它知道「这条工作流到底想干什么」。已有 goal 则不覆盖。
|
|
401
|
-
const runCtx: RunContext = { ...ctx, goal: ctx.goal ?? def.description };
|
|
402
|
-
|
|
403
|
-
// 通用目标锚定:从 inputs 中提取用户的原始目标,
|
|
404
|
-
// 注入 RunContext.goalAnchor,让 agent/llm/skill 步自动前置到每步 prompt。
|
|
405
|
-
// 提取策略:优先 "goal" 命名的输入 → 其次第一个 required string 输入。
|
|
406
|
-
runCtx.goalAnchor = buildGoalAnchor(def.inputs ?? [], inputs);
|
|
407
|
-
|
|
408
|
-
// self_heal:把全局 max_retries 默认透传给每步的 in-run retry(step.max_retries 可覆盖)。
|
|
409
|
-
runCtx.selfHealMaxRetries = def.self_heal?.max_retries;
|
|
410
|
-
runCtx.retryDelayMs = def.self_heal?.retry_delay_ms;
|
|
411
|
-
runCtx.maxTurnsRetryDelayMs = def.self_heal?.max_turns_retry_delay_ms;
|
|
412
|
-
|
|
413
|
-
// 声明产物清单:state.init 用于 new-task 清理/completed 报产物;
|
|
414
|
-
// 挂到 runCtx 供 tool 步覆盖判定(声明产物自动 pre-Read 解 Write 守卫)。
|
|
415
|
-
const declaredArtifacts = collectDeclaredArtifacts(def);
|
|
416
|
-
runCtx.declaredArtifacts = declaredArtifacts;
|
|
417
|
-
|
|
418
|
-
// 任务身份语义仲裁器(hash 不等 + 旧任务未完成时的二审,见 taskIdentity.ts):
|
|
419
|
-
// 短语快路径 → LLM 单轮仲裁 → AskUserQuestion → 兜底 new-task。
|
|
420
|
-
// init 非 generator,仲裁过程的人话记录收进 notes,init 返回后统一 yield。
|
|
134
|
+
/** Same executor for roots and children. Only roots emit terminal host events. */
|
|
135
|
+
async function* executeWorkflowScript(
|
|
136
|
+
script: WorkflowScript,
|
|
137
|
+
opts: ExecutionOptions,
|
|
138
|
+
): AsyncGenerator<
|
|
139
|
+
LoopEvent,
|
|
140
|
+
(WorkflowCallResult & { reused: boolean }) | undefined,
|
|
141
|
+
void
|
|
142
|
+
> {
|
|
143
|
+
const { ctx, inputs } = opts;
|
|
144
|
+
ctx.signal?.throwIfAborted();
|
|
145
|
+
const sessionId = sanitizeWorkflowDirName(
|
|
146
|
+
opts.sessionId ?? ctx.runtimeContext?.sessionId ?? DEFAULT_SESSION_ID,
|
|
147
|
+
);
|
|
148
|
+
const scriptHash = opts.bundle.hash;
|
|
149
|
+
|
|
150
|
+
const state = new WorkflowState(
|
|
151
|
+
effectiveWorkflowCwd(ctx),
|
|
152
|
+
script.name,
|
|
153
|
+
sessionId,
|
|
154
|
+
script.meta.inputs,
|
|
155
|
+
opts.taskId ?? ctx.managedExecution?.runId,
|
|
156
|
+
opts.parentStateDir,
|
|
157
|
+
);
|
|
421
158
|
const arbitrationNotes: string[] = [];
|
|
422
|
-
const state = new WorkflowState(getWorkingDir(), def.name);
|
|
423
159
|
const resume = await state.init(
|
|
424
|
-
def.name,
|
|
425
160
|
inputs,
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
161
|
+
scriptHash,
|
|
162
|
+
script.meta.artifacts ?? [],
|
|
163
|
+
ctx.managedExecution || opts.parentStateDir
|
|
164
|
+
? undefined
|
|
165
|
+
: buildTaskArbitrator({
|
|
166
|
+
provider: ctx.provider,
|
|
167
|
+
chat: ctx.chat,
|
|
168
|
+
executeTool: ctx.executeTool,
|
|
169
|
+
signal: ctx.signal,
|
|
170
|
+
notes: arbitrationNotes,
|
|
171
|
+
}),
|
|
436
172
|
);
|
|
437
173
|
|
|
438
|
-
|
|
439
|
-
type: '
|
|
440
|
-
name: def.name,
|
|
441
|
-
totalSteps: def.steps.length,
|
|
442
|
-
};
|
|
443
|
-
|
|
444
|
-
// 仲裁过程可见化(命中延续短语 / LLM 判定 / 用户确认 / 兜底)
|
|
445
|
-
for (const n of arbitrationNotes) {
|
|
446
|
-
yield { type: 'warning', message: n };
|
|
174
|
+
for (const note of arbitrationNotes) {
|
|
175
|
+
yield { type: 'warning', message: note };
|
|
447
176
|
}
|
|
448
177
|
|
|
449
|
-
//
|
|
450
|
-
// 成功运行会 archive() 把状态目录改名 .done,init() 回退读 .done 检测到 completed。
|
|
178
|
+
// 已完成:报实际产物,不重跑。
|
|
451
179
|
if (resume.mode === 'completed') {
|
|
452
|
-
const
|
|
180
|
+
const restoredEvent = {
|
|
181
|
+
type: 'workflow_progress',
|
|
182
|
+
workflow: script.name,
|
|
183
|
+
runId: opts.runId,
|
|
184
|
+
rootRunId: opts.scope.rootRunId,
|
|
185
|
+
...(opts.parentRunId ? { parentRunId: opts.parentRunId } : {}),
|
|
186
|
+
depth: opts.depth,
|
|
187
|
+
phase: 'workflow',
|
|
188
|
+
status: 'complete',
|
|
189
|
+
message: '恢复已完成结果',
|
|
190
|
+
} as const;
|
|
191
|
+
await state.appendEvent(restoredEvent);
|
|
192
|
+
yield restoredEvent;
|
|
193
|
+
const list = resume.artifacts?.length
|
|
194
|
+
? resume.artifacts.map((a) => ` · ${a}`).join('\n')
|
|
195
|
+
: ' (本次运行没有声明产物)';
|
|
453
196
|
yield {
|
|
454
197
|
type: 'text',
|
|
455
|
-
delta:
|
|
198
|
+
delta: `工作流「${script.name}」的这个目标此前已完成,产物:\n${list}\n换个目标即可重新执行。\n`,
|
|
199
|
+
};
|
|
200
|
+
if (!opts.parentStateDir) yield { type: 'turn_done' };
|
|
201
|
+
return {
|
|
202
|
+
workflow: script.name,
|
|
203
|
+
runId: opts.runId,
|
|
204
|
+
result: await state.readResult(),
|
|
205
|
+
artifacts: (resume.artifacts ?? []).map((p) =>
|
|
206
|
+
resolve(state.workingDir, p),
|
|
207
|
+
),
|
|
208
|
+
reused: true,
|
|
456
209
|
};
|
|
457
|
-
yield { type: 'workflow_done', name: def.name, vars: vars.snapshot() };
|
|
458
|
-
return;
|
|
459
210
|
}
|
|
460
211
|
|
|
461
|
-
|
|
462
|
-
// 必须明示——曾有用户在 yaml 重构后同 goal 重跑,看到「从第 2 步续跑」
|
|
463
|
-
// 困惑是 bug 还是残留;作废旧断点时不说原因同样会被当成 bug。
|
|
464
|
-
if (resume.defChanged) {
|
|
212
|
+
if (resume.scriptChanged) {
|
|
465
213
|
yield {
|
|
466
214
|
type: 'warning',
|
|
467
|
-
message:
|
|
468
|
-
'♻ 工作流定义已变更(步骤结构与旧断点不再匹配),旧执行现场已作废,本次从头开始执行。',
|
|
215
|
+
message: `⚠ 工作流「${script.name}」的脚本/依赖已改变或产物/返回值缺失 —— 旧断点作废,本次全新开始。`,
|
|
469
216
|
};
|
|
470
217
|
}
|
|
471
|
-
|
|
472
|
-
// fresh/new-task 启动清理可见化:全新任务开始前已删掉的残留产物(上次任务
|
|
473
|
-
// 的声明产物 + /new 后无主孤儿)。用户约定:全新 plan 一律从干净状态开始;
|
|
474
|
-
// 删除范围仅限本工作流声明的产物,同任务 resume 不清理。
|
|
475
|
-
if (resume.cleanedArtifacts?.length) {
|
|
218
|
+
if (resume.archivedTo) {
|
|
476
219
|
yield {
|
|
477
220
|
type: 'warning',
|
|
478
|
-
message:
|
|
221
|
+
message: `旧产物已归档到 ${resume.archivedTo}(不是删除,需要时可取回)。`,
|
|
479
222
|
};
|
|
480
223
|
}
|
|
481
224
|
|
|
482
|
-
//
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
225
|
+
// 目标锚:仲裁判定的延续要锚**旧目标**,本次输入降级成补充要求。
|
|
226
|
+
ctx.goalAnchor = ctx.managedExecution
|
|
227
|
+
? ctx.managedExecution.anchor
|
|
228
|
+
: resume.prevGoal
|
|
229
|
+
? buildGoalAnchor({ goal: resume.prevGoal }, script.meta.inputs)
|
|
230
|
+
: buildGoalAnchor(inputs, script.meta.inputs);
|
|
231
|
+
ctx.goalSupplements = await state.readSupplements();
|
|
232
|
+
ctx.declaredArtifacts = new Set(script.meta.artifacts ?? []);
|
|
233
|
+
|
|
234
|
+
const journal = new Journal(state.journalPath, {
|
|
235
|
+
bundleHash: scriptHash,
|
|
236
|
+
rootRunId: opts.scope.rootRunId,
|
|
237
|
+
});
|
|
238
|
+
await journal.load(script.meta.resumeBackoff ?? 0);
|
|
494
239
|
|
|
495
|
-
if (resume.mode === 'resume'
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
runCtx.goalAnchor = `> 🎯 用户原始目标:${truncateGoal(resume.prevGoal)}\n> ⚠️ 所有操作必须直接回应此目标,不可偏离或遗忘。`;
|
|
504
|
-
// 新输入是带信息量的补充指令(非纯「继续」延续短语)→ 并入贯穿补充要求;
|
|
505
|
-
// 经 goalSupplements 统一渲染(与 pause 反馈/澄清结论同通道),且去重防
|
|
506
|
-
// 反复 resume 时同一句话累积成噪声。
|
|
507
|
-
const supp = resume.supplementaryGoal;
|
|
508
|
-
if (
|
|
509
|
-
supp &&
|
|
510
|
-
supp !== resume.prevGoal &&
|
|
511
|
-
!isContinuationPhrase(supp) &&
|
|
512
|
-
!(runCtx.goalSupplements ?? []).some((s) => s.startsWith(supp.slice(0, 240)))
|
|
513
|
-
) {
|
|
514
|
-
await addGoalSupplement(runCtx, state, supp);
|
|
515
|
-
}
|
|
240
|
+
if (resume.mode === 'resume') {
|
|
241
|
+
yield {
|
|
242
|
+
type: 'warning',
|
|
243
|
+
message:
|
|
244
|
+
`↻ 续跑工作流「${script.name}」(上次已完成 ${journal.loadedCount} 次调用` +
|
|
245
|
+
`${resume.lastError ? `,上次失败于:${resume.lastError}` : ''})。`,
|
|
246
|
+
};
|
|
516
247
|
}
|
|
517
248
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
249
|
+
const queue = new EventQueue<LoopEvent>();
|
|
250
|
+
const workflowContext = createWorkflowContext({
|
|
251
|
+
ctx,
|
|
252
|
+
state,
|
|
253
|
+
journal,
|
|
254
|
+
emit: (ev) => queue.push(ev),
|
|
255
|
+
args: inputs,
|
|
256
|
+
workflowName: script.name,
|
|
257
|
+
runId: opts.runId,
|
|
258
|
+
parentRunId: opts.parentRunId,
|
|
259
|
+
depth: opts.depth,
|
|
260
|
+
session: sessionId,
|
|
261
|
+
task: state.taskId,
|
|
262
|
+
workflowDir: script.dir,
|
|
263
|
+
inputResourceMap: ctx.inputResourceMap,
|
|
264
|
+
scope: opts.scope,
|
|
265
|
+
invokeWorkflow: async (name, rawInputs, site, label) => {
|
|
266
|
+
const bundle = opts.bundle.children.get(name);
|
|
267
|
+
if (!bundle)
|
|
268
|
+
throw new Error(`子工作流 ${name} 未声明在 meta.dependencies 中`);
|
|
269
|
+
const bound = await bindResourceInputs({
|
|
270
|
+
workflowName: name,
|
|
271
|
+
sessionId,
|
|
272
|
+
cwd: effectiveWorkflowCwd(ctx),
|
|
273
|
+
rawInputs: rawInputs as Record<string, string>,
|
|
274
|
+
defs: bundle.script.meta.inputs,
|
|
275
|
+
attachments: [],
|
|
276
|
+
});
|
|
277
|
+
const childInputs = coerceInputs(
|
|
278
|
+
bound.inputs as Record<string, string>,
|
|
279
|
+
bundle.script.meta.inputs,
|
|
280
|
+
);
|
|
281
|
+
if (bound.resourceIdentity.length)
|
|
282
|
+
childInputs.__resource_fingerprint = bound.resourceIdentity.join('|');
|
|
283
|
+
const taskId = fingerprint('child-instance-v1', {
|
|
284
|
+
parent: [script.name, state.taskId],
|
|
285
|
+
site,
|
|
286
|
+
name,
|
|
287
|
+
inputs: childInputs,
|
|
288
|
+
});
|
|
289
|
+
const childRunId = fingerprint('child-run-v2', {
|
|
290
|
+
rootRunId: opts.scope.rootRunId,
|
|
291
|
+
parentRunId: opts.runId,
|
|
292
|
+
site,
|
|
293
|
+
taskId,
|
|
294
|
+
});
|
|
295
|
+
const childCtx: RunContext = {
|
|
296
|
+
...ctx,
|
|
297
|
+
cwd: effectiveWorkflowCwd(ctx),
|
|
298
|
+
history: structuredClone(
|
|
299
|
+
ctx.history.filter((m) => m.role === 'system'),
|
|
300
|
+
),
|
|
301
|
+
sessionState: ctx.sessionState
|
|
302
|
+
? structuredClone(ctx.sessionState)
|
|
303
|
+
: undefined,
|
|
304
|
+
goal: undefined,
|
|
305
|
+
goalAnchor: undefined,
|
|
306
|
+
goalSupplements: undefined,
|
|
307
|
+
declaredArtifacts: undefined,
|
|
308
|
+
transientContext: undefined,
|
|
309
|
+
transientBundle: undefined,
|
|
310
|
+
managedExecution: undefined,
|
|
311
|
+
managedBridge: undefined,
|
|
312
|
+
usageRecorder:
|
|
313
|
+
ctx.usageRecorder ??
|
|
314
|
+
ctx.managedBridge?.recordUsage?.bind(ctx.managedBridge),
|
|
315
|
+
inputResources: Object.values(bound.resourceMap).flat(),
|
|
316
|
+
inputResourceMap: bound.resourceMap,
|
|
317
|
+
};
|
|
318
|
+
const prefix = `${script.name} → ${label ?? name}`;
|
|
319
|
+
await state.appendProgress(`▶ 子流程 ${prefix} (${site}, ${taskId})`);
|
|
320
|
+
const run = executeWorkflowScript(bundle.script, {
|
|
321
|
+
ctx: childCtx,
|
|
322
|
+
inputs: childInputs,
|
|
323
|
+
sessionId,
|
|
324
|
+
bundle,
|
|
325
|
+
scope: opts.scope,
|
|
326
|
+
parentStateDir: state.dir,
|
|
327
|
+
taskId,
|
|
328
|
+
runId: childRunId,
|
|
329
|
+
parentRunId: opts.runId,
|
|
330
|
+
depth: opts.depth + 1,
|
|
331
|
+
});
|
|
332
|
+
try {
|
|
333
|
+
while (true) {
|
|
334
|
+
const next = await run.next();
|
|
335
|
+
if (next.done) {
|
|
336
|
+
if (!next.value) throw new Error(`子工作流 ${name} 未返回执行结果`);
|
|
337
|
+
const { reused, ...value } = next.value;
|
|
338
|
+
await state.appendProgress(
|
|
339
|
+
`✓ 子流程 ${prefix} (${reused ? '复用' : '执行完成'})`,
|
|
340
|
+
);
|
|
341
|
+
return { value, reused };
|
|
342
|
+
}
|
|
343
|
+
const ev = next.value;
|
|
344
|
+
if (
|
|
345
|
+
ev.type === 'plugin_progress' ||
|
|
346
|
+
ev.type === 'warning' ||
|
|
347
|
+
ev.type === 'text' ||
|
|
348
|
+
ev.type === 'assistant_message'
|
|
349
|
+
) {
|
|
350
|
+
const message =
|
|
351
|
+
ev.type === 'text'
|
|
352
|
+
? ev.delta
|
|
353
|
+
: ev.type === 'assistant_message'
|
|
354
|
+
? typeof ev.message.content === 'string'
|
|
355
|
+
? ev.message.content
|
|
356
|
+
: JSON.stringify(ev.message.content)
|
|
357
|
+
: ev.message;
|
|
358
|
+
queue.push({
|
|
359
|
+
type: 'plugin_progress',
|
|
360
|
+
pluginId: WF_PLUGIN_ID,
|
|
361
|
+
message: `[${prefix}] ${message}`,
|
|
362
|
+
});
|
|
363
|
+
} else if (
|
|
364
|
+
ev.type !== 'turn_done' &&
|
|
365
|
+
ev.type !== 'interrupted' &&
|
|
366
|
+
ev.type !== 'error'
|
|
367
|
+
)
|
|
368
|
+
queue.push(ev);
|
|
369
|
+
}
|
|
370
|
+
} catch (error) {
|
|
371
|
+
await state.appendProgress(`✗ 子流程 ${prefix}: ${String(error)}`);
|
|
372
|
+
throw error;
|
|
536
373
|
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
? `,循环 ${resume.loopResume.loopId} 恢复到第 ${resume.loopResume.iteration + 1} 轮迭代`
|
|
540
|
-
: '';
|
|
541
|
-
const msg = `♻ 检测到同名未完成任务,从第 ${startFrom + 1} 步续跑(已回放 ${resume.completedStepIds?.length ?? 0} 个完成步变量${loopNote})。要全新开始请先 /new。`;
|
|
542
|
-
yield { type: 'warning', message: msg };
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// P1 TUI 可见:开工时给 LiveArea 状态行一条 plugin_progress(startFrom/total)。
|
|
546
|
-
yield {
|
|
547
|
-
type: 'plugin_progress',
|
|
548
|
-
pluginId: WF_PLUGIN_ID,
|
|
549
|
-
current: startFrom,
|
|
550
|
-
max: def.steps.length,
|
|
551
|
-
message: `▶ workflow ${def.name}`,
|
|
552
|
-
};
|
|
374
|
+
},
|
|
375
|
+
});
|
|
553
376
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
def.steps,
|
|
558
|
-
vars,
|
|
559
|
-
runCtx,
|
|
560
|
-
state,
|
|
561
|
-
{ name: (id) => id, descendIntoLoop: true },
|
|
562
|
-
startFrom,
|
|
563
|
-
resume.mode === 'resume' ? resume.loopResume : undefined,
|
|
377
|
+
if (resume.supplementaryGoal) {
|
|
378
|
+
await ((workflowContext as any).state.addGoalSupplement as (t: string) => Promise<void>)(
|
|
379
|
+
resume.supplementaryGoal,
|
|
564
380
|
);
|
|
565
|
-
|
|
566
|
-
// 'completed' / 信号对象(顶层游离 break/continue)→ 正常完成。
|
|
567
|
-
if (flow === 'halted') return;
|
|
381
|
+
}
|
|
568
382
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
383
|
+
const startEvent = {
|
|
384
|
+
type: 'workflow_progress',
|
|
385
|
+
workflow: script.name,
|
|
386
|
+
runId: opts.runId,
|
|
387
|
+
rootRunId: opts.scope.rootRunId,
|
|
388
|
+
...(opts.parentRunId ? { parentRunId: opts.parentRunId } : {}),
|
|
389
|
+
depth: opts.depth,
|
|
390
|
+
phase: 'workflow',
|
|
391
|
+
status: 'start',
|
|
392
|
+
message: `开始 ${script.name}`,
|
|
393
|
+
} as const;
|
|
394
|
+
await state.appendEvent(startEvent);
|
|
395
|
+
yield startEvent;
|
|
396
|
+
|
|
397
|
+
let failure: unknown = null;
|
|
398
|
+
let scriptResult: unknown;
|
|
399
|
+
const done = runScript(script, workflowContext, scriptHash)
|
|
400
|
+
.then(async (result) => {
|
|
401
|
+
ctx.signal?.throwIfAborted();
|
|
402
|
+
scriptResult = await state.writeResult(result);
|
|
403
|
+
})
|
|
404
|
+
.catch((e) => {
|
|
405
|
+
failure = e;
|
|
406
|
+
})
|
|
407
|
+
.finally(() => queue.close());
|
|
408
|
+
|
|
409
|
+
for await (const ev of queue.drain()) yield ev;
|
|
410
|
+
await done;
|
|
411
|
+
|
|
412
|
+
if (failure) {
|
|
413
|
+
// 中断(用户 ESC)与脚本出错语义完全不同:前者保留现场可 resume。
|
|
414
|
+
if (
|
|
415
|
+
ctx.signal?.aborted ||
|
|
416
|
+
(failure instanceof Error && failure.name === 'AbortError')
|
|
417
|
+
) {
|
|
418
|
+
await state.recordFailure('用户中断', [...(ctx.declaredArtifacts ?? [])]);
|
|
419
|
+
await state.appendProgress('■ 用户中断');
|
|
420
|
+
if (opts.parentStateDir)
|
|
421
|
+
throw Object.assign(new Error('用户中断'), { name: 'AbortError' });
|
|
422
|
+
const interruptedEvent = {
|
|
423
|
+
type: 'workflow_progress',
|
|
424
|
+
workflow: script.name,
|
|
425
|
+
runId: opts.runId,
|
|
426
|
+
rootRunId: opts.scope.rootRunId,
|
|
427
|
+
depth: opts.depth,
|
|
428
|
+
phase: 'workflow',
|
|
429
|
+
status: 'warning',
|
|
430
|
+
message: '用户中断,现场已保留',
|
|
431
|
+
} as const;
|
|
432
|
+
await state.appendEvent(interruptedEvent);
|
|
433
|
+
yield interruptedEvent;
|
|
434
|
+
await ctx.managedBridge?.finish({
|
|
435
|
+
status: 'stopped',
|
|
436
|
+
artifacts: [...(ctx.declaredArtifacts ?? [])],
|
|
437
|
+
blocker: '用户停止,现场已保留',
|
|
438
|
+
});
|
|
439
|
+
yield { type: 'interrupted' };
|
|
579
440
|
return;
|
|
580
441
|
}
|
|
442
|
+
const message =
|
|
443
|
+
failure instanceof WorkflowScriptError
|
|
444
|
+
? failure.message
|
|
445
|
+
: `工作流「${script.name}」执行失败:${
|
|
446
|
+
failure instanceof Error ? failure.message : String(failure)
|
|
447
|
+
}`;
|
|
448
|
+
// 工作流可以在 Error 上附带通用的 workflowOutcome。Runner 只透传这个
|
|
449
|
+
// 生命周期标签,不解释其业务含义。
|
|
450
|
+
const workflowOutcome = workflowOutcomeFromFailure(failure);
|
|
451
|
+
await state.recordFailure(message, [...(ctx.declaredArtifacts ?? [])]);
|
|
452
|
+
await state.appendProgress(`■ 失败:${message}`);
|
|
453
|
+
if (opts.parentStateDir) throw failure;
|
|
454
|
+
const failureEvent = {
|
|
455
|
+
type: 'workflow_progress',
|
|
456
|
+
workflow: script.name,
|
|
457
|
+
runId: opts.runId,
|
|
458
|
+
rootRunId: opts.scope.rootRunId,
|
|
459
|
+
...(opts.parentRunId ? { parentRunId: opts.parentRunId } : {}),
|
|
460
|
+
depth: opts.depth,
|
|
461
|
+
phase: 'workflow',
|
|
462
|
+
status: 'warning',
|
|
463
|
+
message,
|
|
464
|
+
} as const;
|
|
465
|
+
await state.appendEvent(failureEvent);
|
|
466
|
+
yield failureEvent;
|
|
467
|
+
await ctx.managedBridge?.finish({
|
|
468
|
+
status: 'failed',
|
|
469
|
+
artifacts: [...(ctx.declaredArtifacts ?? [])],
|
|
470
|
+
blocker: message,
|
|
471
|
+
...(workflowOutcome ? { outcome: workflowOutcome } : {}),
|
|
472
|
+
});
|
|
473
|
+
yield { type: 'error', error: message };
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
581
476
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
477
|
+
// 把运行时经 declareArtifact() 登记的产物一并落盘 —— 只写 meta.artifacts
|
|
478
|
+
// 的话,循环体里的逐迭代产物会静默丢失(见 markDone 的注释)。
|
|
479
|
+
await state.markDone([...(ctx.declaredArtifacts ?? [])]);
|
|
480
|
+
await state.appendProgress(
|
|
481
|
+
`✓ 完成(重放 ${journal.replayedCount} 次 / 上次账本 ${journal.loadedCount} 条)`,
|
|
482
|
+
);
|
|
483
|
+
const completeEvent = {
|
|
484
|
+
type: 'workflow_progress',
|
|
485
|
+
workflow: script.name,
|
|
486
|
+
runId: opts.runId,
|
|
487
|
+
rootRunId: opts.scope.rootRunId,
|
|
488
|
+
...(opts.parentRunId ? { parentRunId: opts.parentRunId } : {}),
|
|
489
|
+
depth: opts.depth,
|
|
490
|
+
phase: 'workflow',
|
|
491
|
+
status: 'complete',
|
|
492
|
+
message: '执行完成',
|
|
493
|
+
} as const;
|
|
494
|
+
await state.appendEvent(completeEvent);
|
|
495
|
+
yield completeEvent;
|
|
496
|
+
await state.archive();
|
|
497
|
+
const artifacts = (await state.allArtifacts()).map((p) =>
|
|
498
|
+
resolve(state.workingDir, p),
|
|
499
|
+
);
|
|
500
|
+
if (opts.parentStateDir)
|
|
501
|
+
return {
|
|
502
|
+
workflow: script.name,
|
|
503
|
+
runId: opts.runId,
|
|
504
|
+
result: scriptResult,
|
|
505
|
+
artifacts,
|
|
506
|
+
reused: false,
|
|
590
507
|
};
|
|
508
|
+
const result =
|
|
509
|
+
scriptResult && typeof scriptResult === 'object'
|
|
510
|
+
? (scriptResult as { evidence?: unknown })
|
|
511
|
+
: undefined;
|
|
512
|
+
const evidence = Array.isArray(result?.evidence)
|
|
513
|
+
? result.evidence.map((item) => String(item))
|
|
514
|
+
: [];
|
|
515
|
+
const finished = await ctx.managedBridge?.finish({
|
|
516
|
+
status: 'verified',
|
|
517
|
+
artifacts,
|
|
518
|
+
evidence,
|
|
519
|
+
});
|
|
520
|
+
// ⚠️ 收尾状态必须过进程边界。传进去 'verified' 拿回来的很可能是
|
|
521
|
+
// 'ready_for_acceptance' —— 机器自检通过但还没人验收,这两者对宿主是天差地别。
|
|
522
|
+
// 就地丢弃会让 `-p --output-format json` 的宿主把「等待验收」读成「办完了」。
|
|
523
|
+
yield { type: 'turn_done', ...managedStatusOf(finished) };
|
|
524
|
+
return {
|
|
525
|
+
workflow: script.name,
|
|
526
|
+
runId: opts.runId,
|
|
527
|
+
result: scriptResult,
|
|
528
|
+
artifacts,
|
|
529
|
+
reused: false,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
591
532
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
533
|
+
/**
|
|
534
|
+
* Read the structured outcome after the ESM loader has wrapped a workflow
|
|
535
|
+
* exception. The wrapper is deliberately treated as an implementation detail:
|
|
536
|
+
* both its own field and a bounded `cause` chain are accepted so the
|
|
537
|
+
* workflow → loader → runner path cannot silently downgrade budget_limited to
|
|
538
|
+
* a generic failed result.
|
|
539
|
+
*/
|
|
540
|
+
export function workflowOutcomeFromFailure(
|
|
541
|
+
failure: unknown,
|
|
542
|
+
): string | undefined {
|
|
543
|
+
let current = failure;
|
|
544
|
+
for (
|
|
545
|
+
let depth = 0;
|
|
546
|
+
depth < 8 && current && typeof current === 'object';
|
|
547
|
+
depth++
|
|
548
|
+
) {
|
|
549
|
+
const outcome = (current as { workflowOutcome?: unknown }).workflowOutcome;
|
|
550
|
+
if (typeof outcome === 'string' && outcome.trim()) return outcome.trim();
|
|
551
|
+
current = (current as { cause?: unknown }).cause;
|
|
603
552
|
}
|
|
553
|
+
return undefined;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/** 从 bridge.finish 的返回值里取生命周期状态;拿不到就不带这个字段。 */
|
|
557
|
+
function managedStatusOf(finished: unknown): { managedStatus?: string } {
|
|
558
|
+
const status = (finished as { status?: unknown } | undefined)?.status;
|
|
559
|
+
return typeof status === 'string' ? { managedStatus: status } : {};
|
|
604
560
|
}
|