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,317 @@
|
|
|
1
|
+
import { mkdtemp } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from 'bun:test';
|
|
6
|
+
|
|
7
|
+
import { GoalRuntime, GoalStore } from '../../../src/goal/index.js';
|
|
8
|
+
import { prepareGoalCommand } from '../../../src/goal/workflowController.js';
|
|
9
|
+
|
|
10
|
+
async function harness() {
|
|
11
|
+
const root = await mkdtemp(join(tmpdir(), 'goal-controller-'));
|
|
12
|
+
const runtime = new GoalRuntime({ store: new GoalStore({ stateRoot: root }) });
|
|
13
|
+
const options = {
|
|
14
|
+
cwd: root,
|
|
15
|
+
runtime,
|
|
16
|
+
runtimeContext: {
|
|
17
|
+
workspaceId: 'workspace-1', sessionId: 'session-1', cwd: root,
|
|
18
|
+
surface: 'workflow' as const, profile: 'main' as const,
|
|
19
|
+
owner: 'local-owner' as const, memoryAccess: 'read-only' as const,
|
|
20
|
+
interactiveAsk: false,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
return { runtime, options };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('/workflow goal controller', () => {
|
|
27
|
+
test('creates stable Goal/Attempt and rejects an implicit objective replacement', async () => {
|
|
28
|
+
const { options } = await harness();
|
|
29
|
+
const first = await prepareGoalCommand({
|
|
30
|
+
objective: '实现功能', checks: 'shell:bun test',
|
|
31
|
+
group_id: 'group-1', task_id: 'task-1',
|
|
32
|
+
}, options);
|
|
33
|
+
expect(first.handled).toBe(false);
|
|
34
|
+
expect(first.execution?.correlations?.taskId).toBe('task-1');
|
|
35
|
+
const conflict = await prepareGoalCommand({ objective: '换一个目标' }, options);
|
|
36
|
+
expect(conflict.handled).toBe(true);
|
|
37
|
+
expect(conflict.events[0]).toMatchObject({ type: 'error' });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('confirmed workflow contract is versioned and verification stops at human acceptance', async () => {
|
|
41
|
+
const { runtime, options } = await harness();
|
|
42
|
+
const prepared = await prepareGoalCommand({ objective: '交付功能' }, options);
|
|
43
|
+
await prepared.bridge?.updateContract({
|
|
44
|
+
objective: '交付功能', deliverables: ['代码', '说明'], acceptanceCriteria: ['测试通过'],
|
|
45
|
+
});
|
|
46
|
+
const ready = await prepared.bridge?.finish({
|
|
47
|
+
status: 'verified', artifacts: ['out/result.md'], evidence: ['tests pass'],
|
|
48
|
+
}) as import('../../../src/goal/types.js').Goal | undefined;
|
|
49
|
+
expect(ready?.status).toBe('ready_for_acceptance');
|
|
50
|
+
expect(ready?.currentContractVersion).toBe(2);
|
|
51
|
+
const status = await prepareGoalCommand({ action: 'status' }, options);
|
|
52
|
+
expect(status.events[0]).toMatchObject({ type: 'text' });
|
|
53
|
+
const accepted = await prepareGoalCommand({ action: 'accept' }, options);
|
|
54
|
+
expect(accepted.events[0]).toMatchObject({ type: 'text' });
|
|
55
|
+
expect((await runtime.get({ workspaceId: 'workspace-1', sessionId: 'session-1' }))?.status)
|
|
56
|
+
.toBe('accepted');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('pause/resume reuses the stopped Attempt journal identity', async () => {
|
|
60
|
+
const { options } = await harness();
|
|
61
|
+
const first = await prepareGoalCommand({ objective: '长任务' }, options);
|
|
62
|
+
await first.bridge?.finish({ status: 'stopped', blocker: '用户暂停' });
|
|
63
|
+
const resumed = await prepareGoalCommand({ action: 'resume' }, options);
|
|
64
|
+
expect(resumed.handled).toBe(false);
|
|
65
|
+
expect(resumed.execution?.runId).toBe(first.execution?.runId);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* ── 资源记账(A2)─────────────────────────────────────────────────────
|
|
71
|
+
*
|
|
72
|
+
* `recordUsage` 此前**全仓零调用点**:方法定义在 GoalRuntime 上,
|
|
73
|
+
* `GoalResourceUsage` / `budget_limited` / `usage_limited` 全是有形状无执行器,
|
|
74
|
+
* 连桌面端 App.tsx 都已经在渲染两个永远不会发生的状态。这里把通路锁死。
|
|
75
|
+
*/
|
|
76
|
+
describe('goal bridge —— 资源记账', () => {
|
|
77
|
+
test('★ recordUsage 累计进 goal.usage,并回报最新契约的上限', async () => {
|
|
78
|
+
const { runtime, options } = await harness();
|
|
79
|
+
const prepared = await prepareGoalCommand(
|
|
80
|
+
{ objective: '记账测试', checks: 'shell:exit 0' },
|
|
81
|
+
options,
|
|
82
|
+
);
|
|
83
|
+
expect(prepared.bridge?.recordUsage).toBeDefined();
|
|
84
|
+
|
|
85
|
+
const first = await prepared.bridge!.recordUsage!({ inputTokens: 100, outputTokens: 20 });
|
|
86
|
+
expect(first.inputTokens).toBe(100);
|
|
87
|
+
expect(first.outputTokens).toBe(20);
|
|
88
|
+
// 没设预算时上限是 null(不是 0 —— 0 会被判成「一开始就超了」)。
|
|
89
|
+
expect(first.limit).toBeNull();
|
|
90
|
+
|
|
91
|
+
const second = await prepared.bridge!.recordUsage!({ inputTokens: 50, outputTokens: 5 });
|
|
92
|
+
expect(second.inputTokens).toBe(150);
|
|
93
|
+
expect(second.outputTokens).toBe(25);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('★ 用量落进事件流,跨进程读回来还在(不是进程内计数)', async () => {
|
|
97
|
+
const { runtime, options } = await harness();
|
|
98
|
+
const prepared = await prepareGoalCommand(
|
|
99
|
+
{ objective: '持久化测试', checks: 'shell:exit 0' },
|
|
100
|
+
options,
|
|
101
|
+
);
|
|
102
|
+
await prepared.bridge!.recordUsage!({ inputTokens: 777, outputTokens: 88 });
|
|
103
|
+
|
|
104
|
+
// 重新从 store 读(模拟 resume:账本重放不会重新执行 agent 调用,
|
|
105
|
+
// 已花掉的钱必须从持久层拿回来,否则预算闸永远不触发)。
|
|
106
|
+
const scope = { workspaceId: 'workspace-1', sessionId: 'session-1' };
|
|
107
|
+
const reloaded = await runtime.get(scope);
|
|
108
|
+
expect(reloaded?.usage.inputTokens).toBe(777);
|
|
109
|
+
expect(reloaded?.usage.outputTokens).toBe(88);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('★ 改预算 = 新契约版本,已消耗量不重置', async () => {
|
|
113
|
+
const { runtime, options } = await harness();
|
|
114
|
+
const prepared = await prepareGoalCommand(
|
|
115
|
+
{ objective: '预算改版测试', checks: 'shell:exit 0' },
|
|
116
|
+
options,
|
|
117
|
+
);
|
|
118
|
+
await prepared.bridge!.recordUsage!({ inputTokens: 1_000, outputTokens: 100 });
|
|
119
|
+
|
|
120
|
+
const before = await prepared.bridge!.updateContract({
|
|
121
|
+
objective: '预算改版测试',
|
|
122
|
+
tokenBudget: 5_000,
|
|
123
|
+
});
|
|
124
|
+
const after = await prepared.bridge!.recordUsage!({ inputTokens: 1, outputTokens: 1 });
|
|
125
|
+
|
|
126
|
+
expect(before.version).toBeGreaterThan(1);
|
|
127
|
+
expect(after.limit).toBe(5_000);
|
|
128
|
+
// 「加预算继续跑」是自然操作 —— 已花的 1000 不该被偷偷清零。
|
|
129
|
+
expect(after.inputTokens).toBe(1_001);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('★ update 显式改预算时保留其它契约字段', async () => {
|
|
133
|
+
const { runtime, options } = await harness();
|
|
134
|
+
const prepared = await prepareGoalCommand(
|
|
135
|
+
{ objective: '保留契约字段', checks: '测试通过', token_budget: 1_000 },
|
|
136
|
+
options,
|
|
137
|
+
);
|
|
138
|
+
await prepared.bridge!.updateContract({
|
|
139
|
+
objective: '保留契约字段',
|
|
140
|
+
deliverables: ['交付代码'],
|
|
141
|
+
scope: '当前工作区',
|
|
142
|
+
sourceRefs: ['issue-42'],
|
|
143
|
+
tokenBudget: 1_000,
|
|
144
|
+
});
|
|
145
|
+
await prepared.bridge!.finish({ status: 'stopped', blocker: '暂停' });
|
|
146
|
+
|
|
147
|
+
const updated = await prepareGoalCommand(
|
|
148
|
+
{ action: 'update', objective: '保留契约字段(修订)', token_budget: 2_000 },
|
|
149
|
+
options,
|
|
150
|
+
);
|
|
151
|
+
expect(updated.handled).toBe(false);
|
|
152
|
+
const goal = await runtime.get({ workspaceId: 'workspace-1', sessionId: 'session-1' });
|
|
153
|
+
const contract = goal ? runtime.currentContract(goal) : null;
|
|
154
|
+
expect(contract).toMatchObject({
|
|
155
|
+
objective: '保留契约字段(修订)',
|
|
156
|
+
deliverables: ['交付代码'],
|
|
157
|
+
scope: '当前工作区',
|
|
158
|
+
sourceRefs: ['issue-42'],
|
|
159
|
+
tokenBudget: 2_000,
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* ── usage_limited(A5)────────────────────────────────────────────────
|
|
166
|
+
*
|
|
167
|
+
* 这个状态此前只在 TRANSITIONS 与 types 里声明,**没有任何代码能推到它**,
|
|
168
|
+
* 而 minimal-desktop/src/App.tsx 已经在渲染它 —— 一个永远不会发生的 UI 分支。
|
|
169
|
+
*/
|
|
170
|
+
describe('goal bridge —— 服务端配额耗尽落 usage_limited', () => {
|
|
171
|
+
test('★ 配额类失败 → usage_limited(不是笼统的 failed)', async () => {
|
|
172
|
+
const { runtime, options } = await harness();
|
|
173
|
+
const prepared = await prepareGoalCommand(
|
|
174
|
+
{ objective: '配额测试', checks: 'shell:exit 0' },
|
|
175
|
+
options,
|
|
176
|
+
);
|
|
177
|
+
await prepared.bridge!.finish({
|
|
178
|
+
status: 'failed',
|
|
179
|
+
blocker: '在线模型请求失败:TOKEN_QUOTA_EXHAUSTED Token 额度已用尽',
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const goal = await runtime.get({ workspaceId: 'workspace-1', sessionId: 'session-1' });
|
|
183
|
+
expect(goal?.status).toBe('usage_limited');
|
|
184
|
+
// 可恢复:解决授权后能回到 active(TRANSITIONS 允许)。
|
|
185
|
+
expect(goal?.statusReason).toContain('TOKEN_QUOTA_EXHAUSTED');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('★ 普通失败不该被误判成配额问题', async () => {
|
|
189
|
+
const { runtime, options } = await harness();
|
|
190
|
+
const prepared = await prepareGoalCommand(
|
|
191
|
+
{ objective: '普通失败测试', checks: 'shell:exit 0' },
|
|
192
|
+
options,
|
|
193
|
+
);
|
|
194
|
+
await prepared.bridge!.finish({
|
|
195
|
+
status: 'failed',
|
|
196
|
+
blocker: '脚本第 12 行抛错:文件不存在',
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const goal = await runtime.get({ workspaceId: 'workspace-1', sessionId: 'session-1' });
|
|
200
|
+
// 普通失败让 Goal 留在 active(可以再起一次 attempt),不是 usage_limited。
|
|
201
|
+
expect(goal?.status).not.toBe('usage_limited');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* ── budget_limited 真的可达(A3 收口)──────────────────────────────────
|
|
207
|
+
*
|
|
208
|
+
* 复核发现的真实缺口:`budget_limited` 在 TRANSITIONS 和类型里都声明了,
|
|
209
|
+
* `workflows/goal/workflow.mjs` 也确实会把 `status='budget_limited'` 写进 STATUS.json,
|
|
210
|
+
* 但脚本随后统一 `throw new Error(...)`,runner 的通用 catch 一律记 `status:'failed'`,
|
|
211
|
+
* `finishAttempt` 把 failed 派生成 `blocked`——而 TRANSITIONS 里根本没有
|
|
212
|
+
* `blocked → budget_limited` 这条边。整条链路走一遍,Goal 永远停在 blocked,
|
|
213
|
+
* `budget_limited` 从声明到落地之间没有任何代码路径。
|
|
214
|
+
*
|
|
215
|
+
* 修法:工作流在 Error 上挂 `workflowOutcome` 属性传结构化信息 → runner 原样
|
|
216
|
+
* 转发成 `finish({..., outcome})` → 这里按
|
|
217
|
+
* `statusMapping.ts::OUTCOME_TO_ATTEMPT` 精化成 `'stopped'` → `finishAttempt` 派生成
|
|
218
|
+
* `paused` → 再精化成 `budget_limited`(新增的 `paused → budget_limited` 边)。
|
|
219
|
+
*/
|
|
220
|
+
describe('goal bridge —— 预算耗尽落 budget_limited(不是 blocked,也不是普通 paused)', () => {
|
|
221
|
+
test('★ 带 outcome:budget_limited 的 stopped → Goal 落 budget_limited', async () => {
|
|
222
|
+
const { runtime, options } = await harness();
|
|
223
|
+
const prepared = await prepareGoalCommand(
|
|
224
|
+
{ objective: '预算耗尽测试', checks: 'shell:exit 0' },
|
|
225
|
+
options,
|
|
226
|
+
);
|
|
227
|
+
// 模拟 workflow.mjs 的真实调用形状:runner 把脚本 Error 上的 workflowOutcome
|
|
228
|
+
// 原样转发给 bridge.finish,status 恒是 runner 自己的通用默认值 'failed'。
|
|
229
|
+
await prepared.bridge!.finish({
|
|
230
|
+
status: 'failed',
|
|
231
|
+
blocker: '💰 token 预算用尽,已停在轮次边界',
|
|
232
|
+
outcome: 'budget_limited',
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const goal = await runtime.get({ workspaceId: 'workspace-1', sessionId: 'session-1' });
|
|
236
|
+
expect(goal?.status).toBe('budget_limited');
|
|
237
|
+
expect(goal?.status).not.toBe('blocked');
|
|
238
|
+
// 可恢复:TRANSITIONS 允许 budget_limited → active,加预算后能续跑。
|
|
239
|
+
const resumed = await prepareGoalCommand({ action: 'resume' }, options);
|
|
240
|
+
expect(resumed.handled).toBe(false);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test('用户主动暂停(无 outcome)仍然落 paused,不会被误判成 budget_limited', async () => {
|
|
244
|
+
const { runtime, options } = await harness();
|
|
245
|
+
const prepared = await prepareGoalCommand(
|
|
246
|
+
{ objective: '普通暂停测试', checks: 'shell:exit 0' },
|
|
247
|
+
options,
|
|
248
|
+
);
|
|
249
|
+
await prepared.bridge!.finish({ status: 'stopped', blocker: '用户停止,现场已保留' });
|
|
250
|
+
|
|
251
|
+
const goal = await runtime.get({ workspaceId: 'workspace-1', sessionId: 'session-1' });
|
|
252
|
+
expect(goal?.status).toBe('paused');
|
|
253
|
+
expect(goal?.status).not.toBe('budget_limited');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test('未知 outcome 值不精化,退回 runner 传入的 status(防御未来新增结局忘了同步)', async () => {
|
|
257
|
+
const { runtime, options } = await harness();
|
|
258
|
+
const prepared = await prepareGoalCommand(
|
|
259
|
+
{ objective: '未知结局测试', checks: 'shell:exit 0' },
|
|
260
|
+
options,
|
|
261
|
+
);
|
|
262
|
+
await prepared.bridge!.finish({
|
|
263
|
+
status: 'failed', blocker: '未知原因', outcome: 'some_future_outcome',
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const goal = await runtime.get({ workspaceId: 'workspace-1', sessionId: 'session-1' });
|
|
267
|
+
expect(goal?.status).toBe('blocked');
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* ── resume 不再静默清空预算(A4 收口)──────────────────────────────────
|
|
273
|
+
*
|
|
274
|
+
* 复核发现的真实缺口:workflow.mjs 每次都无条件把
|
|
275
|
+
* `Number(args.token_budget) > 0 ? Number(args.token_budget) : null` 写进新契约版本。
|
|
276
|
+
* resume/rework 时如果调用方没有重新传 token_budget(文档说的就是不需要重传),
|
|
277
|
+
* `args.token_budget` 是 undefined → 新契约的预算被静默写成 null(不限),
|
|
278
|
+
* 「用尽后加预算续跑」的断点续跑设计对用户完全不可见地失效了。
|
|
279
|
+
*/
|
|
280
|
+
describe('goal 控制器 —— resume 沿用已有预算,不静默清零', () => {
|
|
281
|
+
test('★ resume 不显式传 token_budget 时,沿用上一版契约的预算', async () => {
|
|
282
|
+
const { runtime, options } = await harness();
|
|
283
|
+
const prepared = await prepareGoalCommand(
|
|
284
|
+
{ objective: '预算续跑测试', checks: 'shell:exit 0' },
|
|
285
|
+
options,
|
|
286
|
+
);
|
|
287
|
+
await prepared.bridge!.updateContract({ objective: '预算续跑测试', tokenBudget: 42_000 });
|
|
288
|
+
await prepared.bridge!.finish({
|
|
289
|
+
status: 'failed', blocker: '预算耗尽', outcome: 'budget_limited',
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// resume 时不传 token_budget —— 这正是文档描述的用户操作:
|
|
293
|
+
// 「加预算后用同一目标重跑即可从这里继续」,不改预算就不用重复传参。
|
|
294
|
+
const resumed = await prepareGoalCommand({ action: 'resume' }, options);
|
|
295
|
+
expect(resumed.inputs.token_budget).toBe(42_000);
|
|
296
|
+
|
|
297
|
+
const goal = await runtime.get({ workspaceId: 'workspace-1', sessionId: 'session-1' });
|
|
298
|
+
expect(goal?.status).toBe('active');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test('resume 显式传新预算时,新值优先于旧契约的预算', async () => {
|
|
302
|
+
const { options } = await harness();
|
|
303
|
+
const prepared = await prepareGoalCommand(
|
|
304
|
+
{ objective: '预算改值测试', checks: 'shell:exit 0' },
|
|
305
|
+
options,
|
|
306
|
+
);
|
|
307
|
+
await prepared.bridge!.updateContract({ objective: '预算改值测试', tokenBudget: 1_000 });
|
|
308
|
+
await prepared.bridge!.finish({
|
|
309
|
+
status: 'failed', blocker: '预算耗尽', outcome: 'budget_limited',
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const resumed = await prepareGoalCommand(
|
|
313
|
+
{ action: 'resume', token_budget: 99_000 }, options,
|
|
314
|
+
);
|
|
315
|
+
expect(resumed.inputs.token_budget).toBe(99_000);
|
|
316
|
+
});
|
|
317
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/workflow-runner/test/journalLanes.test.ts —— 并发下账本定位必须确定
|
|
3
|
+
*
|
|
4
|
+
* 守的是一条**实测存在过的静默降级**:
|
|
5
|
+
*
|
|
6
|
+
* 账本序号原本在拿到并发闸(上限 8)之后才自增,于是扇出超过 8 项时,第 9 项的
|
|
7
|
+
* 序号取决于前 8 项谁先返回 —— 也就是取决于 LLM 延迟。同一个脚本两次运行的序号
|
|
8
|
+
* 分配可能完全不同 → 首个失配 → 后面全部实跑,**断点续跑静默失效**。
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ **所以这里的扇出规模必须大于并发上限**。N ≤ 8 时所有项同时起跑、顺序恰好稳定,
|
|
11
|
+
* 用例恒绿,等于没测 —— 这与上一轮 PDF 那条「144 DPI 恰好等于 scale:2,尺寸判据
|
|
12
|
+
* 撤掉修复照样绿」是同一类假保证。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { afterEach, describe, expect, test } from 'bun:test';
|
|
16
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
17
|
+
import { tmpdir } from 'node:os';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
|
|
20
|
+
import { Journal, MAIN_LANE } from '../src/journal.js';
|
|
21
|
+
|
|
22
|
+
const dirs: string[] = [];
|
|
23
|
+
function tmpJournal(): string {
|
|
24
|
+
const d = mkdtempSync(join(tmpdir(), 'wf-lane-'));
|
|
25
|
+
dirs.push(d);
|
|
26
|
+
return join(d, 'journal.jsonl');
|
|
27
|
+
}
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
for (const d of dirs.splice(0)) {
|
|
30
|
+
try {
|
|
31
|
+
rmSync(d, { recursive: true, force: true, maxRetries: 3 });
|
|
32
|
+
} catch {
|
|
33
|
+
/* Windows 偶发 EBUSY */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/** 制造「完成顺序与发起顺序不同」的调度:下标越大越快返回。 */
|
|
39
|
+
const reversedDelay = (i: number, n: number) =>
|
|
40
|
+
new Promise((r) => setTimeout(r, (n - i) * 2));
|
|
41
|
+
|
|
42
|
+
describe('车道让并发下的定位与调度无关', () => {
|
|
43
|
+
test('★ 扇出 20 项(> 并发上限 8)乱序完成,第二次仍 100% 重放', async () => {
|
|
44
|
+
const path = tmpJournal();
|
|
45
|
+
const N = 20;
|
|
46
|
+
|
|
47
|
+
const run1 = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
48
|
+
await run1.load();
|
|
49
|
+
await Promise.all(
|
|
50
|
+
Array.from({ length: N }, async (_, i) => {
|
|
51
|
+
await reversedDelay(i, N);
|
|
52
|
+
return run1.step('agent', { i }, async () => `r${i}`, `main/p1i${i}`);
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// 第二次:换一种完成顺序(正序),车道定位不该受影响
|
|
57
|
+
const run2 = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
58
|
+
await run2.load();
|
|
59
|
+
const results = await Promise.all(
|
|
60
|
+
Array.from({ length: N }, async (_, i) => {
|
|
61
|
+
await new Promise((r) => setTimeout(r, i * 2));
|
|
62
|
+
return run2.step(
|
|
63
|
+
'agent',
|
|
64
|
+
{ i },
|
|
65
|
+
async (): Promise<string> => {
|
|
66
|
+
throw new Error(`第 ${i} 项不该实跑`);
|
|
67
|
+
},
|
|
68
|
+
`main/p1i${i}`,
|
|
69
|
+
);
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
expect(results).toEqual(Array.from({ length: N }, (_, i) => `r${i}`));
|
|
74
|
+
expect(run2.replayedCount).toBe(N);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('★ 对照:同一条车道上并发 20 项 —— 定位就会错乱(这正是不能裸 Promise.all 的原因)', async () => {
|
|
78
|
+
const path = tmpJournal();
|
|
79
|
+
const N = 20;
|
|
80
|
+
|
|
81
|
+
const run1 = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
82
|
+
await run1.load();
|
|
83
|
+
await Promise.all(
|
|
84
|
+
Array.from({ length: N }, async (_, i) => {
|
|
85
|
+
await reversedDelay(i, N);
|
|
86
|
+
return run1.step('agent', { i }, async () => `r${i}`, MAIN_LANE);
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const run2 = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
91
|
+
await run2.load();
|
|
92
|
+
let liveRuns = 0;
|
|
93
|
+
await Promise.all(
|
|
94
|
+
Array.from({ length: N }, async (_, i) => {
|
|
95
|
+
await new Promise((r) => setTimeout(r, i * 2));
|
|
96
|
+
return run2.step(
|
|
97
|
+
'agent',
|
|
98
|
+
{ i },
|
|
99
|
+
async () => {
|
|
100
|
+
liveRuns++;
|
|
101
|
+
return `r${i}`;
|
|
102
|
+
},
|
|
103
|
+
MAIN_LANE,
|
|
104
|
+
);
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// 顺序变了 → 序号与内容对不上 → 大量实跑。这条断言故意宽松:
|
|
109
|
+
// 要证明的是「会退化」,不是退化到某个精确数字。
|
|
110
|
+
expect(liveRuns).toBeGreaterThan(0);
|
|
111
|
+
expect(run2.replayedCount).toBeLessThan(N);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('一条车道失配只作废自己,不牵连兄弟车道', async () => {
|
|
115
|
+
const path = tmpJournal();
|
|
116
|
+
const run1 = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
117
|
+
await run1.load();
|
|
118
|
+
for (const i of [0, 1]) {
|
|
119
|
+
await run1.step('agent', { i, s: 1 }, async () => `a${i}`, `main/p1i${i}`);
|
|
120
|
+
await run1.step('agent', { i, s: 2 }, async () => `b${i}`, `main/p1i${i}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const run2 = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
124
|
+
await run2.load();
|
|
125
|
+
// 第 0 项第一阶段内容变了 → 只有 0 号车道分叉
|
|
126
|
+
const a0 = await run2.step('agent', { i: 0, s: 99 }, async () => 'NEW', 'main/p1i0');
|
|
127
|
+
const b0 = await run2.step('agent', { i: 0, s: 2 }, async () => 'LIVE0', 'main/p1i0');
|
|
128
|
+
const a1 = await run2.step('agent', { i: 1, s: 1 }, async () => 'X', 'main/p1i1');
|
|
129
|
+
const b1 = await run2.step('agent', { i: 1, s: 2 }, async () => 'X', 'main/p1i1');
|
|
130
|
+
|
|
131
|
+
expect(a0).toBe('NEW');
|
|
132
|
+
expect(b0).toBe('LIVE0'); // 同车道后续作废 → 实跑
|
|
133
|
+
expect(a1).toBe('a1'); // 兄弟车道不受影响 → 重放
|
|
134
|
+
expect(b1).toBe('b1');
|
|
135
|
+
expect(run2.laneDiverged('main/p1i0')).toBe(true);
|
|
136
|
+
expect(run2.laneDiverged('main/p1i1')).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('★ 主车道保持「首个失配即作废其后」—— 这条安全属性不许丢', async () => {
|
|
140
|
+
// 反例:run1 产出 plan v1 → 审查得 R2;run2 第一步产出 v2,那句审查的
|
|
141
|
+
// 指纹一模一样(路径是常量)。若按内容寻址,R2 会被当成对 v2 的审查返回。
|
|
142
|
+
const path = tmpJournal();
|
|
143
|
+
const run1 = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
144
|
+
await run1.load();
|
|
145
|
+
await run1.step('agent', { p: '写计划' }, async () => 'plan v1');
|
|
146
|
+
await run1.step('agent', { p: '审查 out/plan.md' }, async () => '审查(v1)');
|
|
147
|
+
|
|
148
|
+
const run2 = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
149
|
+
await run2.load();
|
|
150
|
+
await run2.step('agent', { p: '写计划(改了提示词)' }, async () => 'plan v2');
|
|
151
|
+
const review = await run2.step(
|
|
152
|
+
'agent',
|
|
153
|
+
{ p: '审查 out/plan.md' }, // 指纹与上次完全相同
|
|
154
|
+
async () => '审查(v2)',
|
|
155
|
+
);
|
|
156
|
+
expect(review).toBe('审查(v2)'); // 必须实跑,不得拿 v1 的审查冒充
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('v1 账本不会被 V2 重放', async () => {
|
|
160
|
+
const path = tmpJournal();
|
|
161
|
+
const { writeFile, mkdir } = await import('node:fs/promises');
|
|
162
|
+
const { dirname } = await import('node:path');
|
|
163
|
+
const { fingerprint } = await import('../src/journal.js');
|
|
164
|
+
await mkdir(dirname(path), { recursive: true });
|
|
165
|
+
await writeFile(
|
|
166
|
+
path,
|
|
167
|
+
[
|
|
168
|
+
JSON.stringify({ seq: 1, kind: 'tool', fp: fingerprint('tool', { a: 1 }), result: 'old1' }),
|
|
169
|
+
JSON.stringify({ seq: 2, kind: 'tool', fp: fingerprint('tool', { a: 2 }), result: 'old2' }),
|
|
170
|
+
].join('\n') + '\n',
|
|
171
|
+
'utf8',
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const j = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
175
|
+
await j.load();
|
|
176
|
+
expect(await j.step('tool', { a: 1 }, async () => 'live1')).toBe('live1');
|
|
177
|
+
expect(await j.step('tool', { a: 2 }, async () => 'live2')).toBe('live2');
|
|
178
|
+
expect(j.replayedCount).toBe(0);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test('replaying 判据是「有没有实跑过」,并发下也成立', async () => {
|
|
182
|
+
const path = tmpJournal();
|
|
183
|
+
const run1 = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
184
|
+
await run1.load();
|
|
185
|
+
await run1.step('tool', { a: 1 }, async () => 'r1', 'main/p1i0');
|
|
186
|
+
await run1.step('tool', { a: 2 }, async () => 'r2', 'main/p1i1');
|
|
187
|
+
|
|
188
|
+
const run2 = new Journal(path, { bundleHash: 'test', rootRunId: 'test-root' });
|
|
189
|
+
await run2.load();
|
|
190
|
+
expect(run2.replaying).toBe(true);
|
|
191
|
+
// 先命中一次重放(乱序:先跑 1 号车道)
|
|
192
|
+
await run2.step('tool', { a: 2 }, async () => 'x', 'main/p1i1');
|
|
193
|
+
expect(run2.replaying).toBe(true);
|
|
194
|
+
// 一旦有实跑就翻
|
|
195
|
+
await run2.step('tool', { a: 999 }, async () => 'live');
|
|
196
|
+
expect(run2.replaying).toBe(false);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/workflow-runner/test/listResources.test.ts
|
|
3
|
+
* —— 守 `skills/workflow-creator/scripts/list-resources.mjs` 的准确性
|
|
4
|
+
*
|
|
5
|
+
* 这个脚本的**全部价值就是准确**:agent 照着它写 `allowedTools`,写错的名字会被
|
|
6
|
+
* 引擎静默过滤掉。一份不准的清单比没有清单更糟 —— 它会让人理直气壮地写错。
|
|
7
|
+
*
|
|
8
|
+
* 第一版就栽在这里:从**变量名**推断工具名(`lspTool` → `Lsp`、
|
|
9
|
+
* `webFetchTool` → `Webfetch`、`genImageTool` → `GenImage`),三个全错,
|
|
10
|
+
* 真名是 `LSP` / `WebFetch` / `GenerateImage`。现在改成直接问内核要 `name` 字段。
|
|
11
|
+
*
|
|
12
|
+
* 另有两个 Windows 专属的坑(spawn 到 shell 时命令被截断),见脚本里的注释。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, expect, test } from 'bun:test';
|
|
16
|
+
import { spawnSync } from 'node:child_process';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
|
|
19
|
+
import { ALL_TOOLS } from '../../../src/tools/index.js';
|
|
20
|
+
|
|
21
|
+
const ROOT = join(import.meta.dir, '..', '..', '..');
|
|
22
|
+
const SCRIPT = join(
|
|
23
|
+
ROOT,
|
|
24
|
+
'skills',
|
|
25
|
+
'workflow-creator',
|
|
26
|
+
'scripts',
|
|
27
|
+
'list-resources.mjs',
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
function runScript(args: string[] = []) {
|
|
31
|
+
const r = spawnSync('node', [SCRIPT, ...args], {
|
|
32
|
+
cwd: ROOT,
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
shell: process.platform === 'win32',
|
|
35
|
+
});
|
|
36
|
+
return { status: r.status, stdout: r.stdout ?? '', stderr: r.stderr ?? '' };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('list-resources.mjs', () => {
|
|
40
|
+
test('跑得起来(node 直接 spawn,不需要 bun 在 PATH 里也不该崩)', () => {
|
|
41
|
+
const r = runScript(['--plugins']);
|
|
42
|
+
expect(r.status).toBe(0);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('★ 工具名与 ALL_TOOLS 的真实 name 字段**完全一致**', () => {
|
|
46
|
+
// 一个字母都不能差 —— 差了就会被 allowedTools 静默过滤。
|
|
47
|
+
const r = runScript(['--json']);
|
|
48
|
+
expect(r.status).toBe(0);
|
|
49
|
+
const data = JSON.parse(r.stdout) as {
|
|
50
|
+
tools: string[];
|
|
51
|
+
toolsExact: boolean;
|
|
52
|
+
};
|
|
53
|
+
if (!data.toolsExact) {
|
|
54
|
+
// 拿不到真值时脚本必须**明说降级**而不是给一份猜的清单
|
|
55
|
+
expect(data.tools).toEqual([]);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
expect([...data.tools].sort()).toEqual(ALL_TOOLS.map((t) => t.name).sort());
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('★ 那三个曾被推断错的名字必须正确', () => {
|
|
62
|
+
// LSP / WebFetch / GenerateImage —— 变量名分别是 lspTool / webFetchTool / genImageTool,
|
|
63
|
+
// 按变量名推断会得到 Lsp / Webfetch / GenImage,全错。
|
|
64
|
+
const r = runScript(['--json']);
|
|
65
|
+
const data = JSON.parse(r.stdout) as {
|
|
66
|
+
tools: string[];
|
|
67
|
+
toolsExact: boolean;
|
|
68
|
+
};
|
|
69
|
+
if (!data.toolsExact) return;
|
|
70
|
+
for (const name of ['LSP', 'WebFetch', 'GenerateImage', 'WebBrowser']) {
|
|
71
|
+
expect(data.tools).toContain(name);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('skills 清单非空且带描述(否则 agent 判断不了该用哪个)', () => {
|
|
76
|
+
const r = runScript(['--json']);
|
|
77
|
+
const data = JSON.parse(r.stdout) as {
|
|
78
|
+
skills: { name: string; description: string }[];
|
|
79
|
+
};
|
|
80
|
+
expect(data.skills.length).toBeGreaterThan(10);
|
|
81
|
+
// 至少多数条目要有描述 —— 全空说明 frontmatter 解析坏了
|
|
82
|
+
const withDesc = data.skills.filter((s) => s.description.length > 0);
|
|
83
|
+
expect(withDesc.length).toBeGreaterThan(data.skills.length / 2);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('★ 一定要列出 workflow-runner 与 computer_use(最容易被忽略的两个入口)', () => {
|
|
87
|
+
const r = runScript(['--json']);
|
|
88
|
+
const data = JSON.parse(r.stdout) as {
|
|
89
|
+
plugins: { name: string; commands: string[] }[];
|
|
90
|
+
};
|
|
91
|
+
const names = data.plugins.map((p) => p.name);
|
|
92
|
+
expect(names).toContain('workflow-runner');
|
|
93
|
+
// computer_use 的 plugin.json 里 name 写的是 computer-use(连字符)
|
|
94
|
+
expect(names.some((n) => n.replace('_', '-') === 'computer-use')).toBe(
|
|
95
|
+
true,
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('人类可读输出里工具按用途归组,不是一坨字母序', () => {
|
|
100
|
+
const r = runScript(['--tools']);
|
|
101
|
+
if (!r.stdout.includes('═══ 工具')) return; // bun 不可用时的降级路径
|
|
102
|
+
expect(r.stdout).toContain('读写文件');
|
|
103
|
+
expect(r.stdout).toContain('桌面操控');
|
|
104
|
+
expect(r.stdout).toContain('ComputerUse');
|
|
105
|
+
});
|
|
106
|
+
});
|