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,721 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ============================================================
|
|
3
|
-
* plugins/workflow-runner/src/loader.ts —— workflow yaml 双源加载 + 校验
|
|
4
|
-
* ------------------------------------------------------------
|
|
5
|
-
* - 扫描 cwd/workflows/ 与 packageRoot/workflows/(cwd 优先,复用
|
|
6
|
-
* getResourceSearchPaths 契约)
|
|
7
|
-
* - 一个坏 yaml 不应拖累其它工作流(catch 单文件错误,继续扫描)
|
|
8
|
-
* - 目录不存在 / 为空 → 静默返回 [](默认 T-A-O-R 不受影响)
|
|
9
|
-
*
|
|
10
|
-
* 不引 AJV:下面手写校验器(validate/validateSteps),覆盖结构性硬错误并对
|
|
11
|
-
* 每条 step 字段做类型/一致性校验——
|
|
12
|
-
* - name / description / steps 必填且类型正确;steps 非空
|
|
13
|
-
* - 每个 step 必须有且只有一个动作字段(tool / skill / llm / agent / type)
|
|
14
|
-
* - 控制流字段一致性 + ADR-05 的 llm-only 字段(output_schema/context_files/
|
|
15
|
-
* allowed_tools)准入 + 断点续跑字段(artifacts/max_retries)类型
|
|
16
|
-
* 手写而非 schema 库:依赖少、错误信息可中文化、按减法不为「完整 schema」引依赖。
|
|
17
|
-
* ============================================================
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { readFile, readdir } from 'node:fs/promises';
|
|
21
|
-
import { basename, join } from 'node:path';
|
|
22
|
-
|
|
23
|
-
import { parse as parseYamlLib } from 'yaml';
|
|
24
|
-
|
|
25
|
-
import { getResourceSearchPaths } from '../../../src/plugin-sdk.js';
|
|
26
|
-
import type { StepDef, WorkflowDef } from './types.js';
|
|
27
|
-
|
|
28
|
-
export class WorkflowLoadError extends Error {
|
|
29
|
-
constructor(message: string, public readonly file?: string) {
|
|
30
|
-
super(message);
|
|
31
|
-
this.name = 'WorkflowLoadError';
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 扫描双源目录,返回所有 *.yaml / *.yml 解析后的 workflow。
|
|
37
|
-
* 永远不抛;坏文件 console.warn 并跳过。
|
|
38
|
-
*/
|
|
39
|
-
export async function listWorkflows(workingDir?: string): Promise<WorkflowDef[]> {
|
|
40
|
-
const out: WorkflowDef[] = [];
|
|
41
|
-
const seen = new Set<string>();
|
|
42
|
-
const roots = getResourceSearchPaths('workflows', import.meta.url, workingDir);
|
|
43
|
-
|
|
44
|
-
for (const root of roots) {
|
|
45
|
-
let entries: string[];
|
|
46
|
-
try {
|
|
47
|
-
entries = await readdir(root);
|
|
48
|
-
} catch {
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
for (const f of entries) {
|
|
52
|
-
if (!f.endsWith('.yaml') && !f.endsWith('.yml')) continue;
|
|
53
|
-
const full = join(root, f);
|
|
54
|
-
try {
|
|
55
|
-
const def = await parseWorkflowFile(full);
|
|
56
|
-
if (!seen.has(def.name)) {
|
|
57
|
-
seen.add(def.name);
|
|
58
|
-
out.push(def);
|
|
59
|
-
}
|
|
60
|
-
} catch (e) {
|
|
61
|
-
// eslint-disable-next-line no-console
|
|
62
|
-
console.warn(`[workflows] 忽略损坏的 yaml ${full}: ${(e as Error).message}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return out;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** 按 name 查找。找不到返回 null —— 调用方决定如何提示用户。 */
|
|
70
|
-
export async function findWorkflowByName(
|
|
71
|
-
name: string,
|
|
72
|
-
workingDir?: string,
|
|
73
|
-
): Promise<WorkflowDef | null> {
|
|
74
|
-
const all = await listWorkflows(workingDir);
|
|
75
|
-
const normalized = normalizeWorkflowLookupName(name);
|
|
76
|
-
return all.find((w) => w.name === normalized || workflowFileBaseName(w) === normalized) ?? null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function stripYamlExt(name: string): string {
|
|
80
|
-
return name.replace(/\.ya?ml$/i, '');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function normalizeWorkflowLookupName(name: string): string {
|
|
84
|
-
return stripYamlExt(basename(name));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function workflowFileBaseName(workflow: WorkflowDef): string | null {
|
|
88
|
-
if (!workflow.__source) return null;
|
|
89
|
-
return stripYamlExt(basename(workflow.__source));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** 读单个 yaml 文件 → 解析 → 校验。失败抛 WorkflowLoadError。 */
|
|
93
|
-
export async function parseWorkflowFile(file: string): Promise<WorkflowDef> {
|
|
94
|
-
let raw: string;
|
|
95
|
-
try {
|
|
96
|
-
raw = await readFile(file, 'utf8');
|
|
97
|
-
} catch (e) {
|
|
98
|
-
throw new WorkflowLoadError(`无法读取 ${file}: ${(e as Error).message}`, file);
|
|
99
|
-
}
|
|
100
|
-
let data: unknown;
|
|
101
|
-
try {
|
|
102
|
-
data = parseYamlLib(raw);
|
|
103
|
-
} catch (e) {
|
|
104
|
-
throw new WorkflowLoadError(`yaml 语法错误: ${(e as Error).message}`, file);
|
|
105
|
-
}
|
|
106
|
-
if (!data || typeof data !== 'object') {
|
|
107
|
-
throw new WorkflowLoadError('yaml 根节点必须是对象', file);
|
|
108
|
-
}
|
|
109
|
-
validate(data as Record<string, unknown>, file);
|
|
110
|
-
const def = data as WorkflowDef;
|
|
111
|
-
def.__source = file;
|
|
112
|
-
return def;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ---------------- 手写最小校验 ----------------
|
|
116
|
-
|
|
117
|
-
function validate(obj: Record<string, unknown>, file: string): void {
|
|
118
|
-
if (typeof obj.name !== 'string' || obj.name.length === 0) {
|
|
119
|
-
throw new WorkflowLoadError("缺少必填字段 'name'(string)", file);
|
|
120
|
-
}
|
|
121
|
-
if (typeof obj.description !== 'string' || obj.description.length === 0) {
|
|
122
|
-
throw new WorkflowLoadError("缺少必填字段 'description'(string)", file);
|
|
123
|
-
}
|
|
124
|
-
if (!Array.isArray(obj.steps) || obj.steps.length === 0) {
|
|
125
|
-
throw new WorkflowLoadError("缺少必填字段 'steps' 或为空数组", file);
|
|
126
|
-
}
|
|
127
|
-
validateSteps(obj.steps as unknown[], file, '');
|
|
128
|
-
|
|
129
|
-
// self_heal 校验(可选)
|
|
130
|
-
if (obj.self_heal !== undefined) {
|
|
131
|
-
const sh = obj.self_heal as Record<string, unknown>;
|
|
132
|
-
if (typeof sh !== 'object' || sh === null || Array.isArray(sh)) {
|
|
133
|
-
throw new WorkflowLoadError("'self_heal' 必须是对象", file);
|
|
134
|
-
}
|
|
135
|
-
if (sh.backoff !== undefined) {
|
|
136
|
-
if (typeof sh.backoff !== 'number' || !Number.isInteger(sh.backoff) || sh.backoff < 0) {
|
|
137
|
-
throw new WorkflowLoadError("self_heal.backoff 必须是 >= 0 的整数", file);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (sh.max_retries !== undefined) {
|
|
141
|
-
if (typeof sh.max_retries !== 'number' || !Number.isInteger(sh.max_retries) || sh.max_retries < 1) {
|
|
142
|
-
throw new WorkflowLoadError("self_heal.max_retries 必须是 >= 1 的整数", file);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (sh.retry_delay_ms !== undefined) {
|
|
146
|
-
if (typeof sh.retry_delay_ms !== 'number' || !Number.isFinite(sh.retry_delay_ms) || sh.retry_delay_ms < 0) {
|
|
147
|
-
throw new WorkflowLoadError("self_heal.retry_delay_ms 必须是 >= 0 的数字(毫秒)", file);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (sh.max_turns_retry_delay_ms !== undefined) {
|
|
151
|
-
if (typeof sh.max_turns_retry_delay_ms !== 'number' || !Number.isFinite(sh.max_turns_retry_delay_ms) || sh.max_turns_retry_delay_ms < 0) {
|
|
152
|
-
throw new WorkflowLoadError("self_heal.max_turns_retry_delay_ms 必须是 >= 0 的数字(毫秒)", file);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// artifacts 校验(WorkflowDef 级别,可选)
|
|
158
|
-
if (obj.artifacts !== undefined) {
|
|
159
|
-
if (!Array.isArray(obj.artifacts) || !(obj.artifacts as unknown[]).every((x) => typeof x === 'string' && x.length > 0)) {
|
|
160
|
-
throw new WorkflowLoadError("'artifacts' 必须是非空字符串数组", file);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (obj.inputs !== undefined) {
|
|
165
|
-
if (!Array.isArray(obj.inputs)) {
|
|
166
|
-
throw new WorkflowLoadError("'inputs' 必须是数组", file);
|
|
167
|
-
}
|
|
168
|
-
for (const [i, item] of (obj.inputs as unknown[]).entries()) {
|
|
169
|
-
if (!item || typeof item !== 'object') {
|
|
170
|
-
throw new WorkflowLoadError(`inputs[${i}] 必须是对象`, file);
|
|
171
|
-
}
|
|
172
|
-
const r = item as Record<string, unknown>;
|
|
173
|
-
if (typeof r.name !== 'string' || r.name.length === 0) {
|
|
174
|
-
throw new WorkflowLoadError(`inputs[${i}].name 必填`, file);
|
|
175
|
-
}
|
|
176
|
-
if (r.type !== undefined && r.type !== 'string' && r.type !== 'number' && r.type !== 'enum') {
|
|
177
|
-
throw new WorkflowLoadError(`inputs[${i}].type 必须是 string/number/enum`, file);
|
|
178
|
-
}
|
|
179
|
-
if (r.type === 'enum' && (!Array.isArray(r.values) || r.values.length === 0)) {
|
|
180
|
-
throw new WorkflowLoadError(`inputs[${i}].values 必填(type=enum 时)`, file);
|
|
181
|
-
}
|
|
182
|
-
// greedy 首参:只许 boolean,且只能标在第一个 input(语义=吸收全部位置参数,
|
|
183
|
-
// 标在非首位无意义且会误导,响亮拒绝)。
|
|
184
|
-
if (r.greedy !== undefined && typeof r.greedy !== 'boolean') {
|
|
185
|
-
throw new WorkflowLoadError(`inputs[${i}].greedy 必须是 boolean`, file);
|
|
186
|
-
}
|
|
187
|
-
if (r.greedy === true && i !== 0) {
|
|
188
|
-
throw new WorkflowLoadError(`inputs[${i}].greedy 只能标在第一个 input 上`, file);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// ADR-05: 控制流 type 集合——这些节点的"动作"由 type 自身定义,
|
|
195
|
-
// 因此不能附带 tool/skill/llm 也不能配 output_schema / context_files / allowed_tools。
|
|
196
|
-
// 导出供「editor ↔ 后端」schema 一致性测试比对(test/workflowEditorSchemaSync.test.ts)。
|
|
197
|
-
// editor 因 D2 红线禁止 import 后端、手抄了同一份;后端在此演进时该测试会红,提示同步 editor。
|
|
198
|
-
export const CONTROL_FLOW_TYPES = new Set([
|
|
199
|
-
'assert',
|
|
200
|
-
'verify',
|
|
201
|
-
'set',
|
|
202
|
-
'branch',
|
|
203
|
-
'loop',
|
|
204
|
-
'break',
|
|
205
|
-
'continue',
|
|
206
|
-
'echo',
|
|
207
|
-
'pause',
|
|
208
|
-
]);
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* `uses:` 的合法取值。与 `types.ts::HostCapability` 同源 —— 加新能力要两处一起改,
|
|
212
|
-
* 单测钉住二者一致(漏改的表现是 yaml 写了却静默不生效)。
|
|
213
|
-
*/
|
|
214
|
-
export const VALID_HOST_CAPABILITIES = new Set(['browser', 'computer']);
|
|
215
|
-
|
|
216
|
-
export const VALID_STEP_TYPES = new Set([
|
|
217
|
-
'assert',
|
|
218
|
-
'verify',
|
|
219
|
-
'set',
|
|
220
|
-
'branch',
|
|
221
|
-
'loop',
|
|
222
|
-
'break',
|
|
223
|
-
'continue',
|
|
224
|
-
'echo',
|
|
225
|
-
'pause',
|
|
226
|
-
]);
|
|
227
|
-
|
|
228
|
-
// 动作字段里「自带 kind 含义」的那几个(不含 type —— type 的 kind 由其值决定)。
|
|
229
|
-
// 单一来源:validateSteps 的 actionFields + test/workflowEditorSchemaSync.test.ts 都从这里取,
|
|
230
|
-
// 新增动作 kind 只改这一处(并同步 editor 手抄镜像 + 该测试快照)。
|
|
231
|
-
export const ACTION_KINDS = ['tool', 'skill', 'agent', 'llm'] as const;
|
|
232
|
-
|
|
233
|
-
// onError 合法值(单一来源:onError 校验 + editor schema 同步测试)。
|
|
234
|
-
export const ON_ERROR_VALUES = ['halt', 'continue', 'recover'] as const;
|
|
235
|
-
|
|
236
|
-
function isValidMaxIterations(value: unknown): boolean {
|
|
237
|
-
return (
|
|
238
|
-
(typeof value === 'number' && Number.isInteger(value) && value > 0) ||
|
|
239
|
-
(typeof value === 'string' && (value.includes('${') || /^\d+$/.test(value.trim())))
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function isValidImageDetail(value: unknown): boolean {
|
|
244
|
-
return value === 'low' || value === 'default' || value === 'high';
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function assertLlmOrAgentMultimodalField(
|
|
248
|
-
s: Record<string, unknown>,
|
|
249
|
-
key: string,
|
|
250
|
-
p: string,
|
|
251
|
-
file: string,
|
|
252
|
-
): void {
|
|
253
|
-
if (s.llm === undefined && s.agent === undefined) {
|
|
254
|
-
throw new WorkflowLoadError(`${p}.${key} 仅支持 llm / agent 节点`, file);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
function assertHistoryModeField(
|
|
259
|
-
s: Record<string, unknown>,
|
|
260
|
-
p: string,
|
|
261
|
-
file: string,
|
|
262
|
-
): void {
|
|
263
|
-
if (s.llm === undefined && s.agent === undefined && s.skill === undefined) {
|
|
264
|
-
throw new WorkflowLoadError(
|
|
265
|
-
`${p}.history_mode 只能用在 llm / agent / skill 节点上`,
|
|
266
|
-
file,
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function requireNonEmptyString(
|
|
272
|
-
s: Record<string, unknown>,
|
|
273
|
-
key: string,
|
|
274
|
-
p: string,
|
|
275
|
-
file: string,
|
|
276
|
-
typeName: string,
|
|
277
|
-
): void {
|
|
278
|
-
if (typeof s[key] !== 'string' || String(s[key]).length === 0) {
|
|
279
|
-
throw new WorkflowLoadError(`${p} type=${typeName} requires non-empty '${key}'`, file);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function validateSteps(steps: unknown[], file: string, pathPrefix: string): void {
|
|
284
|
-
const seenIds = new Set<string>();
|
|
285
|
-
for (const [i, item] of steps.entries()) {
|
|
286
|
-
const p = pathPrefix ? `${pathPrefix}.steps[${i}]` : `steps[${i}]`;
|
|
287
|
-
if (!item || typeof item !== 'object') {
|
|
288
|
-
throw new WorkflowLoadError(`${p} 必须是对象`, file);
|
|
289
|
-
}
|
|
290
|
-
const s = item as Record<string, unknown>;
|
|
291
|
-
if (typeof s.id !== 'string' || s.id.length === 0) {
|
|
292
|
-
throw new WorkflowLoadError(`${p}.id 必填`, file);
|
|
293
|
-
}
|
|
294
|
-
// id 唯一仅在同级 steps 数组内强制
|
|
295
|
-
if (seenIds.has(s.id)) {
|
|
296
|
-
throw new WorkflowLoadError(`${p}.id "${s.id}" 在当前 steps 列表中重复`, file);
|
|
297
|
-
}
|
|
298
|
-
seenIds.add(s.id);
|
|
299
|
-
|
|
300
|
-
// type 枚举校验(提前于动作互斥,给更准确的错误信息)
|
|
301
|
-
if (s.type !== undefined && (typeof s.type !== 'string' || !VALID_STEP_TYPES.has(s.type))) {
|
|
302
|
-
throw new WorkflowLoadError(
|
|
303
|
-
`${p}.type "${String(s.type)}" 非法;合法值: ${[...VALID_STEP_TYPES].join(' / ')}`,
|
|
304
|
-
file,
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// 动作字段恰好一个(ACTION_KINDS 单一来源 + 控制流 type)
|
|
309
|
-
const actionFields = [...ACTION_KINDS, 'type'] as (keyof StepDef)[];
|
|
310
|
-
const present = actionFields.filter((k) => s[k] !== undefined);
|
|
311
|
-
if (present.length === 0) {
|
|
312
|
-
throw new WorkflowLoadError(
|
|
313
|
-
`${p} 必须有以下字段之一: ${actionFields.join(' / ')}`,
|
|
314
|
-
file,
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
if (present.length > 1) {
|
|
318
|
-
throw new WorkflowLoadError(
|
|
319
|
-
`${p} 同时设置了 ${present.join(', ')};只允许其中一个`,
|
|
320
|
-
file,
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// onError 枚举校验:runner 只认 halt/continue/recover,typo 会被静默当 halt,
|
|
325
|
-
// 故在加载期拦掉,给作者明确错误。
|
|
326
|
-
if (
|
|
327
|
-
s.onError !== undefined &&
|
|
328
|
-
!ON_ERROR_VALUES.includes(s.onError as (typeof ON_ERROR_VALUES)[number])
|
|
329
|
-
) {
|
|
330
|
-
throw new WorkflowLoadError(
|
|
331
|
-
`${p}.onError "${String(s.onError)}" 非法;合法值: ${ON_ERROR_VALUES.join(' / ')}`,
|
|
332
|
-
file,
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
// recover_hint 必须是字符串(仅 onError:'recover' 时有意义,但不强制配对)
|
|
336
|
-
if (s.recover_hint !== undefined && typeof s.recover_hint !== 'string') {
|
|
337
|
-
throw new WorkflowLoadError(`${p}.recover_hint 必须是 string`, file);
|
|
338
|
-
}
|
|
339
|
-
// uses:宿主能力声明。写错的代价是「这一步安静地拿不到能力」,而工具的报错
|
|
340
|
-
// (「当前宿主未启用 Computer Use」)完全指不向拼错的 yaml —— 所以在加载期拦掉。
|
|
341
|
-
if (s.uses !== undefined) {
|
|
342
|
-
if (!Array.isArray(s.uses)) {
|
|
343
|
-
throw new WorkflowLoadError(
|
|
344
|
-
`${p}.uses 必须是数组,如 uses: [computer]`,
|
|
345
|
-
file,
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
for (const capability of s.uses) {
|
|
349
|
-
if (!VALID_HOST_CAPABILITIES.has(capability as string)) {
|
|
350
|
-
throw new WorkflowLoadError(
|
|
351
|
-
`${p}.uses 含非法能力 "${String(capability)}";合法值: ${[...VALID_HOST_CAPABILITIES].join(' / ')}`,
|
|
352
|
-
file,
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
// uses 只对确定性 tool 步有意义:llm/agent/skill 走 runQuery,工具上下文由
|
|
357
|
-
// 主循环提供;控制流步根本不调工具。声明在这些步上是作者的误解,早点说破。
|
|
358
|
-
if (s.tool === undefined) {
|
|
359
|
-
throw new WorkflowLoadError(
|
|
360
|
-
`${p}.uses 只能用在 tool 步上(当前步不是 tool 步)。`
|
|
361
|
-
+ `agent/llm/skill 步的工具上下文由主循环提供,无需也不能在此声明。`,
|
|
362
|
-
file,
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
if (s.free_text !== undefined) {
|
|
367
|
-
if (s.type !== 'pause') {
|
|
368
|
-
throw new WorkflowLoadError(`${p}.free_text 只能用在 type=pause 节点上`, file);
|
|
369
|
-
}
|
|
370
|
-
if (s.free_text !== 'halt' && s.free_text !== 'approve') {
|
|
371
|
-
throw new WorkflowLoadError(
|
|
372
|
-
`${p}.free_text "${String(s.free_text)}" 非法;合法值: halt / approve`,
|
|
373
|
-
file,
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
// 数字字段类型校验:yaml 给字符串会静默进入运行时算术(NaN footgun),加载期拦掉
|
|
378
|
-
if (
|
|
379
|
-
s.maxTurns !== undefined &&
|
|
380
|
-
(typeof s.maxTurns !== 'number' || !Number.isInteger(s.maxTurns) || s.maxTurns < 1)
|
|
381
|
-
) {
|
|
382
|
-
throw new WorkflowLoadError(`${p}.maxTurns 必须是 >= 1 的整数`, file);
|
|
383
|
-
}
|
|
384
|
-
if (
|
|
385
|
-
s.max_retries !== undefined &&
|
|
386
|
-
(typeof s.max_retries !== 'number' || !Number.isInteger(s.max_retries) || s.max_retries < 0)
|
|
387
|
-
) {
|
|
388
|
-
throw new WorkflowLoadError(`${p}.max_retries 必须是 >= 0 的整数`, file);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// 多模态图片输入:仅 llm / agent 节点支持;skill/控制流/确定性 tool 节点
|
|
392
|
-
// 不接收图片字段,避免 yaml 作者以为图片已传给模型但实际被静默忽略。
|
|
393
|
-
const multimodalFields = [
|
|
394
|
-
'images',
|
|
395
|
-
'parse_inline_images',
|
|
396
|
-
'image_detail',
|
|
397
|
-
'image_max_long_side_pixel',
|
|
398
|
-
];
|
|
399
|
-
for (const key of multimodalFields) {
|
|
400
|
-
if (s[key] !== undefined) assertLlmOrAgentMultimodalField(s, key, p, file);
|
|
401
|
-
}
|
|
402
|
-
if (s.images !== undefined) {
|
|
403
|
-
if (!Array.isArray(s.images)) {
|
|
404
|
-
throw new WorkflowLoadError(`${p}.images 必须是数组`, file);
|
|
405
|
-
}
|
|
406
|
-
for (const [j, item] of (s.images as unknown[]).entries()) {
|
|
407
|
-
if (typeof item === 'string') {
|
|
408
|
-
if (item.length === 0) {
|
|
409
|
-
throw new WorkflowLoadError(`${p}.images[${j}] 必须是非空 string`, file);
|
|
410
|
-
}
|
|
411
|
-
continue;
|
|
412
|
-
}
|
|
413
|
-
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
414
|
-
throw new WorkflowLoadError(
|
|
415
|
-
`${p}.images[${j}] 必须是 string 或 { source, detail?, max_long_side_pixel? }`,
|
|
416
|
-
file,
|
|
417
|
-
);
|
|
418
|
-
}
|
|
419
|
-
const r = item as Record<string, unknown>;
|
|
420
|
-
if (typeof r.source !== 'string' || r.source.length === 0) {
|
|
421
|
-
throw new WorkflowLoadError(`${p}.images[${j}].source 必须是非空 string`, file);
|
|
422
|
-
}
|
|
423
|
-
if (r.detail !== undefined && !isValidImageDetail(r.detail)) {
|
|
424
|
-
throw new WorkflowLoadError(
|
|
425
|
-
`${p}.images[${j}].detail 必须是 low/default/high`,
|
|
426
|
-
file,
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
if (
|
|
430
|
-
r.max_long_side_pixel !== undefined &&
|
|
431
|
-
(typeof r.max_long_side_pixel !== 'number' ||
|
|
432
|
-
!Number.isInteger(r.max_long_side_pixel) ||
|
|
433
|
-
r.max_long_side_pixel < 1)
|
|
434
|
-
) {
|
|
435
|
-
throw new WorkflowLoadError(
|
|
436
|
-
`${p}.images[${j}].max_long_side_pixel 必须是正整数`,
|
|
437
|
-
file,
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
if (
|
|
443
|
-
s.parse_inline_images !== undefined &&
|
|
444
|
-
typeof s.parse_inline_images !== 'boolean'
|
|
445
|
-
) {
|
|
446
|
-
throw new WorkflowLoadError(`${p}.parse_inline_images 必须是 boolean`, file);
|
|
447
|
-
}
|
|
448
|
-
if (s.image_detail !== undefined && !isValidImageDetail(s.image_detail)) {
|
|
449
|
-
throw new WorkflowLoadError(`${p}.image_detail 必须是 low/default/high`, file);
|
|
450
|
-
}
|
|
451
|
-
if (
|
|
452
|
-
s.image_max_long_side_pixel !== undefined &&
|
|
453
|
-
(typeof s.image_max_long_side_pixel !== 'number' ||
|
|
454
|
-
!Number.isInteger(s.image_max_long_side_pixel) ||
|
|
455
|
-
s.image_max_long_side_pixel < 1)
|
|
456
|
-
) {
|
|
457
|
-
throw new WorkflowLoadError(`${p}.image_max_long_side_pixel 必须是正整数`, file);
|
|
458
|
-
}
|
|
459
|
-
if (s.history_mode !== undefined) {
|
|
460
|
-
assertHistoryModeField(s, p, file);
|
|
461
|
-
if (s.history_mode !== 'shared' && s.history_mode !== 'isolated') {
|
|
462
|
-
throw new WorkflowLoadError(
|
|
463
|
-
`${p}.history_mode "${String(s.history_mode)}" 非法;合法值: shared / isolated`,
|
|
464
|
-
file,
|
|
465
|
-
);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// 控制流字段一致性
|
|
470
|
-
if (s.type === 'branch' && s.condition === undefined) {
|
|
471
|
-
throw new WorkflowLoadError(`${p} type=branch 必须带 'condition'`, file);
|
|
472
|
-
}
|
|
473
|
-
if (s.type === 'loop') {
|
|
474
|
-
const hasOver = s.over !== undefined;
|
|
475
|
-
const hasMaxIter = s.max_iterations !== undefined;
|
|
476
|
-
// 两种模式互斥:for-each(over 必填)或 while(max_iterations 必填)
|
|
477
|
-
if (!hasOver && !hasMaxIter) {
|
|
478
|
-
throw new WorkflowLoadError(`${p} type=loop: for-each 模式需 'over',while 模式需 'max_iterations',二者必选其一`, file);
|
|
479
|
-
}
|
|
480
|
-
if (hasOver) {
|
|
481
|
-
// for-each 模式
|
|
482
|
-
if (typeof s.as !== 'string' || s.as.length === 0) {
|
|
483
|
-
throw new WorkflowLoadError(`${p} type=loop for-each 模式必须带 'as'`, file);
|
|
484
|
-
}
|
|
485
|
-
} else {
|
|
486
|
-
// while 模式:as 可选(不设则不注入迭代变量)
|
|
487
|
-
if (s.as !== undefined && (typeof s.as !== 'string' || s.as.length === 0)) {
|
|
488
|
-
throw new WorkflowLoadError(`${p} type=loop while 模式的 'as' 必须是非空字符串`, file);
|
|
489
|
-
}
|
|
490
|
-
// max_iterations:正整数 或 含 ${} 的模板字符串(运行时插值 + coerce,
|
|
491
|
-
// 失败响亮报错)。裸字符串数字("50")也容忍。其它一律加载期拦掉——
|
|
492
|
-
// 曾有 "${max_iterations}" 不被插值直接参与 < 比较 → NaN → 静默 0 次迭代。
|
|
493
|
-
const mi = s.max_iterations;
|
|
494
|
-
if (!isValidMaxIterations(mi)) {
|
|
495
|
-
throw new WorkflowLoadError(
|
|
496
|
-
`${p}.max_iterations 必须是正整数或含 \${} 的模板字符串,得到: ${JSON.stringify(mi)}`,
|
|
497
|
-
file,
|
|
498
|
-
);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
if (!Array.isArray(s.do)) {
|
|
502
|
-
throw new WorkflowLoadError(`${p} type=loop 必须带 'do' 数组`, file);
|
|
503
|
-
}
|
|
504
|
-
validateSteps(s.do as unknown[], file, `${p}.do`);
|
|
505
|
-
}
|
|
506
|
-
if (s.type === 'break') {
|
|
507
|
-
// break 是循环退出信号,通常配合 when 条件使用
|
|
508
|
-
// 无额外必填字段;when 缺失时等价于无条件跳出(极少用)
|
|
509
|
-
}
|
|
510
|
-
// continue:跳到(目标)循环的下一轮迭代;与 break 同族(when 可选)
|
|
511
|
-
// echo:确定性输出 message(支持 ${} 插值),不烧 LLM
|
|
512
|
-
if (s.type === 'echo') {
|
|
513
|
-
requireNonEmptyString(s, 'message', p, file, 'echo');
|
|
514
|
-
} else if (s.message !== undefined) {
|
|
515
|
-
throw new WorkflowLoadError(`${p}.message 只能用在 type=echo 节点上`, file);
|
|
516
|
-
}
|
|
517
|
-
// target:仅 break/continue 的标签语义(指定要穿透到哪一层循环)
|
|
518
|
-
if (s.target !== undefined) {
|
|
519
|
-
if (s.type !== 'break' && s.type !== 'continue') {
|
|
520
|
-
throw new WorkflowLoadError(`${p}.target 只能用在 type=break / type=continue 节点上`, file);
|
|
521
|
-
}
|
|
522
|
-
if (typeof s.target !== 'string' || s.target.length === 0) {
|
|
523
|
-
throw new WorkflowLoadError(`${p}.target 必须是非空 string(目标循环的 id)`, file);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
// title:任意步可选的人话进度显示名(声明后状态行显示它、不显示块内相对序号)
|
|
527
|
-
if (s.title !== undefined && (typeof s.title !== 'string' || s.title.length === 0)) {
|
|
528
|
-
throw new WorkflowLoadError(`${p}.title 必须是非空 string(进度显示名)`, file);
|
|
529
|
-
}
|
|
530
|
-
// goal_supplement:动作步与 pause 可声明(产出作为用户补充要求贯穿后续步骤)
|
|
531
|
-
if (s.goal_supplement !== undefined) {
|
|
532
|
-
if (typeof s.goal_supplement !== 'boolean') {
|
|
533
|
-
throw new WorkflowLoadError(`${p}.goal_supplement 必须是 boolean`, file);
|
|
534
|
-
}
|
|
535
|
-
const isActionStep = ACTION_KINDS.some((k) => s[k] !== undefined);
|
|
536
|
-
if (!isActionStep && s.type !== 'pause') {
|
|
537
|
-
throw new WorkflowLoadError(
|
|
538
|
-
`${p}.goal_supplement 只能用在动作步(${ACTION_KINDS.join('/')})或 type=pause 节点上`,
|
|
539
|
-
file,
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
// done_when:仅 agent 步可选(确定性完成条件,引擎 nudge 兜底 announce-not-do 烂尾)
|
|
544
|
-
if (s.done_when !== undefined) {
|
|
545
|
-
if (typeof s.done_when !== 'string' || s.done_when.trim().length === 0) {
|
|
546
|
-
throw new WorkflowLoadError(`${p}.done_when 必须是非空 string(白名单表达式)`, file);
|
|
547
|
-
}
|
|
548
|
-
if (s.agent === undefined) {
|
|
549
|
-
throw new WorkflowLoadError(`${p}.done_when 仅支持 agent 节点`, file);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
if (s.type === 'assert' && s.condition === undefined) {
|
|
553
|
-
throw new WorkflowLoadError(`${p} type=assert 必须带 'condition'`, file);
|
|
554
|
-
}
|
|
555
|
-
// set:确定性变量赋值(曾有 yaml 用 llm:"PLAN" 当赋值——模型自由回复非确定,set 是根治)
|
|
556
|
-
if (s.type === 'set') {
|
|
557
|
-
if (
|
|
558
|
-
!s.set ||
|
|
559
|
-
typeof s.set !== 'object' ||
|
|
560
|
-
Array.isArray(s.set) ||
|
|
561
|
-
Object.keys(s.set as Record<string, unknown>).length === 0
|
|
562
|
-
) {
|
|
563
|
-
throw new WorkflowLoadError(
|
|
564
|
-
`${p} type=set 必须带非空对象 'set'({ 变量名: 值 },值支持 \${} 插值)`,
|
|
565
|
-
file,
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
if (s.type === 'verify') {
|
|
570
|
-
// 兼容两种格式:YAML 数组 或逗号分隔字符串
|
|
571
|
-
const rawChecks: unknown[] = Array.isArray(s.checks)
|
|
572
|
-
? s.checks
|
|
573
|
-
: typeof s.checks === 'string'
|
|
574
|
-
? s.checks.split(',').map((x) => x.trim()).filter(Boolean)
|
|
575
|
-
: [];
|
|
576
|
-
if (rawChecks.length === 0) {
|
|
577
|
-
throw new WorkflowLoadError(`${p} type=verify 必须带 checks(数组或逗号分隔字符串)`, file);
|
|
578
|
-
}
|
|
579
|
-
for (const [ci, c] of rawChecks.entries()) {
|
|
580
|
-
if (typeof c !== 'string') {
|
|
581
|
-
throw new WorkflowLoadError(`${p}.checks[${ci}] 必须是字符串`, file);
|
|
582
|
-
}
|
|
583
|
-
// 含 ${...} 模板的 check(如 checks: "${verify_checks}")在运行时插值后才知道真实值,
|
|
584
|
-
// 加载期跳过格式校验,交给 verify.ts 在插值后解析。否则这类合法的动态配置会被
|
|
585
|
-
// 误判为"损坏 yaml"而整个工作流加载失败(ralph-loop 的 run_verify 即如此)。
|
|
586
|
-
if (c.includes('${')) continue;
|
|
587
|
-
if (!c.includes(':')) {
|
|
588
|
-
throw new WorkflowLoadError(`${p}.checks[${ci}] 格式错误,应为 "type:value"(如 "file_exists:./x")`, file);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
if (s.type === 'branch') {
|
|
593
|
-
if (s.then !== undefined && !Array.isArray(s.then)) {
|
|
594
|
-
throw new WorkflowLoadError(`${p}.then 必须是数组`, file);
|
|
595
|
-
}
|
|
596
|
-
if (s.else !== undefined && !Array.isArray(s.else)) {
|
|
597
|
-
throw new WorkflowLoadError(`${p}.else 必须是数组`, file);
|
|
598
|
-
}
|
|
599
|
-
if (Array.isArray(s.then)) validateSteps(s.then as unknown[], file, `${p}.then`);
|
|
600
|
-
if (Array.isArray(s.else)) validateSteps(s.else as unknown[], file, `${p}.else`);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
// ---------------- ADR-05 新增校验 ----------------
|
|
604
|
-
|
|
605
|
-
// output_schema:必须是 object;不能用在控制流节点上
|
|
606
|
-
if (s.output_schema !== undefined) {
|
|
607
|
-
if (
|
|
608
|
-
typeof s.output_schema !== 'object' ||
|
|
609
|
-
s.output_schema === null ||
|
|
610
|
-
Array.isArray(s.output_schema)
|
|
611
|
-
) {
|
|
612
|
-
throw new WorkflowLoadError(`${p}.output_schema 必须是对象`, file);
|
|
613
|
-
}
|
|
614
|
-
if (typeof s.type === 'string' && CONTROL_FLOW_TYPES.has(s.type)) {
|
|
615
|
-
throw new WorkflowLoadError(
|
|
616
|
-
`${p}.output_schema 不能用在 type=${s.type} 控制流节点上`,
|
|
617
|
-
file,
|
|
618
|
-
);
|
|
619
|
-
}
|
|
620
|
-
// P3:output_schema 在 skill 节点上是静默 no-op(execSkillStep 无视它)。
|
|
621
|
-
// 与其悄悄丢弃,不如 loader 直接拒绝——仅 llm 节点支持 structured output。
|
|
622
|
-
if (s.skill !== undefined) {
|
|
623
|
-
throw new WorkflowLoadError(
|
|
624
|
-
`${p}.output_schema 暂不支持 skill 节点(仅 llm 节点生效)`,
|
|
625
|
-
file,
|
|
626
|
-
);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// context_files:[{ path, hint? }];不能用在控制流节点上
|
|
631
|
-
if (s.context_files !== undefined) {
|
|
632
|
-
if (!Array.isArray(s.context_files)) {
|
|
633
|
-
throw new WorkflowLoadError(`${p}.context_files 必须是数组`, file);
|
|
634
|
-
}
|
|
635
|
-
for (const [j, cf] of (s.context_files as unknown[]).entries()) {
|
|
636
|
-
if (!cf || typeof cf !== 'object' || Array.isArray(cf)) {
|
|
637
|
-
throw new WorkflowLoadError(
|
|
638
|
-
`${p}.context_files[${j}] 必须是 { path: string, hint?: string } 对象`,
|
|
639
|
-
file,
|
|
640
|
-
);
|
|
641
|
-
}
|
|
642
|
-
const r = cf as Record<string, unknown>;
|
|
643
|
-
if (typeof r.path !== 'string' || r.path.length === 0) {
|
|
644
|
-
throw new WorkflowLoadError(
|
|
645
|
-
`${p}.context_files[${j}].path 必填(非空 string)`,
|
|
646
|
-
file,
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
if (r.hint !== undefined && typeof r.hint !== 'string') {
|
|
650
|
-
throw new WorkflowLoadError(
|
|
651
|
-
`${p}.context_files[${j}].hint 必须是 string`,
|
|
652
|
-
file,
|
|
653
|
-
);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
if (typeof s.type === 'string' && CONTROL_FLOW_TYPES.has(s.type)) {
|
|
657
|
-
throw new WorkflowLoadError(
|
|
658
|
-
`${p}.context_files 不能用在 type=${s.type} 控制流节点上`,
|
|
659
|
-
file,
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
// P3:context_files 同样是 skill 节点的静默 no-op(execSkillStep 无视它),
|
|
663
|
-
// 仅 llm 节点经 runQuery 生效——loader 直接拒绝,避免悄悄丢弃。
|
|
664
|
-
if (s.skill !== undefined) {
|
|
665
|
-
throw new WorkflowLoadError(
|
|
666
|
-
`${p}.context_files 暂不支持 skill 节点(仅 llm 节点生效)`,
|
|
667
|
-
file,
|
|
668
|
-
);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// allowed_tools:string[];不能用在控制流节点上
|
|
673
|
-
if (s.allowed_tools !== undefined) {
|
|
674
|
-
if (
|
|
675
|
-
!Array.isArray(s.allowed_tools) ||
|
|
676
|
-
!(s.allowed_tools as unknown[]).every((x) => typeof x === 'string' && x.length > 0)
|
|
677
|
-
) {
|
|
678
|
-
throw new WorkflowLoadError(
|
|
679
|
-
`${p}.allowed_tools 必须是非空 string 数组`,
|
|
680
|
-
file,
|
|
681
|
-
);
|
|
682
|
-
}
|
|
683
|
-
if (typeof s.type === 'string' && CONTROL_FLOW_TYPES.has(s.type)) {
|
|
684
|
-
throw new WorkflowLoadError(
|
|
685
|
-
`${p}.allowed_tools 不能用在 type=${s.type} 控制流节点上`,
|
|
686
|
-
file,
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
// P3:allowed_tools 与 context_files 配对,skill 节点上同样无效——一并拒绝。
|
|
690
|
-
if (s.skill !== undefined) {
|
|
691
|
-
throw new WorkflowLoadError(
|
|
692
|
-
`${p}.allowed_tools 暂不支持 skill 节点(仅 llm 节点生效)`,
|
|
693
|
-
file,
|
|
694
|
-
);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// ---------------- Phase 1: 断点续执行 / 自愈字段校验 ----------------
|
|
699
|
-
|
|
700
|
-
// step.artifacts: string[](可选)
|
|
701
|
-
if (s.artifacts !== undefined) {
|
|
702
|
-
if (!Array.isArray(s.artifacts) || !(s.artifacts as unknown[]).every((x) => typeof x === 'string' && x.length > 0)) {
|
|
703
|
-
throw new WorkflowLoadError(
|
|
704
|
-
`${p}.artifacts 必须是非空字符串数组`,
|
|
705
|
-
file,
|
|
706
|
-
);
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// step.max_retries: number(可选,>= 0 的整数)
|
|
711
|
-
if (s.max_retries !== undefined) {
|
|
712
|
-
const mr = s.max_retries;
|
|
713
|
-
if (typeof mr !== 'number' || !Number.isInteger(mr) || mr < 0) {
|
|
714
|
-
throw new WorkflowLoadError(
|
|
715
|
-
`${p}.max_retries 必须是 >= 0 的整数`,
|
|
716
|
-
file,
|
|
717
|
-
);
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
}
|