minimal-agent 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/package.json +62 -5
- package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
- package/plugins/cron-runner/src/commands.js +4 -4
- package/plugins/cron-runner/src/commands.ts +4 -4
- package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
- package/plugins/workflow-runner/commands/workflow.md +57 -90
- package/plugins/workflow-runner/commands/workflows.md +4 -4
- package/plugins/workflow-runner/plugin.js +24 -13
- package/plugins/workflow-runner/plugin.ts +28 -13
- package/plugins/workflow-runner/src/discovery.js +160 -0
- package/plugins/workflow-runner/src/discovery.ts +194 -0
- package/plugins/workflow-runner/src/goalAnchor.js +53 -0
- package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
- package/plugins/workflow-runner/src/index.js +204 -185
- package/plugins/workflow-runner/src/index.ts +249 -224
- package/plugins/workflow-runner/src/inputCoercion.js +40 -0
- package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
- package/plugins/workflow-runner/src/journal.js +224 -0
- package/plugins/workflow-runner/src/journal.ts +274 -0
- package/plugins/workflow-runner/src/jsonSchema.js +132 -0
- package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
- package/plugins/workflow-runner/src/moduleResolution.js +64 -0
- package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
- package/plugins/workflow-runner/src/resourceInputs.js +220 -0
- package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
- package/plugins/workflow-runner/src/runner.js +404 -467
- package/plugins/workflow-runner/src/runner.ts +491 -535
- package/plugins/workflow-runner/src/runtime/config.js +46 -0
- package/plugins/workflow-runner/src/runtime/config.ts +69 -0
- package/plugins/workflow-runner/src/runtime/context.js +1304 -0
- package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
- package/plugins/workflow-runner/src/runtime/exec.js +34 -0
- package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
- package/plugins/workflow-runner/src/runtime/flow.js +8 -0
- package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
- package/plugins/workflow-runner/src/runtime/fs.js +27 -0
- package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
- package/plugins/workflow-runner/src/runtime/model.js +6 -0
- package/plugins/workflow-runner/src/runtime/model.ts +8 -0
- package/plugins/workflow-runner/src/runtime/output.js +7 -0
- package/plugins/workflow-runner/src/runtime/output.ts +21 -0
- package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
- package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
- package/plugins/workflow-runner/src/runtime/state.js +16 -0
- package/plugins/workflow-runner/src/runtime/state.ts +19 -0
- package/plugins/workflow-runner/src/runtime/tools.js +3 -0
- package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
- package/plugins/workflow-runner/src/sandbox.js +141 -0
- package/plugins/workflow-runner/src/sandbox.ts +182 -0
- package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
- package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
- package/plugins/workflow-runner/src/taskIdentity.js +172 -152
- package/plugins/workflow-runner/src/taskIdentity.ts +233 -200
- package/plugins/workflow-runner/src/toolRuntime.js +29 -8
- package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
- package/plugins/workflow-runner/src/types.js +7 -105
- package/plugins/workflow-runner/src/types.ts +155 -520
- package/plugins/workflow-runner/src/util.js +47 -0
- package/plugins/workflow-runner/src/util.ts +51 -0
- package/plugins/workflow-runner/src/workflowContext.js +20 -0
- package/plugins/workflow-runner/src/workflowContext.ts +58 -0
- package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
- package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
- package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
- package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
- package/plugins/workflow-runner/src/workflowState.js +542 -469
- package/plugins/workflow-runner/src/workflowState.ts +639 -589
- package/plugins/workflow-runner/test/api.test.ts +223 -0
- package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
- package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
- package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
- package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
- package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
- package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
- package/plugins/workflow-runner/test/discovery.test.ts +234 -0
- package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
- package/plugins/workflow-runner/test/goalController.test.ts +317 -0
- package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
- package/plugins/workflow-runner/test/listResources.test.ts +106 -0
- package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
- package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
- package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
- package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
- package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
- package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
- package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
- package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
- package/skills/pptx/examples/assets/cover-bg.svg +23 -0
- package/skills/pptx/examples/assets/icon-spark.svg +5 -0
- package/skills/skill-creator/scripts/run_eval.py +3 -2
- package/skills/ui-design/SKILL.md +5 -2
- package/skills/ui-design/references/layout-composition.md +1 -1
- package/skills/workflow-creator/SKILL.md +344 -0
- package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
- package/skills/workflow-creator/references/api.md +216 -0
- package/skills/workflow-creator/references/migration.md +103 -0
- package/skills/workflow-creator/references/patterns.md +779 -0
- package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
- package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
- package/skills/workflow-creator/scripts/validate.mjs +275 -0
- package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
- package/src/advisor/advisorCoordinator.js +503 -0
- package/src/advisor/advisorCoordinator.ts +657 -0
- package/src/cli/configMenu.tsx +0 -1
- package/src/cli/print.js +44 -6
- package/src/cli/print.ts +73 -4
- package/src/cli/streamJson.js +5 -6
- package/src/cli/streamJson.ts +8 -4
- package/src/cli/wizards/advisorWizard.js +4 -2
- package/src/cli/wizards/advisorWizard.tsx +8 -2
- package/src/cli/wizards/contextAdvisorWizard.js +5 -4
- package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
- package/src/collaboration/taskFlow.js +115 -0
- package/src/collaboration/taskFlow.ts +169 -0
- package/src/config/configDefaults.js +41 -1
- package/src/config/configDefaults.ts +43 -1
- package/src/config/configDiagnostics.ts +2 -2
- package/src/config/configFile.js +40 -9
- package/src/config/configFile.ts +60 -11
- package/src/config/configService.js +443 -74
- package/src/config/configService.ts +723 -142
- package/src/config/numericLimits.js +22 -0
- package/src/config/numericLimits.ts +24 -0
- package/src/config.js +1 -0
- package/src/config.ts +3 -2
- package/src/context/budgetPlan.js +1 -1
- package/src/context/budgetPlan.ts +1 -1
- package/src/context/compact.js +7 -2
- package/src/context/compact.ts +9 -2
- package/src/context/microCompactLite.js +212 -13
- package/src/context/microCompactLite.ts +261 -12
- package/src/context/sessionRegistry.js +8 -1
- package/src/context/sessionRegistry.ts +18 -5
- package/src/context/toolResultArchive.js +72 -0
- package/src/context/toolResultArchive.ts +95 -0
- package/src/goal/errors.js +8 -0
- package/src/goal/errors.ts +18 -0
- package/src/goal/goalRuntime.js +344 -0
- package/src/goal/goalRuntime.ts +466 -0
- package/src/goal/goalStore.js +200 -0
- package/src/goal/goalStore.ts +219 -0
- package/src/goal/index.js +4 -0
- package/src/goal/index.ts +4 -0
- package/src/goal/statusMapping.js +71 -0
- package/src/goal/statusMapping.ts +95 -0
- package/src/goal/types.js +7 -0
- package/src/goal/types.ts +179 -0
- package/src/goal/workflowController.js +306 -0
- package/src/goal/workflowController.ts +360 -0
- package/src/harness/evidenceLedger.js +2 -0
- package/src/harness/evidenceLedger.ts +1 -0
- package/src/harness/stallGuard.js +9 -3
- package/src/harness/stallGuard.ts +7 -3
- package/src/images/imageResolver.js +161 -0
- package/src/images/imageResolver.ts +189 -0
- package/src/infra/verification.js +56 -20
- package/src/infra/verification.ts +87 -20
- package/src/llm/client.js +79 -21
- package/src/llm/client.ts +182 -35
- package/src/llm/gatewayClient.js +201 -0
- package/src/llm/gatewayClient.ts +299 -0
- package/src/llm/messageProvenance.js +46 -0
- package/src/llm/messageProvenance.ts +58 -0
- package/src/llm/modelProvider.js +1 -0
- package/src/llm/modelProvider.ts +22 -0
- package/src/llm/providers/anthropicFormat.js +108 -1
- package/src/llm/providers/anthropicFormat.ts +115 -4
- package/src/llm/providers/openaiFormat.js +9 -0
- package/src/llm/providers/openaiFormat.ts +12 -3
- package/src/loop.js +319 -54
- package/src/loop.ts +373 -57
- package/src/main.js +4 -1
- package/src/main.tsx +4 -1
- package/src/managedExecution/index.js +2 -0
- package/src/managedExecution/index.ts +2 -0
- package/src/managedExecution/registry.js +15 -0
- package/src/managedExecution/registry.ts +20 -0
- package/src/managedExecution/types.js +1 -0
- package/src/managedExecution/types.ts +86 -0
- package/src/modelRef.js +9 -0
- package/src/modelRef.ts +31 -0
- package/src/multimodal.js +14 -28
- package/src/multimodal.ts +17 -33
- package/src/net/lanDiscovery.js +65 -0
- package/src/net/lanDiscovery.ts +82 -0
- package/src/plugin-sdk.js +28 -3
- package/src/plugin-sdk.ts +39 -6
- package/src/plugins/pluginApi.ts +16 -1
- package/src/plugins/pluginLoader.js +4 -0
- package/src/plugins/pluginLoader.ts +6 -0
- package/src/plugins/pluginRunner.js +152 -9
- package/src/plugins/pluginRunner.ts +186 -8
- package/src/plugins/slashFallback.js +113 -0
- package/src/plugins/slashFallback.ts +137 -0
- package/src/prompts/policyRegistry.js +15 -8
- package/src/prompts/policyRegistry.ts +18 -8
- package/src/prompts/promptAssembly.js +103 -12
- package/src/prompts/promptAssembly.ts +114 -12
- package/src/prompts/skillList.js +21 -3
- package/src/prompts/skillList.ts +27 -2
- package/src/prompts/stableSystemPrompt.js +18 -0
- package/src/prompts/stableSystemPrompt.ts +33 -0
- package/src/runtime/agentRuntimeContext.js +8 -2
- package/src/runtime/agentRuntimeContext.ts +17 -4
- package/src/session/runAgentTurn.js +78 -12
- package/src/session/runAgentTurn.ts +105 -23
- package/src/session/runTurn.js +10 -9
- package/src/session/runTurn.ts +17 -8
- package/src/session/sessionState.ts +2 -0
- package/src/team/auth.js +186 -0
- package/src/team/auth.ts +298 -0
- package/src/team/cli.js +105 -0
- package/src/team/cli.ts +122 -0
- package/src/team/onlineToolContract.js +64 -0
- package/src/team/onlineToolContract.ts +109 -0
- package/src/team/toolPolicy.js +36 -0
- package/src/team/toolPolicy.ts +75 -0
- package/src/tools/advisor/advisor.js +148 -37
- package/src/tools/advisor/advisor.ts +161 -38
- package/src/tools/advisor/advisorDecision.js +71 -0
- package/src/tools/advisor/advisorDecision.ts +85 -0
- package/src/tools/agent/agent.js +18 -5
- package/src/tools/agent/agent.ts +21 -7
- package/src/tools/agent/agentControl.js +246 -0
- package/src/tools/agent/agentControl.ts +284 -0
- package/src/tools/agent/agentTaskService.js +40 -1
- package/src/tools/agent/agentTaskService.ts +44 -1
- package/src/tools/agent/agentTypes.js +14 -2
- package/src/tools/agent/agentTypes.ts +15 -2
- package/src/tools/applypatch/applyPatch.js +362 -0
- package/src/tools/applypatch/applyPatch.ts +428 -0
- package/src/tools/bash/bash.js +70 -18
- package/src/tools/bash/bash.ts +75 -20
- package/src/tools/capabilities.js +10 -5
- package/src/tools/capabilities.ts +10 -5
- package/src/tools/computeruse/computerIntent.js +32 -0
- package/src/tools/computeruse/computerIntent.ts +46 -0
- package/src/tools/computeruse/telemetryLog.js +12 -0
- package/src/tools/computeruse/telemetryLog.ts +16 -0
- package/src/tools/computeruse/vision/telemetry.js +1 -0
- package/src/tools/computeruse/vision/telemetry.ts +1 -0
- package/src/tools/effectiveToolSet.js +4 -0
- package/src/tools/effectiveToolSet.ts +4 -0
- package/src/tools/index.js +21 -2
- package/src/tools/index.ts +33 -2
- package/src/tools/powershell/powershell.js +18 -1
- package/src/tools/powershell/powershell.ts +18 -2
- package/src/tools/presentresult/presentresult.js +6 -15
- package/src/tools/presentresult/presentresult.ts +6 -15
- package/src/tools/read/read.js +69 -11
- package/src/tools/read/read.ts +70 -11
- package/src/tools/shared/fileUtils.js +1 -2
- package/src/tools/shared/fileUtils.ts +1 -2
- package/src/tools/shared/permanentDeleteGuard.js +34 -3
- package/src/tools/shared/permanentDeleteGuard.ts +43 -6
- package/src/tools/shared/toolOutputStore.js +23 -1
- package/src/tools/shared/toolOutputStore.ts +22 -1
- package/src/tools/skill/skill.js +9 -3
- package/src/tools/skill/skill.ts +8 -4
- package/src/tools/toolPromptGuides.js +117 -0
- package/src/tools/toolPromptGuides.ts +137 -0
- package/src/tools/webbrowser/agentBrowserPath.js +19 -9
- package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
- package/src/tools/webbrowser/browser.js +27 -11
- package/src/tools/webbrowser/browser.ts +68 -24
- package/src/tools/webbrowser/browserAutomation.js +329 -0
- package/src/tools/webbrowser/browserAutomation.ts +467 -0
- package/src/tools/webbrowser/browserAxState.js +97 -0
- package/src/tools/webbrowser/browserAxState.ts +142 -0
- package/src/tools/webbrowser/browserBatch.js +1578 -0
- package/src/tools/webbrowser/browserBatch.ts +2382 -0
- package/src/tools/webbrowser/browserCdp.js +208 -0
- package/src/tools/webbrowser/browserCdp.ts +255 -0
- package/src/tools/webbrowser/browserConditions.js +24 -0
- package/src/tools/webbrowser/browserConditions.ts +45 -0
- package/src/tools/webbrowser/browserContent.js +281 -0
- package/src/tools/webbrowser/browserContent.ts +438 -0
- package/src/tools/webbrowser/browserControl.js +131 -30
- package/src/tools/webbrowser/browserControl.ts +210 -50
- package/src/tools/webbrowser/browserDaemon.js +22 -14
- package/src/tools/webbrowser/browserDaemon.ts +54 -21
- package/src/tools/webbrowser/browserFrames.js +47 -0
- package/src/tools/webbrowser/browserFrames.ts +54 -0
- package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
- package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
- package/src/tools/webbrowser/browserLocator.js +140 -0
- package/src/tools/webbrowser/browserLocator.ts +194 -0
- package/src/tools/webbrowser/browserOutcome.js +10 -17
- package/src/tools/webbrowser/browserOutcome.ts +48 -31
- package/src/tools/webbrowser/browserRecovery.js +58 -0
- package/src/tools/webbrowser/browserRecovery.ts +83 -0
- package/src/tools/webbrowser/browserRunRegistry.js +35 -3
- package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
- package/src/tools/webbrowser/browserService.js +28 -2
- package/src/tools/webbrowser/browserService.ts +49 -9
- package/src/tools/webbrowser/browserStream.js +132 -33
- package/src/tools/webbrowser/browserStream.ts +224 -50
- package/src/tools/webbrowser/browserTabs.js +48 -15
- package/src/tools/webbrowser/browserTabs.ts +178 -96
- package/src/tools/webbrowser/webbrowser.js +683 -144
- package/src/tools/webbrowser/webbrowser.ts +979 -236
- package/src/tools/webfetch/preapproved.js +0 -2
- package/src/tools/webfetch/preapproved.ts +0 -2
- package/src/tools/websearch/websearch.js +129 -8
- package/src/tools/websearch/websearch.ts +138 -6
- package/src/types.ts +450 -42
- package/src/ui/App.js +377 -58
- package/src/ui/App.tsx +576 -111
- package/src/ui/LiveArea.js +14 -8
- package/src/ui/LiveArea.tsx +14 -10
- package/src/ui/MessageList.js +63 -20
- package/src/ui/MessageList.tsx +101 -16
- package/src/ui/ModelPicker.js +7 -4
- package/src/ui/ModelPicker.tsx +55 -8
- package/src/ui/hooks/useChat.js +152 -163
- package/src/ui/hooks/useChat.ts +396 -334
- package/src/ui/loginCommand.js +56 -0
- package/src/ui/loginCommand.ts +63 -0
- package/src/ui/toolProgress.js +41 -0
- package/src/ui/toolProgress.ts +44 -0
- package/src/web/browserBridge.js +20 -2
- package/src/web/browserBridge.ts +20 -2
- package/src/web/protocol.ts +1897 -194
- package/workflows/adaptive-research/workflow.mjs +86 -0
- package/workflows/e2e-capabilities/workflow.mjs +102 -0
- package/workflows/e2e-llm/workflow.mjs +52 -0
- package/workflows/e2e-smoke/workflow.mjs +59 -0
- package/workflows/e2e-vision-schema/README.md +48 -0
- package/workflows/e2e-vision-schema/workflow.mjs +149 -0
- package/workflows/e2e-write-greet/workflow.mjs +37 -0
- package/workflows/file-census/README.md +14 -0
- package/workflows/file-census/scripts/.gitkeep +2 -0
- package/workflows/file-census/workflow.mjs +110 -0
- package/workflows/goal/README.md +166 -0
- package/workflows/goal/prompts/audit.md +57 -0
- package/workflows/goal/prompts/contract.md +56 -0
- package/workflows/goal/prompts/iterate.md +37 -0
- package/workflows/goal/workflow.mjs +501 -0
- package/workflows/illustrate/README.md +90 -0
- package/workflows/illustrate/assets/engines.json +20 -0
- package/workflows/illustrate/assets/extract-baidu.js +21 -0
- package/workflows/illustrate/assets/extract-bing.js +33 -0
- package/workflows/illustrate/assets/extract-google.js +31 -0
- package/workflows/illustrate/workflow.mjs +301 -0
- package/workflows/plan/workflow.mjs +389 -0
- package/workflows/question-crops/README.md +35 -0
- package/workflows/question-crops/prompts/inventory.md +23 -0
- package/workflows/question-crops/scripts/image-ops.mjs +305 -0
- package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
- package/workflows/question-crops/workflow.mjs +172 -0
- package/workflows/ralph-loop/workflow.mjs +162 -0
- package/workflows/research/DESIGN.md +449 -0
- package/workflows/research/README.md +74 -0
- package/workflows/research/prompts/s0-goal.md +43 -0
- package/workflows/research/prompts/s1-early-exit.md +22 -0
- package/workflows/research/prompts/s1-map.md +58 -0
- package/workflows/research/prompts/s2-options.md +53 -0
- package/workflows/research/prompts/s3-deep-dive.md +56 -0
- package/workflows/research/prompts/s4-verify.md +74 -0
- package/workflows/research/prompts/s5-synthesis.md +81 -0
- package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
- package/workflows/research/prompts/s6-package.md +32 -0
- package/workflows/research/scripts/build-report.mjs +98 -0
- package/workflows/research/workflow.mjs +288 -0
- package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
- package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
- package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
- package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
- package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
- package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
- package/workflows/wechat-reading-shots/workflow.mjs +266 -0
- package/workflows/wrong-question-docx/README.md +55 -0
- package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
- package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
- package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
- package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
- package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
- package/workflows/wrong-question-docx/workflow.mjs +485 -0
- package/plugins/workflow-runner/src/agentCommon.js +0 -354
- package/plugins/workflow-runner/src/agentCommon.ts +0 -435
- package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
- package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
- package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
- package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
- package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
- package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
- package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
- package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
- package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
- package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
- package/plugins/workflow-runner/src/dslManifest.js +0 -72
- package/plugins/workflow-runner/src/dslManifest.ts +0 -127
- package/plugins/workflow-runner/src/expressions.js +0 -618
- package/plugins/workflow-runner/src/expressions.ts +0 -622
- package/plugins/workflow-runner/src/loader.js +0 -573
- package/plugins/workflow-runner/src/loader.ts +0 -721
- package/plugins/workflow-runner/src/multimodal.js +0 -38
- package/plugins/workflow-runner/src/multimodal.ts +0 -64
- package/plugins/workflow-runner/src/recovery.js +0 -119
- package/plugins/workflow-runner/src/recovery.ts +0 -149
- package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
- package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
- package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -41
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -50
- package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
- package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
- package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
- package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
- package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
- package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
- package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
- package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -208
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -256
- package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -58
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -68
- package/plugins/workflow-runner/src/stepPipeline.js +0 -277
- package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
- package/plugins/workflow-runner/test/agent.test.ts +0 -587
- package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
- package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
- package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
- package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -147
- package/plugins/workflow-runner/test/context-files.test.ts +0 -320
- package/plugins/workflow-runner/test/e2e.test.ts +0 -370
- package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -226
- package/plugins/workflow-runner/test/expressions.test.ts +0 -397
- package/plugins/workflow-runner/test/foundation.test.ts +0 -126
- package/plugins/workflow-runner/test/graceful.test.ts +0 -139
- package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
- package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
- package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
- package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
- package/plugins/workflow-runner/test/loader.test.ts +0 -617
- package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
- package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
- package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
- package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -87
- package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
- package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
- package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -220
- package/plugins/workflow-runner/test/recovery.test.ts +0 -265
- package/plugins/workflow-runner/test/runner.test.ts +0 -911
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -379
- package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -150
- package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
- package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -338
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -270
- package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
- package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
- package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
- package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
- package/plugins/workflow-runner/test/word.load.test.ts +0 -208
- package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
- package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
- package/src/advisorState.js +0 -22
- package/src/cli/configWizard.js +0 -293
- package/src/tools/toolProgressBridge.js +0 -48
- package/workflows/adaptive-research.yaml +0 -50
- package/workflows/book-review-short.yaml +0 -125
- package/workflows/e2e-write-greet.yaml +0 -31
- package/workflows/illustrate.yaml +0 -227
- package/workflows/plan.yaml +0 -470
- package/workflows/ppt.yaml +0 -1670
- package/workflows/ralph-loop.yaml +0 -199
- package/workflows/schema.json +0 -176
- package/workflows/webppt.yaml +0 -496
- package/workflows/wechat-reading-shots.yaml +0 -315
- package/workflows/word.yaml +0 -720
- package/workflows/youtube-shorts.yaml +0 -202
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { isUsageLimitError } from '../llm/gatewayClient.js';
|
|
3
|
+
import { GoalRuntime, GoalRuntimeError, } from './index.js';
|
|
4
|
+
import { OUTCOME_TO_ATTEMPT } from './statusMapping.js';
|
|
5
|
+
function scopeFor(options) {
|
|
6
|
+
const workspaceId = options.runtimeContext?.workspaceId
|
|
7
|
+
?? `path-${createHash('sha256').update(options.cwd).digest('hex').slice(0, 20)}`;
|
|
8
|
+
return {
|
|
9
|
+
workspaceId,
|
|
10
|
+
sessionId: options.runtimeContext?.sessionId?.trim() || 'default',
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function criteriaFrom(inputs) {
|
|
14
|
+
const checks = String(inputs.checks ?? '')
|
|
15
|
+
.split(',')
|
|
16
|
+
.map((item) => item.trim())
|
|
17
|
+
.filter(Boolean);
|
|
18
|
+
return checks.length > 0
|
|
19
|
+
? checks
|
|
20
|
+
: ['按照确认的目标与交付物完成,并提供可检查的验证证据'];
|
|
21
|
+
}
|
|
22
|
+
function render(goal, runtime) {
|
|
23
|
+
const contract = runtime.currentContract(goal);
|
|
24
|
+
const latest = goal.attempts.at(-1);
|
|
25
|
+
const lines = [
|
|
26
|
+
`Goal ${goal.goalId}`,
|
|
27
|
+
`状态:${goal.status}`,
|
|
28
|
+
`目标:${contract.objective}`,
|
|
29
|
+
`契约:v${contract.version} (${contract.hash.slice(0, 12)})`,
|
|
30
|
+
`执行:${goal.attempts.length} 次${latest ? `,最近 ${latest.status}` : ''}`,
|
|
31
|
+
];
|
|
32
|
+
if (goal.statusReason)
|
|
33
|
+
lines.push(`原因:${goal.statusReason}`);
|
|
34
|
+
if (goal.status === 'ready_for_acceptance') {
|
|
35
|
+
lines.push('成果已通过 Agent 自检,等待指定人类验收;Agent 不会自行标记完成。');
|
|
36
|
+
}
|
|
37
|
+
return `${lines.join('\n')}\n`;
|
|
38
|
+
}
|
|
39
|
+
function handled(goal, runtime, prefix) {
|
|
40
|
+
return {
|
|
41
|
+
handled: true,
|
|
42
|
+
inputs: {},
|
|
43
|
+
events: [
|
|
44
|
+
{ type: 'text', delta: `${prefix ? `${prefix}\n` : ''}${render(goal, runtime)}` },
|
|
45
|
+
{ type: 'turn_done' },
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function error(message) {
|
|
50
|
+
return { handled: true, inputs: {}, events: [{ type: 'error', error: message }] };
|
|
51
|
+
}
|
|
52
|
+
/** `/workflow goal` 的外层生命周期控制;返回 execute=false 时不会进入脚本。 */
|
|
53
|
+
export async function prepareGoalCommand(rawInputs, options) {
|
|
54
|
+
const runtime = options.runtime ?? new GoalRuntime();
|
|
55
|
+
const scope = scopeFor(options);
|
|
56
|
+
const actorId = options.actorId ?? 'local-human';
|
|
57
|
+
const action = String(rawInputs.action ?? '').trim();
|
|
58
|
+
const objective = String(rawInputs.objective ?? '').trim();
|
|
59
|
+
let current = await runtime.get(scope);
|
|
60
|
+
const inferredSource = options.source ?? (typeof rawInputs.group_id === 'string' && rawInputs.group_id.trim()
|
|
61
|
+
&& typeof rawInputs.task_id === 'string' && rawInputs.task_id.trim()
|
|
62
|
+
? {
|
|
63
|
+
kind: 'group-task',
|
|
64
|
+
groupId: rawInputs.group_id.trim(),
|
|
65
|
+
taskId: rawInputs.task_id.trim(),
|
|
66
|
+
...(typeof rawInputs.agent_member_id === 'string' && rawInputs.agent_member_id.trim()
|
|
67
|
+
? { assigneeAgentId: rawInputs.agent_member_id.trim() }
|
|
68
|
+
: {}),
|
|
69
|
+
}
|
|
70
|
+
: undefined);
|
|
71
|
+
try {
|
|
72
|
+
if (action === 'status' || (!action && !objective)) {
|
|
73
|
+
return current
|
|
74
|
+
? handled(current, runtime)
|
|
75
|
+
: error('当前 Session 没有 Goal。用 /workflow goal <目标> 创建。');
|
|
76
|
+
}
|
|
77
|
+
if (action === 'cancel') {
|
|
78
|
+
if (!current)
|
|
79
|
+
return error('当前 Session 没有可取消的 Goal。');
|
|
80
|
+
current = await runtime.transition(scope, current.goalId, current.revision, 'cancelled', {
|
|
81
|
+
reason: String(rawInputs.reason ?? '用户取消'), actorId,
|
|
82
|
+
});
|
|
83
|
+
return handled(current, runtime, 'Goal 已取消,历史与产物仍保留。');
|
|
84
|
+
}
|
|
85
|
+
if (action === 'pause') {
|
|
86
|
+
if (!current)
|
|
87
|
+
return error('当前 Session 没有可暂停的 Goal。');
|
|
88
|
+
current = await runtime.transition(scope, current.goalId, current.revision, 'paused', {
|
|
89
|
+
reason: String(rawInputs.reason ?? '用户暂停'), actorId,
|
|
90
|
+
});
|
|
91
|
+
return handled(current, runtime, 'Goal 已暂停,现场已保留。');
|
|
92
|
+
}
|
|
93
|
+
if (action === 'accept') {
|
|
94
|
+
if (!current)
|
|
95
|
+
return error('当前 Session 没有待验收 Goal。');
|
|
96
|
+
current = await runtime.transition(scope, current.goalId, current.revision, 'accepted', { actorId });
|
|
97
|
+
return handled(current, runtime, '人类验收已通过。');
|
|
98
|
+
}
|
|
99
|
+
if (action === 'update') {
|
|
100
|
+
if (!current)
|
|
101
|
+
return error('当前 Session 没有可更新的 Goal。');
|
|
102
|
+
if (!objective)
|
|
103
|
+
return error('update 必须提供新的 objective。');
|
|
104
|
+
const patch = {
|
|
105
|
+
objective,
|
|
106
|
+
confirmedBy: actorId,
|
|
107
|
+
};
|
|
108
|
+
// 未提供 checks 时保留原验收标准;只有显式传入才替换。
|
|
109
|
+
if (Object.prototype.hasOwnProperty.call(rawInputs, 'checks')) {
|
|
110
|
+
patch.acceptanceCriteria = criteriaFrom(rawInputs);
|
|
111
|
+
}
|
|
112
|
+
// 预算也是版本化契约的一部分。显式传 0/非法值表示清除预算;未传时
|
|
113
|
+
// 由 Runtime 的合并规则保留旧值,避免 update 误清空预算。
|
|
114
|
+
if (Object.prototype.hasOwnProperty.call(rawInputs, 'token_budget')) {
|
|
115
|
+
const budget = Number(rawInputs.token_budget);
|
|
116
|
+
patch.tokenBudget = Number.isFinite(budget) && budget > 0 ? budget : null;
|
|
117
|
+
}
|
|
118
|
+
current = await runtime.revise(scope, current.goalId, current.revision, patch);
|
|
119
|
+
}
|
|
120
|
+
else if (action === 'resume' || action === 'rework') {
|
|
121
|
+
if (!current)
|
|
122
|
+
return error('当前 Session 没有可继续的 Goal。');
|
|
123
|
+
current = await runtime.transition(scope, current.goalId, current.revision, 'active', {
|
|
124
|
+
reason: action === 'rework'
|
|
125
|
+
? String(rawInputs.reason ?? '人类要求返工')
|
|
126
|
+
: '用户继续执行',
|
|
127
|
+
actorId,
|
|
128
|
+
});
|
|
129
|
+
rawInputs.objective = runtime.currentContract(current).objective;
|
|
130
|
+
}
|
|
131
|
+
else if (action && action !== 'start') {
|
|
132
|
+
return error(`未知 Goal action:${action}`);
|
|
133
|
+
}
|
|
134
|
+
else if (!current || current.status === 'accepted' || current.status === 'cancelled') {
|
|
135
|
+
if (!objective)
|
|
136
|
+
return error('创建 Goal 必须提供 objective。');
|
|
137
|
+
current = await runtime.create({
|
|
138
|
+
scope,
|
|
139
|
+
contract: {
|
|
140
|
+
objective,
|
|
141
|
+
acceptanceCriteria: criteriaFrom(rawInputs),
|
|
142
|
+
confirmedBy: actorId,
|
|
143
|
+
},
|
|
144
|
+
source: inferredSource,
|
|
145
|
+
createdBy: actorId,
|
|
146
|
+
acceptorHumanIds: options.acceptorHumanIds ?? [actorId],
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const existingObjective = runtime.currentContract(current).objective;
|
|
151
|
+
if (existingObjective !== objective) {
|
|
152
|
+
return error(`当前 Session 已有未完成 Goal「${existingObjective}」。` +
|
|
153
|
+
'请用 --input action=update 显式修改,或先 action=cancel。');
|
|
154
|
+
}
|
|
155
|
+
if (current.status !== 'active')
|
|
156
|
+
return handled(current, runtime);
|
|
157
|
+
}
|
|
158
|
+
// 除非这次调用显式传了新预算,否则沿用当前契约里已有的预算 ——
|
|
159
|
+
// workflow.mjs 每次都无条件把 args.token_budget 写进新契约版本(预算挂在
|
|
160
|
+
// 版本化契约上是刻意设计,见脚本内注释),如果这里不兜底,任何一次「不带
|
|
161
|
+
// token_budget 重跑」(resume/rework/update,甚至只是同目标再跑一轮)都会把
|
|
162
|
+
// 预算悄悄写成 null(不限),「用尽后加预算续跑」的断点设计就形同虚设——
|
|
163
|
+
// 用户以为在续跑同一个受限任务,实际上预算闸已经被静默关掉了。
|
|
164
|
+
if (!Object.prototype.hasOwnProperty.call(rawInputs, 'token_budget') &&
|
|
165
|
+
!(Number(rawInputs.token_budget) > 0)) {
|
|
166
|
+
const existingBudget = runtime.currentContract(current).tokenBudget;
|
|
167
|
+
if (typeof existingBudget === 'number' && existingBudget > 0) {
|
|
168
|
+
rawInputs.token_budget = existingBudget;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const started = await runtime.startAttempt(scope, current.goalId, current.revision, 'goal');
|
|
172
|
+
current = started.goal;
|
|
173
|
+
const execution = started.execution;
|
|
174
|
+
const managedExecution = {
|
|
175
|
+
executionId: execution.goalId,
|
|
176
|
+
version: execution.contractVersion,
|
|
177
|
+
runId: execution.attemptId,
|
|
178
|
+
anchor: renderExecutionAnchor(execution),
|
|
179
|
+
correlations: {
|
|
180
|
+
...(execution.groupId ? { groupId: execution.groupId } : {}),
|
|
181
|
+
...(execution.taskId ? { taskId: execution.taskId } : {}),
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
const bridge = {
|
|
185
|
+
updateContract: async (rawInput) => {
|
|
186
|
+
const input = rawInput;
|
|
187
|
+
const latest = await runtime.get(scope);
|
|
188
|
+
if (!latest || latest.goalId !== execution.goalId) {
|
|
189
|
+
throw new GoalRuntimeError('GOAL_NOT_FOUND', 'Goal 在执行期间已改变。');
|
|
190
|
+
}
|
|
191
|
+
const updated = await runtime.materializeAttemptContract(scope, latest.goalId, latest.revision, execution.attemptId, { ...input, confirmedBy: actorId });
|
|
192
|
+
const contract = runtime.currentContract(updated);
|
|
193
|
+
execution.contractVersion = contract.version;
|
|
194
|
+
execution.contract = contract;
|
|
195
|
+
return {
|
|
196
|
+
version: contract.version,
|
|
197
|
+
anchor: renderExecutionAnchor(execution),
|
|
198
|
+
value: contract,
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
/**
|
|
202
|
+
* 资源记账。累计值落在 `goal.usage`(事件溯源 + 持久化)而不是进程内 ——
|
|
203
|
+
* 断点续跑时账本重放**不会重新执行** agent/llm 调用,进程内计数会从 0 开始,
|
|
204
|
+
* 于是「已经花掉的钱」凭空消失、预算闸永远不触发。
|
|
205
|
+
*
|
|
206
|
+
* ⚠️ 上限取**最新契约**的 tokenBudget,而用量**累计不重置**:
|
|
207
|
+
* 「把预算从 10 万改到 20 万」因此是自然的「加预算继续跑」,
|
|
208
|
+
* 而不是偷偷把已花的清零。
|
|
209
|
+
*/
|
|
210
|
+
recordUsage: async (input) => {
|
|
211
|
+
const latest = await runtime.get(scope);
|
|
212
|
+
if (!latest || latest.goalId !== execution.goalId) {
|
|
213
|
+
throw new GoalRuntimeError('GOAL_NOT_FOUND', 'Goal 在执行期间已改变。');
|
|
214
|
+
}
|
|
215
|
+
const updated = await runtime.recordUsage(scope, latest.goalId, latest.revision, { inputTokens: input.inputTokens, outputTokens: input.outputTokens });
|
|
216
|
+
return {
|
|
217
|
+
inputTokens: updated.usage.inputTokens,
|
|
218
|
+
outputTokens: updated.usage.outputTokens,
|
|
219
|
+
limit: runtime.currentContract(updated).tokenBudget ?? null,
|
|
220
|
+
};
|
|
221
|
+
},
|
|
222
|
+
finish: async (input) => {
|
|
223
|
+
const latest = await runtime.get(scope);
|
|
224
|
+
if (!latest || latest.goalId !== execution.goalId) {
|
|
225
|
+
throw new GoalRuntimeError('GOAL_NOT_FOUND', 'Goal 在执行期间已改变。');
|
|
226
|
+
}
|
|
227
|
+
const attempt = latest.attempts.find((item) => item.attemptId === execution.attemptId);
|
|
228
|
+
if (!attempt || attempt.status !== 'running')
|
|
229
|
+
return latest;
|
|
230
|
+
// Runner 传来的 status 是它自己视角的通用默认值(异常路径恒 'failed')——
|
|
231
|
+
// 它不认识 'budget_limited' 这种具体工作流结局,也不该认识(三层解耦)。
|
|
232
|
+
// 这里按 statusMapping.ts::OUTCOME_TO_ATTEMPT 把工作流自己贴在 Error 上的
|
|
233
|
+
// outcome 精化成真实该记的 attempt 状态:budget_limited 是主动停在安全边界、
|
|
234
|
+
// 现场完好可续跑,要记 'stopped' 而不是 'failed'——记成 'failed' 会让
|
|
235
|
+
// finishAttempt 把 Goal 派生成 blocked,而 TRANSITIONS 里根本没有
|
|
236
|
+
// blocked → budget_limited 这条边,budget_limited 永远到不了(此前正是这样:
|
|
237
|
+
// 状态在类型和 TRANSITIONS 里都声明了,却没有任何代码路径能真正推到它)。
|
|
238
|
+
const outcome = typeof input.outcome === 'string'
|
|
239
|
+
? input.outcome : undefined;
|
|
240
|
+
const attemptStatus = outcome && Object.hasOwn(OUTCOME_TO_ATTEMPT, outcome)
|
|
241
|
+
? OUTCOME_TO_ATTEMPT[outcome]
|
|
242
|
+
: input.status;
|
|
243
|
+
const finished = await runtime.finishAttempt({
|
|
244
|
+
scope,
|
|
245
|
+
expectedGoalId: latest.goalId,
|
|
246
|
+
expectedRevision: latest.revision,
|
|
247
|
+
attemptId: execution.attemptId,
|
|
248
|
+
status: attemptStatus,
|
|
249
|
+
artifacts: input.artifacts,
|
|
250
|
+
evidence: input.evidence,
|
|
251
|
+
blocker: input.blocker,
|
|
252
|
+
});
|
|
253
|
+
// ── 用户设的 token 预算用尽 → budget_limited ────────────────────
|
|
254
|
+
// finishAttempt 把 'stopped' 统一派生成 Goal.paused(与用户主动 action=pause
|
|
255
|
+
// 共用同一落点);这里识别出「这次 stopped 是因为预算」后再精化一步,
|
|
256
|
+
// 避免「用户暂停」与「系统按预算停」被压成同一个状态、桌面端分不清、
|
|
257
|
+
// 用户也不知道该不该加预算再续。
|
|
258
|
+
if (outcome === 'budget_limited' && finished.status === 'paused') {
|
|
259
|
+
return runtime.transition(scope, finished.goalId, finished.revision, 'budget_limited', { reason: input.blocker, actorId });
|
|
260
|
+
}
|
|
261
|
+
// ── 服务端配额/授权不给了 → usage_limited ──────────────────────
|
|
262
|
+
// 识别放在这里而不是具体工作流里:usage_limited 是 Goal 外层生命周期,
|
|
263
|
+
// 工作流只报告通用失败信息;Goal 状态本来就归控制器管理。
|
|
264
|
+
//
|
|
265
|
+
// ⚠️ 与 budget_limited 分工不同,别合并:
|
|
266
|
+
// budget_limited = 用户自己设的额度用完 → 加预算即可继续;
|
|
267
|
+
// usage_limited = 服务端配额/授权不给了 → 加预算没用,要去解决授权。
|
|
268
|
+
// attempt 失败会先把 Goal 推到 blocked;识别出配额类原因后再精化成
|
|
269
|
+
// usage_limited(TRANSITIONS 里显式允许这条精化边)。
|
|
270
|
+
if (input.status === 'failed' &&
|
|
271
|
+
isUsageLimitError(input.blocker ?? '') &&
|
|
272
|
+
finished.status === 'blocked') {
|
|
273
|
+
return runtime.transition(scope, finished.goalId, finished.revision, 'usage_limited', { reason: input.blocker, actorId });
|
|
274
|
+
}
|
|
275
|
+
return finished;
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
return {
|
|
279
|
+
handled: false,
|
|
280
|
+
events: [],
|
|
281
|
+
inputs: rawInputs,
|
|
282
|
+
execution: managedExecution,
|
|
283
|
+
bridge,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
catch (caught) {
|
|
287
|
+
return error(caught instanceof GoalRuntimeError
|
|
288
|
+
? caught.message
|
|
289
|
+
: `Goal Runtime 失败:${caught instanceof Error ? caught.message : String(caught)}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function renderExecutionAnchor(execution) {
|
|
293
|
+
const contract = execution.contract;
|
|
294
|
+
return [
|
|
295
|
+
`🎯 Goal ${execution.goalId} · 契约 v${contract.version}`,
|
|
296
|
+
`目标:${contract.objective}`,
|
|
297
|
+
contract.deliverables.length
|
|
298
|
+
? `交付物:\n${contract.deliverables.map((item) => `- ${item}`).join('\n')}`
|
|
299
|
+
: '',
|
|
300
|
+
contract.acceptanceCriteria.length
|
|
301
|
+
? `验收标准:\n${contract.acceptanceCriteria.map((item) => `- ${item}`).join('\n')}`
|
|
302
|
+
: '',
|
|
303
|
+
contract.scope ? `工作范围:${contract.scope}` : '',
|
|
304
|
+
'只有指定人类可以最终验收;Agent 只能提交可核查证据并进入待验收。',
|
|
305
|
+
].filter(Boolean).join('\n\n');
|
|
306
|
+
}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
import { isUsageLimitError } from '../llm/gatewayClient.js';
|
|
4
|
+
import type { AgentRuntimeContext, LoopEvent } from '../plugin-sdk.js';
|
|
5
|
+
import {
|
|
6
|
+
GoalRuntime,
|
|
7
|
+
GoalRuntimeError,
|
|
8
|
+
type Goal,
|
|
9
|
+
type GoalContractInput,
|
|
10
|
+
type GoalExecutionContext,
|
|
11
|
+
type GoalScope,
|
|
12
|
+
type GoalSource,
|
|
13
|
+
} from './index.js';
|
|
14
|
+
import { OUTCOME_TO_ATTEMPT, type WorkflowOutcome } from './statusMapping.js';
|
|
15
|
+
import type {
|
|
16
|
+
ManagedExecutionBridge,
|
|
17
|
+
ManagedExecutionContext,
|
|
18
|
+
PreparedManagedCommand,
|
|
19
|
+
} from '../managedExecution/types.js';
|
|
20
|
+
|
|
21
|
+
export interface GoalCommandOptions {
|
|
22
|
+
cwd: string;
|
|
23
|
+
runtimeContext?: AgentRuntimeContext;
|
|
24
|
+
actorId?: string;
|
|
25
|
+
acceptorHumanIds?: string[];
|
|
26
|
+
source?: GoalSource;
|
|
27
|
+
runtime?: GoalRuntime;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function scopeFor(options: GoalCommandOptions): GoalScope {
|
|
31
|
+
const workspaceId = options.runtimeContext?.workspaceId
|
|
32
|
+
?? `path-${createHash('sha256').update(options.cwd).digest('hex').slice(0, 20)}`;
|
|
33
|
+
return {
|
|
34
|
+
workspaceId,
|
|
35
|
+
sessionId: options.runtimeContext?.sessionId?.trim() || 'default',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function criteriaFrom(inputs: Record<string, unknown>): string[] {
|
|
40
|
+
const checks = String(inputs.checks ?? '')
|
|
41
|
+
.split(',')
|
|
42
|
+
.map((item) => item.trim())
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
return checks.length > 0
|
|
45
|
+
? checks
|
|
46
|
+
: ['按照确认的目标与交付物完成,并提供可检查的验证证据'];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function render(goal: Goal, runtime: GoalRuntime): string {
|
|
50
|
+
const contract = runtime.currentContract(goal);
|
|
51
|
+
const latest = goal.attempts.at(-1);
|
|
52
|
+
const lines = [
|
|
53
|
+
`Goal ${goal.goalId}`,
|
|
54
|
+
`状态:${goal.status}`,
|
|
55
|
+
`目标:${contract.objective}`,
|
|
56
|
+
`契约:v${contract.version} (${contract.hash.slice(0, 12)})`,
|
|
57
|
+
`执行:${goal.attempts.length} 次${latest ? `,最近 ${latest.status}` : ''}`,
|
|
58
|
+
];
|
|
59
|
+
if (goal.statusReason) lines.push(`原因:${goal.statusReason}`);
|
|
60
|
+
if (goal.status === 'ready_for_acceptance') {
|
|
61
|
+
lines.push('成果已通过 Agent 自检,等待指定人类验收;Agent 不会自行标记完成。');
|
|
62
|
+
}
|
|
63
|
+
return `${lines.join('\n')}\n`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function handled(goal: Goal, runtime: GoalRuntime, prefix?: string): PreparedManagedCommand {
|
|
67
|
+
return {
|
|
68
|
+
handled: true,
|
|
69
|
+
inputs: {},
|
|
70
|
+
events: [
|
|
71
|
+
{ type: 'text', delta: `${prefix ? `${prefix}\n` : ''}${render(goal, runtime)}` },
|
|
72
|
+
{ type: 'turn_done' },
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function error(message: string): PreparedManagedCommand {
|
|
78
|
+
return { handled: true, inputs: {}, events: [{ type: 'error', error: message }] };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** `/workflow goal` 的外层生命周期控制;返回 execute=false 时不会进入脚本。 */
|
|
82
|
+
export async function prepareGoalCommand(
|
|
83
|
+
rawInputs: Record<string, unknown>,
|
|
84
|
+
options: GoalCommandOptions,
|
|
85
|
+
): Promise<PreparedManagedCommand> {
|
|
86
|
+
const runtime = options.runtime ?? new GoalRuntime();
|
|
87
|
+
const scope = scopeFor(options);
|
|
88
|
+
const actorId = options.actorId ?? 'local-human';
|
|
89
|
+
const action = String(rawInputs.action ?? '').trim();
|
|
90
|
+
const objective = String(rawInputs.objective ?? '').trim();
|
|
91
|
+
let current = await runtime.get(scope);
|
|
92
|
+
const inferredSource: GoalSource | undefined = options.source ?? (
|
|
93
|
+
typeof rawInputs.group_id === 'string' && rawInputs.group_id.trim()
|
|
94
|
+
&& typeof rawInputs.task_id === 'string' && rawInputs.task_id.trim()
|
|
95
|
+
? {
|
|
96
|
+
kind: 'group-task',
|
|
97
|
+
groupId: rawInputs.group_id.trim(),
|
|
98
|
+
taskId: rawInputs.task_id.trim(),
|
|
99
|
+
...(typeof rawInputs.agent_member_id === 'string' && rawInputs.agent_member_id.trim()
|
|
100
|
+
? { assigneeAgentId: rawInputs.agent_member_id.trim() }
|
|
101
|
+
: {}),
|
|
102
|
+
}
|
|
103
|
+
: undefined
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
if (action === 'status' || (!action && !objective)) {
|
|
108
|
+
return current
|
|
109
|
+
? handled(current, runtime)
|
|
110
|
+
: error('当前 Session 没有 Goal。用 /workflow goal <目标> 创建。');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (action === 'cancel') {
|
|
114
|
+
if (!current) return error('当前 Session 没有可取消的 Goal。');
|
|
115
|
+
current = await runtime.transition(scope, current.goalId, current.revision, 'cancelled', {
|
|
116
|
+
reason: String(rawInputs.reason ?? '用户取消'), actorId,
|
|
117
|
+
});
|
|
118
|
+
return handled(current, runtime, 'Goal 已取消,历史与产物仍保留。');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (action === 'pause') {
|
|
122
|
+
if (!current) return error('当前 Session 没有可暂停的 Goal。');
|
|
123
|
+
current = await runtime.transition(scope, current.goalId, current.revision, 'paused', {
|
|
124
|
+
reason: String(rawInputs.reason ?? '用户暂停'), actorId,
|
|
125
|
+
});
|
|
126
|
+
return handled(current, runtime, 'Goal 已暂停,现场已保留。');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (action === 'accept') {
|
|
130
|
+
if (!current) return error('当前 Session 没有待验收 Goal。');
|
|
131
|
+
current = await runtime.transition(scope, current.goalId, current.revision, 'accepted', { actorId });
|
|
132
|
+
return handled(current, runtime, '人类验收已通过。');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (action === 'update') {
|
|
136
|
+
if (!current) return error('当前 Session 没有可更新的 Goal。');
|
|
137
|
+
if (!objective) return error('update 必须提供新的 objective。');
|
|
138
|
+
const patch: GoalContractInput = {
|
|
139
|
+
objective,
|
|
140
|
+
confirmedBy: actorId,
|
|
141
|
+
};
|
|
142
|
+
// 未提供 checks 时保留原验收标准;只有显式传入才替换。
|
|
143
|
+
if (Object.prototype.hasOwnProperty.call(rawInputs, 'checks')) {
|
|
144
|
+
patch.acceptanceCriteria = criteriaFrom(rawInputs);
|
|
145
|
+
}
|
|
146
|
+
// 预算也是版本化契约的一部分。显式传 0/非法值表示清除预算;未传时
|
|
147
|
+
// 由 Runtime 的合并规则保留旧值,避免 update 误清空预算。
|
|
148
|
+
if (Object.prototype.hasOwnProperty.call(rawInputs, 'token_budget')) {
|
|
149
|
+
const budget = Number(rawInputs.token_budget);
|
|
150
|
+
patch.tokenBudget = Number.isFinite(budget) && budget > 0 ? budget : null;
|
|
151
|
+
}
|
|
152
|
+
current = await runtime.revise(scope, current.goalId, current.revision, patch);
|
|
153
|
+
} else if (action === 'resume' || action === 'rework') {
|
|
154
|
+
if (!current) return error('当前 Session 没有可继续的 Goal。');
|
|
155
|
+
current = await runtime.transition(scope, current.goalId, current.revision, 'active', {
|
|
156
|
+
reason: action === 'rework'
|
|
157
|
+
? String(rawInputs.reason ?? '人类要求返工')
|
|
158
|
+
: '用户继续执行',
|
|
159
|
+
actorId,
|
|
160
|
+
});
|
|
161
|
+
rawInputs.objective = runtime.currentContract(current).objective;
|
|
162
|
+
} else if (action && action !== 'start') {
|
|
163
|
+
return error(`未知 Goal action:${action}`);
|
|
164
|
+
} else if (!current || current.status === 'accepted' || current.status === 'cancelled') {
|
|
165
|
+
if (!objective) return error('创建 Goal 必须提供 objective。');
|
|
166
|
+
current = await runtime.create({
|
|
167
|
+
scope,
|
|
168
|
+
contract: {
|
|
169
|
+
objective,
|
|
170
|
+
acceptanceCriteria: criteriaFrom(rawInputs),
|
|
171
|
+
confirmedBy: actorId,
|
|
172
|
+
},
|
|
173
|
+
source: inferredSource,
|
|
174
|
+
createdBy: actorId,
|
|
175
|
+
acceptorHumanIds: options.acceptorHumanIds ?? [actorId],
|
|
176
|
+
});
|
|
177
|
+
} else {
|
|
178
|
+
const existingObjective = runtime.currentContract(current).objective;
|
|
179
|
+
if (existingObjective !== objective) {
|
|
180
|
+
return error(
|
|
181
|
+
`当前 Session 已有未完成 Goal「${existingObjective}」。` +
|
|
182
|
+
'请用 --input action=update 显式修改,或先 action=cancel。',
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
if (current.status !== 'active') return handled(current, runtime);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 除非这次调用显式传了新预算,否则沿用当前契约里已有的预算 ——
|
|
189
|
+
// workflow.mjs 每次都无条件把 args.token_budget 写进新契约版本(预算挂在
|
|
190
|
+
// 版本化契约上是刻意设计,见脚本内注释),如果这里不兜底,任何一次「不带
|
|
191
|
+
// token_budget 重跑」(resume/rework/update,甚至只是同目标再跑一轮)都会把
|
|
192
|
+
// 预算悄悄写成 null(不限),「用尽后加预算续跑」的断点设计就形同虚设——
|
|
193
|
+
// 用户以为在续跑同一个受限任务,实际上预算闸已经被静默关掉了。
|
|
194
|
+
if (
|
|
195
|
+
!Object.prototype.hasOwnProperty.call(rawInputs, 'token_budget') &&
|
|
196
|
+
!(Number(rawInputs.token_budget) > 0)
|
|
197
|
+
) {
|
|
198
|
+
const existingBudget = runtime.currentContract(current).tokenBudget;
|
|
199
|
+
if (typeof existingBudget === 'number' && existingBudget > 0) {
|
|
200
|
+
rawInputs.token_budget = existingBudget;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const started = await runtime.startAttempt(scope, current.goalId, current.revision, 'goal');
|
|
205
|
+
current = started.goal;
|
|
206
|
+
const execution = started.execution;
|
|
207
|
+
const managedExecution: ManagedExecutionContext = {
|
|
208
|
+
executionId: execution.goalId,
|
|
209
|
+
version: execution.contractVersion,
|
|
210
|
+
runId: execution.attemptId,
|
|
211
|
+
anchor: renderExecutionAnchor(execution),
|
|
212
|
+
correlations: {
|
|
213
|
+
...(execution.groupId ? { groupId: execution.groupId } : {}),
|
|
214
|
+
...(execution.taskId ? { taskId: execution.taskId } : {}),
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
const bridge: ManagedExecutionBridge = {
|
|
218
|
+
updateContract: async (rawInput) => {
|
|
219
|
+
const input = rawInput as GoalContractInput;
|
|
220
|
+
const latest = await runtime.get(scope);
|
|
221
|
+
if (!latest || latest.goalId !== execution.goalId) {
|
|
222
|
+
throw new GoalRuntimeError('GOAL_NOT_FOUND', 'Goal 在执行期间已改变。');
|
|
223
|
+
}
|
|
224
|
+
const updated = await runtime.materializeAttemptContract(
|
|
225
|
+
scope, latest.goalId, latest.revision, execution.attemptId,
|
|
226
|
+
{ ...input, confirmedBy: actorId },
|
|
227
|
+
);
|
|
228
|
+
const contract = runtime.currentContract(updated);
|
|
229
|
+
execution.contractVersion = contract.version;
|
|
230
|
+
execution.contract = contract;
|
|
231
|
+
return {
|
|
232
|
+
version: contract.version,
|
|
233
|
+
anchor: renderExecutionAnchor(execution),
|
|
234
|
+
value: contract,
|
|
235
|
+
};
|
|
236
|
+
},
|
|
237
|
+
/**
|
|
238
|
+
* 资源记账。累计值落在 `goal.usage`(事件溯源 + 持久化)而不是进程内 ——
|
|
239
|
+
* 断点续跑时账本重放**不会重新执行** agent/llm 调用,进程内计数会从 0 开始,
|
|
240
|
+
* 于是「已经花掉的钱」凭空消失、预算闸永远不触发。
|
|
241
|
+
*
|
|
242
|
+
* ⚠️ 上限取**最新契约**的 tokenBudget,而用量**累计不重置**:
|
|
243
|
+
* 「把预算从 10 万改到 20 万」因此是自然的「加预算继续跑」,
|
|
244
|
+
* 而不是偷偷把已花的清零。
|
|
245
|
+
*/
|
|
246
|
+
recordUsage: async (input) => {
|
|
247
|
+
const latest = await runtime.get(scope);
|
|
248
|
+
if (!latest || latest.goalId !== execution.goalId) {
|
|
249
|
+
throw new GoalRuntimeError('GOAL_NOT_FOUND', 'Goal 在执行期间已改变。');
|
|
250
|
+
}
|
|
251
|
+
const updated = await runtime.recordUsage(
|
|
252
|
+
scope,
|
|
253
|
+
latest.goalId,
|
|
254
|
+
latest.revision,
|
|
255
|
+
{ inputTokens: input.inputTokens, outputTokens: input.outputTokens },
|
|
256
|
+
);
|
|
257
|
+
return {
|
|
258
|
+
inputTokens: updated.usage.inputTokens,
|
|
259
|
+
outputTokens: updated.usage.outputTokens,
|
|
260
|
+
limit: runtime.currentContract(updated).tokenBudget ?? null,
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
finish: async (input) => {
|
|
264
|
+
const latest = await runtime.get(scope);
|
|
265
|
+
if (!latest || latest.goalId !== execution.goalId) {
|
|
266
|
+
throw new GoalRuntimeError('GOAL_NOT_FOUND', 'Goal 在执行期间已改变。');
|
|
267
|
+
}
|
|
268
|
+
const attempt = latest.attempts.find((item) => item.attemptId === execution.attemptId);
|
|
269
|
+
if (!attempt || attempt.status !== 'running') return latest;
|
|
270
|
+
|
|
271
|
+
// Runner 传来的 status 是它自己视角的通用默认值(异常路径恒 'failed')——
|
|
272
|
+
// 它不认识 'budget_limited' 这种具体工作流结局,也不该认识(三层解耦)。
|
|
273
|
+
// 这里按 statusMapping.ts::OUTCOME_TO_ATTEMPT 把工作流自己贴在 Error 上的
|
|
274
|
+
// outcome 精化成真实该记的 attempt 状态:budget_limited 是主动停在安全边界、
|
|
275
|
+
// 现场完好可续跑,要记 'stopped' 而不是 'failed'——记成 'failed' 会让
|
|
276
|
+
// finishAttempt 把 Goal 派生成 blocked,而 TRANSITIONS 里根本没有
|
|
277
|
+
// blocked → budget_limited 这条边,budget_limited 永远到不了(此前正是这样:
|
|
278
|
+
// 状态在类型和 TRANSITIONS 里都声明了,却没有任何代码路径能真正推到它)。
|
|
279
|
+
const outcome = typeof input.outcome === 'string'
|
|
280
|
+
? (input.outcome as WorkflowOutcome) : undefined;
|
|
281
|
+
const attemptStatus = outcome && Object.hasOwn(OUTCOME_TO_ATTEMPT, outcome)
|
|
282
|
+
? OUTCOME_TO_ATTEMPT[outcome]
|
|
283
|
+
: input.status;
|
|
284
|
+
|
|
285
|
+
const finished = await runtime.finishAttempt({
|
|
286
|
+
scope,
|
|
287
|
+
expectedGoalId: latest.goalId,
|
|
288
|
+
expectedRevision: latest.revision,
|
|
289
|
+
attemptId: execution.attemptId,
|
|
290
|
+
status: attemptStatus,
|
|
291
|
+
artifacts: input.artifacts,
|
|
292
|
+
evidence: input.evidence,
|
|
293
|
+
blocker: input.blocker,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// ── 用户设的 token 预算用尽 → budget_limited ────────────────────
|
|
297
|
+
// finishAttempt 把 'stopped' 统一派生成 Goal.paused(与用户主动 action=pause
|
|
298
|
+
// 共用同一落点);这里识别出「这次 stopped 是因为预算」后再精化一步,
|
|
299
|
+
// 避免「用户暂停」与「系统按预算停」被压成同一个状态、桌面端分不清、
|
|
300
|
+
// 用户也不知道该不该加预算再续。
|
|
301
|
+
if (outcome === 'budget_limited' && finished.status === 'paused') {
|
|
302
|
+
return runtime.transition(
|
|
303
|
+
scope, finished.goalId, finished.revision, 'budget_limited',
|
|
304
|
+
{ reason: input.blocker, actorId },
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ── 服务端配额/授权不给了 → usage_limited ──────────────────────
|
|
309
|
+
// 识别放在这里而不是具体工作流里:usage_limited 是 Goal 外层生命周期,
|
|
310
|
+
// 工作流只报告通用失败信息;Goal 状态本来就归控制器管理。
|
|
311
|
+
//
|
|
312
|
+
// ⚠️ 与 budget_limited 分工不同,别合并:
|
|
313
|
+
// budget_limited = 用户自己设的额度用完 → 加预算即可继续;
|
|
314
|
+
// usage_limited = 服务端配额/授权不给了 → 加预算没用,要去解决授权。
|
|
315
|
+
// attempt 失败会先把 Goal 推到 blocked;识别出配额类原因后再精化成
|
|
316
|
+
// usage_limited(TRANSITIONS 里显式允许这条精化边)。
|
|
317
|
+
if (
|
|
318
|
+
input.status === 'failed' &&
|
|
319
|
+
isUsageLimitError(input.blocker ?? '') &&
|
|
320
|
+
finished.status === 'blocked'
|
|
321
|
+
) {
|
|
322
|
+
return runtime.transition(
|
|
323
|
+
scope, finished.goalId, finished.revision, 'usage_limited',
|
|
324
|
+
{ reason: input.blocker, actorId },
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
return finished;
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
return {
|
|
331
|
+
handled: false,
|
|
332
|
+
events: [],
|
|
333
|
+
inputs: rawInputs,
|
|
334
|
+
execution: managedExecution,
|
|
335
|
+
bridge,
|
|
336
|
+
};
|
|
337
|
+
} catch (caught) {
|
|
338
|
+
return error(
|
|
339
|
+
caught instanceof GoalRuntimeError
|
|
340
|
+
? caught.message
|
|
341
|
+
: `Goal Runtime 失败:${caught instanceof Error ? caught.message : String(caught)}`,
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function renderExecutionAnchor(execution: GoalExecutionContext): string {
|
|
347
|
+
const contract = execution.contract;
|
|
348
|
+
return [
|
|
349
|
+
`🎯 Goal ${execution.goalId} · 契约 v${contract.version}`,
|
|
350
|
+
`目标:${contract.objective}`,
|
|
351
|
+
contract.deliverables.length
|
|
352
|
+
? `交付物:\n${contract.deliverables.map((item) => `- ${item}`).join('\n')}`
|
|
353
|
+
: '',
|
|
354
|
+
contract.acceptanceCriteria.length
|
|
355
|
+
? `验收标准:\n${contract.acceptanceCriteria.map((item) => `- ${item}`).join('\n')}`
|
|
356
|
+
: '',
|
|
357
|
+
contract.scope ? `工作范围:${contract.scope}` : '',
|
|
358
|
+
'只有指定人类可以最终验收;Agent 只能提交可核查证据并进入待验收。',
|
|
359
|
+
].filter(Boolean).join('\n\n');
|
|
360
|
+
}
|