minimal-agent 0.13.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 +60 -4
- 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 +197 -206
- package/plugins/workflow-runner/src/index.ts +248 -249
- 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 +400 -489
- package/plugins/workflow-runner/src/runner.ts +488 -560
- 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 +151 -307
- package/plugins/workflow-runner/src/taskIdentity.ts +207 -355
- 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 -211
- package/plugins/workflow-runner/src/types.ts +154 -623
- 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 +474 -802
- package/plugins/workflow-runner/src/workflowState.ts +575 -970
- 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/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 +27 -6
- package/src/cli/print.ts +48 -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 +419 -74
- package/src/config/configService.ts +703 -145
- 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 +34 -5
- package/src/infra/verification.ts +39 -4
- 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 +139 -7
- package/src/plugins/pluginRunner.ts +173 -9
- package/src/plugins/slashFallback.js +113 -0
- package/src/plugins/slashFallback.ts +137 -0
- package/src/prompts/policyRegistry.js +12 -32
- package/src/prompts/policyRegistry.ts +15 -32
- 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 +90 -46
- package/src/tools/advisor/advisor.ts +100 -47
- 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 +22 -15
- package/src/tools/bash/bash.ts +23 -17
- 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/presentresult/presentresult.js +6 -15
- package/src/tools/presentresult/presentresult.ts +6 -15
- package/src/tools/read/read.js +62 -9
- package/src/tools/read/read.ts +63 -9
- package/src/tools/shared/fileUtils.js +1 -2
- package/src/tools/shared/fileUtils.ts +1 -2
- 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 -34
- package/src/tools/webbrowser/browserControl.ts +210 -56
- 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 +679 -144
- package/src/tools/webbrowser/webbrowser.ts +975 -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 +440 -38
- 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/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 -639
- package/plugins/workflow-runner/src/expressions.ts +0 -640
- 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 -126
- package/plugins/workflow-runner/src/recovery.ts +0 -156
- 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 -42
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -52
- 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 -227
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -274
- package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -61
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -72
- 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 -167
- 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/e2eNewLayout.test.ts +0 -314
- package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -234
- package/plugins/workflow-runner/test/expressions.test.ts +0 -439
- 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 -106
- 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 -221
- package/plugins/workflow-runner/test/recovery.test.ts +0 -265
- package/plugins/workflow-runner/test/research.load.test.ts +0 -328
- package/plugins/workflow-runner/test/runner.test.ts +0 -911
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -436
- package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -198
- package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
- package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -354
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -324
- 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 -201
- package/workflows/research.yaml +0 -873
- 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
- /package/workflows/{research.md → research/DESIGN.md} +0 -0
|
@@ -1,632 +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 {
|
|
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';
|
|
31
27
|
import { effectiveWorkflowCwd } from './toolRuntime.js';
|
|
32
|
-
import {
|
|
33
|
-
import { execAgentStep } from './stepExecutors/agent.js';
|
|
34
|
-
import { execAssertStep } from './stepExecutors/assert.js';
|
|
35
|
-
import { execVerifyStep } from './stepExecutors/verify.js';
|
|
36
|
-
import { execLlmStep } from './stepExecutors/llm.js';
|
|
37
|
-
import { execSkillStep } from './stepExecutors/skill.js';
|
|
38
|
-
import { execToolStep } from './stepExecutors/tool.js';
|
|
39
|
-
import { execEchoStep } from './stepExecutors/echo.js';
|
|
40
|
-
import { execSetStep } from './stepExecutors/set.js';
|
|
41
|
-
import { execBranchStep } from './controlFlow/branch.js';
|
|
42
|
-
import { execLoopStep } from './controlFlow/loop.js';
|
|
43
|
-
import { execPauseStep } from './controlFlow/pause.js';
|
|
44
|
-
import { execSignalStep } from './controlFlow/signal.js';
|
|
45
|
-
import type { ControlDeps, ControlOutcome } from './controlFlow/types.js';
|
|
46
|
-
import { runStepWithRetry, type CpScope } from './stepPipeline.js';
|
|
47
|
-
import type {
|
|
48
|
-
RunContext,
|
|
49
|
-
StepDef,
|
|
50
|
-
StepKind,
|
|
51
|
-
StepResult,
|
|
52
|
-
WorkflowDef,
|
|
53
|
-
WorkflowEvent,
|
|
54
|
-
} from './types.js';
|
|
55
|
-
import { normalizeCapture, VarStack } from './types.js';
|
|
28
|
+
import type { RunContext, WorkflowScript } from './types.js';
|
|
56
29
|
import {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
} from './workflowState.js';
|
|
30
|
+
type WorkflowBundle,
|
|
31
|
+
resolveWorkflowBundle,
|
|
32
|
+
} from './workflowDependencies.js';
|
|
33
|
+
import { WorkflowState } from './workflowState.js';
|
|
61
34
|
|
|
62
|
-
/**
|
|
63
|
-
* 插件 ID:把私有 workflow_* 事件镜像成框架通用 plugin_progress 时挂这个 id。
|
|
64
|
-
* LiveArea 状态行会渲染成 "[plugin:workflow-runner] i/total <message>"
|
|
65
|
-
* (src/ui/LiveArea.tsx:123),让 TUI 里跑 workflow 不再"无声"。
|
|
66
|
-
* (-p 非交互模式另由 src/cli/print.ts 把通用 warning 事件落 stderr。)
|
|
67
|
-
*/
|
|
68
35
|
const WF_PLUGIN_ID = 'workflow-runner';
|
|
69
36
|
|
|
70
|
-
/**
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
* 1. 优先取名为 "goal" 的输入(plan.yaml / ralph-loop.yaml 等通用约定)
|
|
76
|
-
* 2. 其次取第一个 required: true 的 string 类型输入
|
|
77
|
-
* 3. 都没有 → 返回空字符串(executor 自动跳过)
|
|
78
|
-
*
|
|
79
|
-
* 锚定字符串格式紧凑(~60-150 字符),注入每步 prompt 前置,
|
|
80
|
-
* 确保 LLM 在任何步骤都能"看到"用户原始目标,避免长上下文稀释。
|
|
81
|
-
*/
|
|
82
|
-
function buildGoalAnchor(
|
|
83
|
-
inputDefs: { name: string; type: string; required?: boolean }[],
|
|
84
|
-
inputValues: Record<string, unknown>,
|
|
85
|
-
): string {
|
|
86
|
-
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;
|
|
87
42
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (typeof v === 'string' && v.trim()) {
|
|
93
|
-
goalValue = v.trim();
|
|
94
|
-
}
|
|
43
|
+
push(item: T): void {
|
|
44
|
+
this.items.push(item);
|
|
45
|
+
this.waiter?.();
|
|
46
|
+
this.waiter = null;
|
|
95
47
|
}
|
|
96
48
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const v = inputValues[firstRequired.name];
|
|
102
|
-
if (typeof v === 'string' && v.trim()) {
|
|
103
|
-
goalValue = v.trim();
|
|
104
|
-
}
|
|
105
|
-
}
|
|
49
|
+
close(): void {
|
|
50
|
+
this.closed = true;
|
|
51
|
+
this.waiter?.();
|
|
52
|
+
this.waiter = null;
|
|
106
53
|
}
|
|
107
54
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** 可中断的退避等待:用户 abort 时立即返回(让后续步骤检查 signal 收口,不在这里抛)。 */
|
|
119
|
-
function sleepInterruptible(ms: number, signal?: AbortSignal): Promise<void> {
|
|
120
|
-
return new Promise((resolve) => {
|
|
121
|
-
if (signal?.aborted) {
|
|
122
|
-
resolve();
|
|
123
|
-
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
|
+
});
|
|
124
64
|
}
|
|
125
|
-
|
|
126
|
-
signal?.removeEventListener('abort', onAbort);
|
|
127
|
-
resolve();
|
|
128
|
-
}, ms);
|
|
129
|
-
const onAbort = (): void => {
|
|
130
|
-
clearTimeout(timer);
|
|
131
|
-
resolve();
|
|
132
|
-
};
|
|
133
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
134
|
-
});
|
|
65
|
+
}
|
|
135
66
|
}
|
|
136
67
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
* - FlowSignal : break / continue 循环信号(工作流不算失败)。
|
|
142
|
-
* target 缺省 = 最近一层循环消费;指定 target 时信号穿透中间层循环上抛,
|
|
143
|
-
* 直到 id 匹配的 loop 消费(break=停止迭代 / continue=跳到下一轮)。
|
|
144
|
-
* 目标不存在时冒泡到顶层按正常完成处理(与游离 break 同语义)。
|
|
145
|
-
* 关键差异:break/continue 只影响循环迭代,循环之后的兄弟步应继续;halt 才真正向上中止。
|
|
146
|
-
*/
|
|
147
|
-
interface FlowSignal {
|
|
148
|
-
signal: 'break' | 'continue';
|
|
149
|
-
target?: string;
|
|
68
|
+
export interface RunWorkflowOptions {
|
|
69
|
+
ctx: RunContext;
|
|
70
|
+
inputs: Record<string, unknown>;
|
|
71
|
+
sessionId?: string;
|
|
150
72
|
}
|
|
151
|
-
type StepFlow = 'completed' | 'halted' | FlowSignal;
|
|
152
73
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
text: string,
|
|
159
|
-
): Promise<void> {
|
|
160
|
-
const t = text.trim();
|
|
161
|
-
if (!t) return;
|
|
162
|
-
// 锚定必须紧凑(注入每步 prompt),逐条截断;完整原文在 capture 变量 / history 里
|
|
163
|
-
const compact = t.length > 240 ? `${t.slice(0, 240)}…` : t;
|
|
164
|
-
ctx.goalSupplements ??= [];
|
|
165
|
-
ctx.goalSupplements.push(compact);
|
|
166
|
-
await state.appendSupplement(compact);
|
|
74
|
+
export interface WorkflowCallResult {
|
|
75
|
+
workflow: string;
|
|
76
|
+
runId: string;
|
|
77
|
+
result: unknown;
|
|
78
|
+
artifacts: string[];
|
|
167
79
|
}
|
|
168
80
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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;
|
|
177
89
|
}
|
|
178
90
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
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,
|
|
195
106
|
};
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
): AsyncGenerator<LoopEvent, StepResult, void> {
|
|
209
|
-
if (step.type === 'assert') return execAssertStep(step, vars, ctx);
|
|
210
|
-
if (step.type === 'verify') return await execVerifyStep(step, vars, ctx);
|
|
211
|
-
// set / echo 从 runSteps 的内联块搬进执行器体系:它们不影响迭代、只产出 StepResult。
|
|
212
|
-
// 搬过来的直接收益是 `capture:` 自然生效(此前它们从不调 bindCapture —— 静默失效)。
|
|
213
|
-
if (step.type === 'set') return yield* execSetStep(step, vars);
|
|
214
|
-
if (step.type === 'echo') return yield* execEchoStep(step, vars);
|
|
215
|
-
if (step.tool) return yield* execToolStep(step, vars, ctx);
|
|
216
|
-
if (step.agent) return yield* execAgentStep(step, vars, ctx);
|
|
217
|
-
if (step.llm) return yield* execLlmStep(step, vars, ctx);
|
|
218
|
-
if (step.skill) return yield* execSkillStep(step, vars, ctx);
|
|
219
|
-
throw new Error(`step ${step.id}: 无可执行字段(tool/agent/llm/skill/assert/verify)`);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* 递归执行 steps[]。返回 StepFlow(completed / halted / break|continue 信号,见上方定义)。
|
|
224
|
-
*
|
|
225
|
-
* yields 混合事件:WorkflowEvent + LoopEvent(skill 子循环转发 / error / interrupted)。
|
|
226
|
-
* index/total 字段相对**当前 block**,嵌套 block 自有计数(branch.then 内 3 步就报 total=3)。
|
|
227
|
-
*/
|
|
228
|
-
async function* runSteps(
|
|
229
|
-
steps: StepDef[],
|
|
230
|
-
vars: VarStack,
|
|
231
|
-
ctx: RunContext,
|
|
232
|
-
state: WorkflowState,
|
|
233
|
-
// checkpoint 记录范围:顶层调用(runWorkflow → def.steps)传 { name: id=>id, descendIntoLoop:true };
|
|
234
|
-
// 顶层 loop 的 do[] 由 loop 处理器派生复合名 scope;更深嵌套传 undefined(不记录)。
|
|
235
|
-
cp?: CpScope,
|
|
236
|
-
// resume:本列表中索引 < startFrom 的步已在上次运行完成,本次跳过(变量已由 runWorkflow 回放)。
|
|
237
|
-
// 顶层与「loop resume 进迭代中间」(loop 处理器对 do[] 传 innerStartFrom)都用它。
|
|
238
|
-
startFrom = 0,
|
|
239
|
-
// 仅顶层传:进行中循环的恢复信息(迭代号 + 迭代内起点),由匹配 id 的 loop 消费一次。
|
|
240
|
-
loopResume?: LoopResumeInfo,
|
|
241
|
-
): AsyncGenerator<WorkflowEvent | LoopEvent, StepFlow, void> {
|
|
242
|
-
for (let i = 0; i < steps.length; i++) {
|
|
243
|
-
const step = steps[i];
|
|
244
|
-
if (ctx.signal?.aborted) {
|
|
245
|
-
yield { type: 'interrupted' };
|
|
246
|
-
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();
|
|
247
119
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (
|
|
122
|
+
opts.ctx.signal?.aborted ||
|
|
123
|
+
(error instanceof Error && error.name === 'AbortError')
|
|
124
|
+
)
|
|
125
|
+
yield { type: 'interrupted' };
|
|
126
|
+
else
|
|
251
127
|
yield {
|
|
252
|
-
type: '
|
|
253
|
-
|
|
254
|
-
reason: 'resume: 上次已完成',
|
|
128
|
+
type: 'error',
|
|
129
|
+
error: error instanceof Error ? error.message : String(error),
|
|
255
130
|
};
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// when 条件:**所有** step 类型统一在这里判。控制流处理器里不要再判一次 ——
|
|
260
|
-
// 重构前 break/continue 块自带一份,而它恒为真(false 时这里已经 continue 掉了),
|
|
261
|
-
// 连它的 catch 分支都不可达。那是纯死代码,随本次重构删除。
|
|
262
|
-
if (step.when) {
|
|
263
|
-
let pass = false;
|
|
264
|
-
try {
|
|
265
|
-
pass = Boolean(evalExpr(step.when, vars));
|
|
266
|
-
} catch (e) {
|
|
267
|
-
const errMsg = `when 表达式错误: ${(e as Error).message}`;
|
|
268
|
-
yield { type: 'workflow_step_end', id: step.id, ok: false, error: errMsg };
|
|
269
|
-
yield {
|
|
270
|
-
type: 'error',
|
|
271
|
-
error: `Workflow halted at step "${step.id}": ${errMsg}`,
|
|
272
|
-
};
|
|
273
|
-
return 'halted';
|
|
274
|
-
}
|
|
275
|
-
if (!pass) {
|
|
276
|
-
yield { type: 'workflow_step_skipped', id: step.id, reason: 'when=false' };
|
|
277
|
-
await state.appendProgress(`- ${step.id} skipped (when=false)`);
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const kind = stepKind(step);
|
|
283
|
-
dlog('wf', `step ${i + 1}/${steps.length} START ${step.id} (${kind})`);
|
|
284
|
-
// 已插值的人话进度名随 start 事件带给 UI:scrollback 进度行显示它而非块内相对
|
|
285
|
-
// 序号,与状态行(下方 plugin_progress)保持一致。未声明 title 的步省略此字段。
|
|
286
|
-
const interpolatedTitle = step.title ? interpolate(step.title, vars) : undefined;
|
|
287
|
-
yield {
|
|
288
|
-
type: 'workflow_step_start',
|
|
289
|
-
id: step.id,
|
|
290
|
-
kind,
|
|
291
|
-
index: i,
|
|
292
|
-
total: steps.length,
|
|
293
|
-
title: interpolatedTitle,
|
|
294
|
-
};
|
|
295
|
-
// P1 TUI 可见:把私有 workflow_step_start 镜像成框架通用 plugin_progress。
|
|
296
|
-
// 有 title 的步显示人话阶段名(如「阶段2/6·起草实现计划」)且不带块内序号——
|
|
297
|
-
// 嵌套块的 i/total(顶层 5、循环体 12…)会被用户误读成"跳步/结构乱";
|
|
298
|
-
// 未声明 title 的步保持 legacy 格式 "i/total step <id> (<kind>)"。
|
|
299
|
-
yield {
|
|
300
|
-
type: 'plugin_progress',
|
|
301
|
-
pluginId: WF_PLUGIN_ID,
|
|
302
|
-
current: i + 1,
|
|
303
|
-
max: steps.length,
|
|
304
|
-
message: interpolatedTitle ?? `step ${step.id} (${kind})`,
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
// ---------- 控制流:改变的是「循环怎么走」,不是产出一个 StepResult ----------
|
|
308
|
-
//
|
|
309
|
-
// 所以它们不塞进 stepExecutors 那套 (step,vars,ctx)→StepResult 的形状里,
|
|
310
|
-
// 各自住在 controlFlow/ 并共用出口 ControlOutcome —— 这里只需一个分派。
|
|
311
|
-
const controlDeps: ControlDeps = {
|
|
312
|
-
ctx,
|
|
313
|
-
state,
|
|
314
|
-
...(cp ? { cp } : {}),
|
|
315
|
-
runBlock: (blockSteps, blockVars, blockCp, blockStart, blockResume) =>
|
|
316
|
-
runSteps(blockSteps, blockVars, ctx, state, blockCp, blockStart, blockResume),
|
|
317
|
-
index: i,
|
|
318
|
-
total: steps.length,
|
|
319
|
-
addGoalSupplement: (text: string) => addGoalSupplement(ctx, state, text),
|
|
320
|
-
};
|
|
321
|
-
let outcome: ControlOutcome | null = null;
|
|
322
|
-
if (step.type === 'branch') {
|
|
323
|
-
outcome = yield* execBranchStep(step, vars, controlDeps);
|
|
324
|
-
} else if (step.type === 'loop') {
|
|
325
|
-
outcome = yield* execLoopStep(step, vars, controlDeps, loopResume);
|
|
326
|
-
// 恢复信息只消费一次:匹配到的那个 loop 用掉之后,后续 loop 不再套用。
|
|
327
|
-
if (loopResume && loopResume.loopId === step.id) loopResume = undefined;
|
|
328
|
-
} else if (step.type === 'pause') {
|
|
329
|
-
outcome = yield* execPauseStep(step, vars, controlDeps);
|
|
330
|
-
} else if (step.type === 'break' || step.type === 'continue') {
|
|
331
|
-
outcome = yield* execSignalStep(step, controlDeps);
|
|
332
|
-
}
|
|
333
|
-
if (outcome) {
|
|
334
|
-
if (outcome.kind === 'halted') return 'halted';
|
|
335
|
-
if (outcome.kind === 'flow') return outcome.signal;
|
|
336
|
-
continue;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// ---------- 动作步:tool / llm / skill / agent / assert / verify / set / echo ----------
|
|
340
|
-
//
|
|
341
|
-
// 统一走 runStepWithRetry:in-run 重试 → onError 三态 → commitStep 提交。
|
|
342
|
-
// 「提交一步」只有 stepPipeline 那一份实现,这里不再自己写(见 stepPipeline 头注)。
|
|
343
|
-
const actionOutcome = yield* runStepWithRetry({
|
|
344
|
-
step,
|
|
345
|
-
kind,
|
|
346
|
-
vars,
|
|
347
|
-
ctx,
|
|
348
|
-
state,
|
|
349
|
-
...(cp ? { cp } : {}),
|
|
350
|
-
index: i,
|
|
351
|
-
total: steps.length,
|
|
352
|
-
addGoalSupplement: (text: string) => addGoalSupplement(ctx, state, text),
|
|
353
|
-
execute: () => dispatchActionStep(step, vars, ctx),
|
|
354
|
-
});
|
|
355
|
-
if (actionOutcome === 'halted') return 'halted';
|
|
356
131
|
}
|
|
357
|
-
return 'completed';
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* 聚合工作流【声明】的产物路径(相对 cwd):def.artifacts + 所有 step.artifacts(含嵌套 then/else/do)。
|
|
362
|
-
* 通用:由 yaml 的 `artifacts:` 声明驱动,runner 不硬编码任何具体文件名(如 PLAN.md)。
|
|
363
|
-
* 用途:new-task 检测时清旧任务残留产物、completed 时报实际产物。
|
|
364
|
-
*/
|
|
365
|
-
function collectDeclaredArtifacts(def: WorkflowDef): string[] {
|
|
366
|
-
const out = new Set<string>();
|
|
367
|
-
for (const a of def.artifacts ?? []) out.add(a);
|
|
368
|
-
const walk = (steps: StepDef[] | undefined): void => {
|
|
369
|
-
for (const s of steps ?? []) {
|
|
370
|
-
for (const a of s.artifacts ?? []) out.add(a);
|
|
371
|
-
walk(s.then);
|
|
372
|
-
walk(s.else);
|
|
373
|
-
walk(s.do);
|
|
374
|
-
}
|
|
375
|
-
};
|
|
376
|
-
walk(def.steps);
|
|
377
|
-
return [...out];
|
|
378
132
|
}
|
|
379
133
|
|
|
380
|
-
/**
|
|
381
|
-
*
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
// -p 的 --session);缺失或为缺省 'default' 时,由工作流引擎自动命名一个唯一
|
|
405
|
-
// session(generateDefaultSessionId,见 taskIdentity.ts)——保证每次工作流运行都
|
|
406
|
-
// 有独立目录,多 session 并行互不覆盖;宿主显式指定时仍可复用/续跑该 session。
|
|
407
|
-
// 注入全局变量 `session`(sanitize 后的目录安全名),供 yaml 产物路径用
|
|
408
|
-
// ${session} 占位符(如 out/research/${session}/01-goal.md)。
|
|
409
|
-
const explicitSessionId = ctx.runtimeContext?.sessionId;
|
|
410
|
-
const sessionId =
|
|
411
|
-
explicitSessionId && explicitSessionId !== DEFAULT_SESSION_ID
|
|
412
|
-
? explicitSessionId
|
|
413
|
-
: generateDefaultSessionId();
|
|
414
|
-
vars.setGlobal('session', sanitizeWorkflowDirName(sessionId));
|
|
415
|
-
|
|
416
|
-
// onError:'recover' 用:把工作流总目标(description)透传给恢复 agent,
|
|
417
|
-
// 让它知道「这条工作流到底想干什么」。已有 goal 则不覆盖。
|
|
418
|
-
const runCtx: RunContext = { ...ctx, goal: ctx.goal ?? def.description };
|
|
419
|
-
|
|
420
|
-
// 通用目标锚定:从 inputs 中提取用户的原始目标,
|
|
421
|
-
// 注入 RunContext.goalAnchor,让 agent/llm/skill 步自动前置到每步 prompt。
|
|
422
|
-
// 提取策略:优先 "goal" 命名的输入 → 其次第一个 required string 输入。
|
|
423
|
-
runCtx.goalAnchor = buildGoalAnchor(def.inputs ?? [], inputs);
|
|
424
|
-
|
|
425
|
-
// self_heal:把全局 max_retries 默认透传给每步的 in-run retry(step.max_retries 可覆盖)。
|
|
426
|
-
runCtx.selfHealMaxRetries = def.self_heal?.max_retries;
|
|
427
|
-
runCtx.retryDelayMs = def.self_heal?.retry_delay_ms;
|
|
428
|
-
runCtx.maxTurnsRetryDelayMs = def.self_heal?.max_turns_retry_delay_ms;
|
|
429
|
-
|
|
430
|
-
// 声明产物清单:state.init 用于 new-task 清理/completed 报产物;
|
|
431
|
-
// 挂到 runCtx 供 tool 步覆盖判定(声明产物自动 pre-Read 解 Write 守卫)。
|
|
432
|
-
// Phase 3:产物路径可能含 ${session} 占位符,收集后按 vars 展开为实际路径
|
|
433
|
-
//(new-task 清理 / completed 报告都以展开后的真实路径为准)。
|
|
434
|
-
const declaredArtifacts = collectDeclaredArtifacts(def)
|
|
435
|
-
.map((a) => interpolate(a, vars))
|
|
436
|
-
.filter((a) => a.length > 0);
|
|
437
|
-
runCtx.declaredArtifacts = declaredArtifacts;
|
|
438
|
-
|
|
439
|
-
// 任务身份语义仲裁器(hash 不等 + 旧任务未完成时的二审,见 taskIdentity.ts):
|
|
440
|
-
// 短语快路径 → LLM 单轮仲裁 → AskUserQuestion → 兜底 new-task。
|
|
441
|
-
// 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
|
+
);
|
|
442
158
|
const arbitrationNotes: string[] = [];
|
|
443
|
-
const state = new WorkflowState(getWorkingDir(), def.name, sessionId);
|
|
444
159
|
const resume = await state.init(
|
|
445
|
-
def.name,
|
|
446
160
|
inputs,
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
+
}),
|
|
457
172
|
);
|
|
458
173
|
|
|
459
|
-
|
|
460
|
-
type: '
|
|
461
|
-
name: def.name,
|
|
462
|
-
totalSteps: def.steps.length,
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
// 仲裁过程可见化(命中延续短语 / LLM 判定 / 用户确认 / 兜底)
|
|
466
|
-
for (const n of arbitrationNotes) {
|
|
467
|
-
yield { type: 'warning', message: n };
|
|
174
|
+
for (const note of arbitrationNotes) {
|
|
175
|
+
yield { type: 'warning', message: note };
|
|
468
176
|
}
|
|
469
177
|
|
|
470
|
-
//
|
|
471
|
-
// 成功运行会 archive() 把状态目录改名 .done,init() 回退读 .done 检测到 completed。
|
|
178
|
+
// 已完成:报实际产物,不重跑。
|
|
472
179
|
if (resume.mode === 'completed') {
|
|
473
|
-
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
|
+
: ' (本次运行没有声明产物)';
|
|
474
196
|
yield {
|
|
475
197
|
type: 'text',
|
|
476
|
-
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,
|
|
477
209
|
};
|
|
478
|
-
yield { type: 'workflow_done', name: def.name, vars: vars.snapshot() };
|
|
479
|
-
return;
|
|
480
210
|
}
|
|
481
211
|
|
|
482
|
-
|
|
483
|
-
// 必须明示——曾有用户在 yaml 重构后同 goal 重跑,看到「从第 2 步续跑」
|
|
484
|
-
// 困惑是 bug 还是残留;作废旧断点时不说原因同样会被当成 bug。
|
|
485
|
-
if (resume.defChanged) {
|
|
212
|
+
if (resume.scriptChanged) {
|
|
486
213
|
yield {
|
|
487
214
|
type: 'warning',
|
|
488
|
-
message:
|
|
489
|
-
'♻ 工作流定义已变更(步骤结构与旧断点不再匹配),旧执行现场已作废,本次从头开始执行。',
|
|
215
|
+
message: `⚠ 工作流「${script.name}」的脚本/依赖已改变或产物/返回值缺失 —— 旧断点作废,本次全新开始。`,
|
|
490
216
|
};
|
|
491
217
|
}
|
|
492
|
-
|
|
493
|
-
// fresh/new-task 启动清理可见化:全新任务开始前清理的残留产物(上次任务
|
|
494
|
-
// 的声明产物 + /new 后无主孤儿)。用户约定:全新 plan 一律从干净状态开始;
|
|
495
|
-
// 删除范围仅限本工作流声明的产物,同任务 resume 不清理。
|
|
496
|
-
// 清理一律先归档到 .trash 回收目录再删残留:仲裁误判 new-task 时成果可找回。
|
|
497
218
|
if (resume.archivedTo) {
|
|
498
219
|
yield {
|
|
499
220
|
type: 'warning',
|
|
500
|
-
message:
|
|
221
|
+
message: `旧产物已归档到 ${resume.archivedTo}(不是删除,需要时可取回)。`,
|
|
501
222
|
};
|
|
502
223
|
}
|
|
503
|
-
|
|
224
|
+
|
|
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);
|
|
239
|
+
|
|
240
|
+
if (resume.mode === 'resume') {
|
|
504
241
|
yield {
|
|
505
242
|
type: 'warning',
|
|
506
|
-
message:
|
|
243
|
+
message:
|
|
244
|
+
`↻ 续跑工作流「${script.name}」(上次已完成 ${journal.loadedCount} 次调用` +
|
|
245
|
+
`${resume.lastError ? `,上次失败于:${resume.lastError}` : ''})。`,
|
|
507
246
|
};
|
|
508
247
|
}
|
|
509
248
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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;
|
|
564
373
|
}
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
? `,循环 ${resume.loopResume.loopId} 恢复到第 ${resume.loopResume.iteration + 1} 轮迭代`
|
|
568
|
-
: '';
|
|
569
|
-
const msg = `♻ 检测到同名未完成任务,从第 ${startFrom + 1} 步续跑(已回放 ${resume.completedStepIds?.length ?? 0} 个完成步变量${loopNote})。要全新开始请先 /new。`;
|
|
570
|
-
yield { type: 'warning', message: msg };
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// P1 TUI 可见:开工时给 LiveArea 状态行一条 plugin_progress(startFrom/total)。
|
|
574
|
-
yield {
|
|
575
|
-
type: 'plugin_progress',
|
|
576
|
-
pluginId: WF_PLUGIN_ID,
|
|
577
|
-
current: startFrom,
|
|
578
|
-
max: def.steps.length,
|
|
579
|
-
message: `▶ workflow ${def.name}`,
|
|
580
|
-
};
|
|
374
|
+
},
|
|
375
|
+
});
|
|
581
376
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
def.steps,
|
|
586
|
-
vars,
|
|
587
|
-
runCtx,
|
|
588
|
-
state,
|
|
589
|
-
{ name: (id) => id, descendIntoLoop: true },
|
|
590
|
-
startFrom,
|
|
591
|
-
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,
|
|
592
380
|
);
|
|
593
|
-
|
|
594
|
-
// 'completed' / 信号对象(顶层游离 break/continue)→ 正常完成。
|
|
595
|
-
if (flow === 'halted') return;
|
|
381
|
+
}
|
|
596
382
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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' };
|
|
607
440
|
return;
|
|
608
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
|
+
}
|
|
609
476
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
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,
|
|
618
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
|
+
}
|
|
619
532
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
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;
|
|
631
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 } : {};
|
|
632
560
|
}
|