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
package/src/tools/agent/agent.ts
CHANGED
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
worktreeChangedFiles,
|
|
60
60
|
worktreeDiffStat,
|
|
61
61
|
} from './worktree.js';
|
|
62
|
-
import { appendAgentEvent, loadAgentRecord, saveAgentRecord, type AgentTaskRecord, type LiveAgentTask } from './agentTaskService.js';
|
|
62
|
+
import { appendAgentEvent, attachAgentHistory, loadAgentRecord, saveAgentRecord, type AgentTaskRecord, type LiveAgentTask } from './agentTaskService.js';
|
|
63
63
|
|
|
64
64
|
// ---------------- 0. 环内模块懒加载(见文件头「循环依赖纪律」) ----------------
|
|
65
65
|
|
|
@@ -120,7 +120,7 @@ const inputSchema = z.object({
|
|
|
120
120
|
run_in_background: z.boolean().optional().describe('立即返回 agent_id,子任务在后台运行;用 TaskOutput/TaskStop 管理'),
|
|
121
121
|
resume_agent_id: z.string().regex(/^agent_[0-9a-f-]{36}$/i).optional().describe('继续当前会话中此前完成、中断或失败的 Agent 历史'),
|
|
122
122
|
});
|
|
123
|
-
type AgentInput = z.infer<typeof inputSchema>;
|
|
123
|
+
export type AgentInput = z.infer<typeof inputSchema>;
|
|
124
124
|
|
|
125
125
|
const parameters = toToolParameters(inputSchema);
|
|
126
126
|
|
|
@@ -227,7 +227,8 @@ export interface AgentTaskDeps {
|
|
|
227
227
|
chat?: RunQueryOptions['chat'];
|
|
228
228
|
executeTool?: RunQueryOptions['executeTool'];
|
|
229
229
|
initialHistory?: RunQueryOptions['history'];
|
|
230
|
-
captureHistory?: (history:RunQueryOptions['history'])=>void|Promise<void>;
|
|
230
|
+
captureHistory?: (history: RunQueryOptions['history']) => void | Promise<void>;
|
|
231
|
+
onHistoryReady?: (history: RunQueryOptions['history']) => void;
|
|
231
232
|
existingWorktree?: AgentWorktree;
|
|
232
233
|
onWorktreeCreated?: (worktree: AgentWorktree) => void | Promise<void>;
|
|
233
234
|
}
|
|
@@ -296,7 +297,10 @@ export async function runAgentTask(
|
|
|
296
297
|
const { runQuery, ALL_TOOLS, executeTool } = await loadCore();
|
|
297
298
|
|
|
298
299
|
const def = resolveAgentType(input.agent_type);
|
|
299
|
-
// 轮数上限:AGENT_TOOL_MAX_TURNS
|
|
300
|
+
// 轮数上限:AGENT_TOOL_MAX_TURNS 可统一覆盖两种类型。默认值的唯一真值在
|
|
301
|
+
// config/configDefaults.ts::DEFAULT_SUBAGENTS_CONFIG(现为 explore 200 / general 100,
|
|
302
|
+
// 那里写了「只读的给宽、能写的给窄」的理由)。这条注释以前写着「explore 50」,
|
|
303
|
+
// 而代码早已是 200 —— 注释里复述数字必然漂移,所以现在只留指针。
|
|
300
304
|
const maxTurns = positiveInt(process.env.AGENT_TOOL_MAX_TURNS)
|
|
301
305
|
?? (def.type === 'explore' ? ctx.subagentConfig?.exploreMaxTurns : ctx.subagentConfig?.generalMaxTurns)
|
|
302
306
|
?? def.maxTurns;
|
|
@@ -377,6 +381,7 @@ export async function runAgentTask(
|
|
|
377
381
|
const history: RunQueryOptions['history'] = deps.initialHistory?.length ? [...deps.initialHistory] : [
|
|
378
382
|
{ role: 'system', content: await buildSubSystemPrompt(cwd, def, subTools) },
|
|
379
383
|
];
|
|
384
|
+
deps.onHistoryReady?.(history);
|
|
380
385
|
const historyLenBefore = history.length;
|
|
381
386
|
const maxConcurrent = positiveInt(process.env.AGENT_TOOL_MAX_CONCURRENT) ?? ctx.subagentConfig?.maxConcurrent ?? 3;
|
|
382
387
|
let releaseLease: (() => void) | null = null;
|
|
@@ -469,8 +474,8 @@ export async function runAgentTask(
|
|
|
469
474
|
let parsed: Record<string, unknown> = {};
|
|
470
475
|
try { parsed = JSON.parse(args) as Record<string, unknown>; } catch { /* executeTool will report malformed input */ }
|
|
471
476
|
const rawPath = typeof parsed.file_path === 'string' ? parsed.file_path : typeof parsed.path === 'string' ? parsed.path : undefined;
|
|
472
|
-
if (def.type === 'general' && !wt &&
|
|
473
|
-
return { ok: false as const, error:
|
|
477
|
+
if (def.type === 'general' && !wt && ['Bash', 'ApplyPatch'].includes(name) && writeScopes[0] !== rootScope[0]) {
|
|
478
|
+
return { ok: false as const, error: `声明了局部 write_scopes 时禁止 ${name}:无法可靠证明一次调用只写入批准范围。请改用单文件工具或使用整个工作目录范围。` };
|
|
474
479
|
}
|
|
475
480
|
if (def.type === 'general' && rawPath) {
|
|
476
481
|
if (isAbsolute(rawPath)) return { ok: false as const, error: `${name} 必须使用工作目录相对路径:${rawPath}` };
|
|
@@ -483,7 +488,14 @@ export async function runAgentTask(
|
|
|
483
488
|
changedFiles.add(relative(cwd, resolve(cwd, rawPath)).split(sep).join('/'));
|
|
484
489
|
}
|
|
485
490
|
}
|
|
486
|
-
|
|
491
|
+
const result = await baseExecute(name, args, s, c);
|
|
492
|
+
if (result.metadata?.kind === 'file_activity') {
|
|
493
|
+
for (const file of result.metadata.files) {
|
|
494
|
+
if (['created', 'modified', 'generated', 'deleted'].includes(file.activity))
|
|
495
|
+
changedFiles.add(file.relativePath);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return result;
|
|
487
499
|
};
|
|
488
500
|
|
|
489
501
|
// ---- 事件消费:子 LoopEvent 一律不外泄,折算成统计 + 步内进度 ----
|
|
@@ -737,6 +749,7 @@ export async function runManagedAgentTask(
|
|
|
737
749
|
abort,
|
|
738
750
|
completion,
|
|
739
751
|
events: new EventEmitter(),
|
|
752
|
+
inbox: [],
|
|
740
753
|
};
|
|
741
754
|
ctx.sessionState.agentTasks.tasks.set(id, task);
|
|
742
755
|
await saveAgentRecord(ctx, task);
|
|
@@ -769,6 +782,7 @@ export async function runManagedAgentTask(
|
|
|
769
782
|
await saveAgentRecord(ctx, task);
|
|
770
783
|
},
|
|
771
784
|
captureHistory: (history) => { captured = [...history]; },
|
|
785
|
+
onHistoryReady: (history) => attachAgentHistory(task, history),
|
|
772
786
|
});
|
|
773
787
|
task.result = result;
|
|
774
788
|
task.history = captured;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { toToolParameters } from '../../utils/zodToJson.js';
|
|
3
|
+
import { runManagedAgentTask } from './agent.js';
|
|
4
|
+
import { appendAgentEvent, enqueueAgentMessage, getLiveAgentTask, listAgentRecords, loadAgentRecord, saveAgentRecord, } from './agentTaskService.js';
|
|
5
|
+
const agentId = z.string().regex(/^agent_[0-9a-f-]{36}$/i);
|
|
6
|
+
function needsContext(context) {
|
|
7
|
+
if (!context.sessionState)
|
|
8
|
+
return { ok: false, error: 'Agent 控制工具需要当前 Session 运行状态。' };
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
function snapshot(record) {
|
|
12
|
+
return {
|
|
13
|
+
agent_id: record.id,
|
|
14
|
+
status: record.status,
|
|
15
|
+
description: record.description,
|
|
16
|
+
progress: record.progress,
|
|
17
|
+
created_at: record.createdAt,
|
|
18
|
+
updated_at: record.updatedAt,
|
|
19
|
+
result: record.result
|
|
20
|
+
? record.result.ok
|
|
21
|
+
? record.result.content.slice(0, 4_000)
|
|
22
|
+
: `Error: ${record.result.error.slice(0, 4_000)}`
|
|
23
|
+
: undefined,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const messageSchema = z.object({
|
|
27
|
+
agent_id: agentId,
|
|
28
|
+
message: z.string().min(1).max(100_000),
|
|
29
|
+
});
|
|
30
|
+
export const agentMessageTool = {
|
|
31
|
+
name: 'AgentMessage',
|
|
32
|
+
description: 'Sends additional context or a correction to one running sub-agent by stable agent_id. ' +
|
|
33
|
+
'The message is inserted at the next model boundary without cancelling its current tool call. ' +
|
|
34
|
+
'Use AgentFollowup instead when the agent is already idle or completed.',
|
|
35
|
+
inputSchema: messageSchema,
|
|
36
|
+
parameters: toToolParameters(messageSchema),
|
|
37
|
+
isReadOnly: false,
|
|
38
|
+
isConcurrencySafe: true,
|
|
39
|
+
call: async () => ({
|
|
40
|
+
ok: false,
|
|
41
|
+
error: 'AgentMessage requires session context',
|
|
42
|
+
}),
|
|
43
|
+
callWithContext: async (input, _signal, context) => {
|
|
44
|
+
const missing = needsContext(context);
|
|
45
|
+
if (missing)
|
|
46
|
+
return missing;
|
|
47
|
+
const task = getLiveAgentTask(context.sessionState.agentTasks, input.agent_id);
|
|
48
|
+
if (!task || task.status !== 'running') {
|
|
49
|
+
const saved = await loadAgentRecord(context, input.agent_id);
|
|
50
|
+
return saved
|
|
51
|
+
? {
|
|
52
|
+
ok: false,
|
|
53
|
+
error: `Agent ${input.agent_id} 当前为 ${saved.status};请使用 AgentFollowup 启动后续回合。`,
|
|
54
|
+
}
|
|
55
|
+
: { ok: false, error: `当前 Session 中不存在 Agent ${input.agent_id}` };
|
|
56
|
+
}
|
|
57
|
+
enqueueAgentMessage(task, input.message);
|
|
58
|
+
await saveAgentRecord(context, task);
|
|
59
|
+
await appendAgentEvent(context, task.id, {
|
|
60
|
+
phase: 'message',
|
|
61
|
+
message: input.message,
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
ok: true,
|
|
65
|
+
content: `消息已发送给 ${task.id},将在下一个模型边界生效。`,
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
const listSchema = z.object({
|
|
70
|
+
include_completed: z.boolean().optional(),
|
|
71
|
+
});
|
|
72
|
+
export const agentListTool = {
|
|
73
|
+
name: 'AgentList',
|
|
74
|
+
description: 'Lists sub-agents owned by the current Session, including stable IDs, status and latest progress.',
|
|
75
|
+
inputSchema: listSchema,
|
|
76
|
+
parameters: toToolParameters(listSchema),
|
|
77
|
+
isReadOnly: true,
|
|
78
|
+
isConcurrencySafe: true,
|
|
79
|
+
call: async () => ({
|
|
80
|
+
ok: false,
|
|
81
|
+
error: 'AgentList requires session context',
|
|
82
|
+
}),
|
|
83
|
+
callWithContext: async (input, _signal, context) => {
|
|
84
|
+
const missing = needsContext(context);
|
|
85
|
+
if (missing)
|
|
86
|
+
return missing;
|
|
87
|
+
const records = (await listAgentRecords(context)).filter((record) => input.include_completed !== false || record.status === 'running');
|
|
88
|
+
return {
|
|
89
|
+
ok: true,
|
|
90
|
+
content: records.length
|
|
91
|
+
? JSON.stringify(records.map(snapshot), null, 2)
|
|
92
|
+
: '当前 Session 没有符合条件的子 Agent。',
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
const waitSchema = z.object({
|
|
97
|
+
agent_ids: z.array(agentId).min(1).max(8),
|
|
98
|
+
timeout_ms: z.number().int().min(0).max(120_000).optional(),
|
|
99
|
+
});
|
|
100
|
+
export const agentWaitTool = {
|
|
101
|
+
name: 'AgentWait',
|
|
102
|
+
description: 'Waits efficiently until any selected running sub-agent finishes or the timeout expires, then returns fresh snapshots for all selected IDs. Do not poll repeatedly.',
|
|
103
|
+
inputSchema: waitSchema,
|
|
104
|
+
parameters: toToolParameters(waitSchema),
|
|
105
|
+
isReadOnly: true,
|
|
106
|
+
isConcurrencySafe: true,
|
|
107
|
+
interruptBehavior: () => 'cancel',
|
|
108
|
+
call: async () => ({
|
|
109
|
+
ok: false,
|
|
110
|
+
error: 'AgentWait requires session context',
|
|
111
|
+
}),
|
|
112
|
+
callWithContext: async (input, signal, context) => {
|
|
113
|
+
const missing = needsContext(context);
|
|
114
|
+
if (missing)
|
|
115
|
+
return missing;
|
|
116
|
+
const live = input.agent_ids
|
|
117
|
+
.map((id) => getLiveAgentTask(context.sessionState.agentTasks, id))
|
|
118
|
+
.filter((task) => Boolean(task?.status === 'running'));
|
|
119
|
+
const timeoutMs = input.timeout_ms ?? 30_000;
|
|
120
|
+
if (live.length && timeoutMs > 0) {
|
|
121
|
+
let timer;
|
|
122
|
+
let onAbort;
|
|
123
|
+
const timeout = new Promise((resolve) => {
|
|
124
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
125
|
+
timer.unref?.();
|
|
126
|
+
});
|
|
127
|
+
const aborted = new Promise((resolve) => {
|
|
128
|
+
if (!signal)
|
|
129
|
+
return;
|
|
130
|
+
onAbort = () => resolve();
|
|
131
|
+
if (signal.aborted)
|
|
132
|
+
resolve();
|
|
133
|
+
else
|
|
134
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
135
|
+
});
|
|
136
|
+
await Promise.race([
|
|
137
|
+
...live.map((task) => task.completion),
|
|
138
|
+
timeout,
|
|
139
|
+
aborted,
|
|
140
|
+
]);
|
|
141
|
+
if (timer)
|
|
142
|
+
clearTimeout(timer);
|
|
143
|
+
if (signal && onAbort)
|
|
144
|
+
signal.removeEventListener('abort', onAbort);
|
|
145
|
+
if (signal?.aborted)
|
|
146
|
+
return { ok: false, error: 'aborted' };
|
|
147
|
+
}
|
|
148
|
+
const records = [];
|
|
149
|
+
const missingIds = [];
|
|
150
|
+
for (const id of input.agent_ids) {
|
|
151
|
+
const record = getLiveAgentTask(context.sessionState.agentTasks, id) ??
|
|
152
|
+
(await loadAgentRecord(context, id));
|
|
153
|
+
if (record)
|
|
154
|
+
records.push(record);
|
|
155
|
+
else
|
|
156
|
+
missingIds.push(id);
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
ok: true,
|
|
160
|
+
content: JSON.stringify({ agents: records.map(snapshot), missing: missingIds }, null, 2),
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
const interruptSchema = z.object({ agent_id: agentId });
|
|
165
|
+
export const agentInterruptTool = {
|
|
166
|
+
name: 'AgentInterrupt',
|
|
167
|
+
description: 'Interrupts one running sub-agent owned by the current Session. Its private history, progress and worktree are preserved for a later AgentFollowup.',
|
|
168
|
+
inputSchema: interruptSchema,
|
|
169
|
+
parameters: toToolParameters(interruptSchema),
|
|
170
|
+
isReadOnly: false,
|
|
171
|
+
isConcurrencySafe: true,
|
|
172
|
+
call: async () => ({
|
|
173
|
+
ok: false,
|
|
174
|
+
error: 'AgentInterrupt requires session context',
|
|
175
|
+
}),
|
|
176
|
+
callWithContext: async (input, _signal, context) => {
|
|
177
|
+
const missing = needsContext(context);
|
|
178
|
+
if (missing)
|
|
179
|
+
return missing;
|
|
180
|
+
const live = getLiveAgentTask(context.sessionState.agentTasks, input.agent_id);
|
|
181
|
+
if (!live || live.status !== 'running') {
|
|
182
|
+
const saved = await loadAgentRecord(context, input.agent_id);
|
|
183
|
+
return saved
|
|
184
|
+
? {
|
|
185
|
+
ok: true,
|
|
186
|
+
content: `Agent ${input.agent_id} 已经是 ${saved.status},未执行中断。`,
|
|
187
|
+
}
|
|
188
|
+
: { ok: false, error: `当前 Session 中不存在 Agent ${input.agent_id}` };
|
|
189
|
+
}
|
|
190
|
+
const previous = live.status;
|
|
191
|
+
live.abort.abort(new Error('interrupted-by-parent-agent'));
|
|
192
|
+
await Promise.race([
|
|
193
|
+
live.completion,
|
|
194
|
+
new Promise((resolve) => setTimeout(resolve, 5_000)),
|
|
195
|
+
]);
|
|
196
|
+
return {
|
|
197
|
+
ok: true,
|
|
198
|
+
content: `已中断 ${live.id}(先前状态:${previous};当前状态:${live.status})。`,
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
const followupSchema = z.object({
|
|
203
|
+
agent_id: agentId,
|
|
204
|
+
message: z.string().min(1).max(100_000),
|
|
205
|
+
run_in_background: z.boolean().optional(),
|
|
206
|
+
});
|
|
207
|
+
export const agentFollowupTool = {
|
|
208
|
+
name: 'AgentFollowup',
|
|
209
|
+
description: 'Adds a follow-up task to a stable sub-agent. If it is running, delivers the message at the next model boundary; if idle, resumes its persisted private history and starts a new turn.',
|
|
210
|
+
inputSchema: followupSchema,
|
|
211
|
+
parameters: toToolParameters(followupSchema),
|
|
212
|
+
isReadOnly: false,
|
|
213
|
+
isConcurrencySafe: true,
|
|
214
|
+
interruptBehavior: () => 'block',
|
|
215
|
+
call: async () => ({
|
|
216
|
+
ok: false,
|
|
217
|
+
error: 'AgentFollowup requires session context',
|
|
218
|
+
}),
|
|
219
|
+
callWithContext: async (input, signal, context) => {
|
|
220
|
+
const missing = needsContext(context);
|
|
221
|
+
if (missing)
|
|
222
|
+
return missing;
|
|
223
|
+
const live = getLiveAgentTask(context.sessionState.agentTasks, input.agent_id);
|
|
224
|
+
if (live?.status === 'running') {
|
|
225
|
+
enqueueAgentMessage(live, input.message);
|
|
226
|
+
await saveAgentRecord(context, live);
|
|
227
|
+
await appendAgentEvent(context, live.id, {
|
|
228
|
+
phase: 'followup',
|
|
229
|
+
message: input.message,
|
|
230
|
+
});
|
|
231
|
+
return { ok: true, content: `后续任务已发送给运行中的 ${live.id}。` };
|
|
232
|
+
}
|
|
233
|
+
const saved = await loadAgentRecord(context, input.agent_id);
|
|
234
|
+
if (!saved)
|
|
235
|
+
return {
|
|
236
|
+
ok: false,
|
|
237
|
+
error: `当前 Session 中不存在 Agent ${input.agent_id}`,
|
|
238
|
+
};
|
|
239
|
+
return runManagedAgentTask({
|
|
240
|
+
description: saved.description,
|
|
241
|
+
prompt: input.message,
|
|
242
|
+
resume_agent_id: input.agent_id,
|
|
243
|
+
run_in_background: input.run_in_background ?? true,
|
|
244
|
+
}, signal, context);
|
|
245
|
+
},
|
|
246
|
+
};
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
import type { Tool, ToolCallContext, ToolResult } from '../../types.js';
|
|
4
|
+
import { toToolParameters } from '../../utils/zodToJson.js';
|
|
5
|
+
import { runManagedAgentTask } from './agent.js';
|
|
6
|
+
import {
|
|
7
|
+
type AgentTaskRecord,
|
|
8
|
+
appendAgentEvent,
|
|
9
|
+
enqueueAgentMessage,
|
|
10
|
+
getLiveAgentTask,
|
|
11
|
+
listAgentRecords,
|
|
12
|
+
loadAgentRecord,
|
|
13
|
+
saveAgentRecord,
|
|
14
|
+
} from './agentTaskService.js';
|
|
15
|
+
|
|
16
|
+
const agentId = z.string().regex(/^agent_[0-9a-f-]{36}$/i);
|
|
17
|
+
|
|
18
|
+
function needsContext(context: ToolCallContext): ToolResult | undefined {
|
|
19
|
+
if (!context.sessionState)
|
|
20
|
+
return { ok: false, error: 'Agent 控制工具需要当前 Session 运行状态。' };
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function snapshot(record: AgentTaskRecord) {
|
|
25
|
+
return {
|
|
26
|
+
agent_id: record.id,
|
|
27
|
+
status: record.status,
|
|
28
|
+
description: record.description,
|
|
29
|
+
progress: record.progress,
|
|
30
|
+
created_at: record.createdAt,
|
|
31
|
+
updated_at: record.updatedAt,
|
|
32
|
+
result: record.result
|
|
33
|
+
? record.result.ok
|
|
34
|
+
? record.result.content.slice(0, 4_000)
|
|
35
|
+
: `Error: ${record.result.error.slice(0, 4_000)}`
|
|
36
|
+
: undefined,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const messageSchema = z.object({
|
|
41
|
+
agent_id: agentId,
|
|
42
|
+
message: z.string().min(1).max(100_000),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export const agentMessageTool: Tool<z.infer<typeof messageSchema>> = {
|
|
46
|
+
name: 'AgentMessage',
|
|
47
|
+
description:
|
|
48
|
+
'Sends additional context or a correction to one running sub-agent by stable agent_id. ' +
|
|
49
|
+
'The message is inserted at the next model boundary without cancelling its current tool call. ' +
|
|
50
|
+
'Use AgentFollowup instead when the agent is already idle or completed.',
|
|
51
|
+
inputSchema: messageSchema,
|
|
52
|
+
parameters: toToolParameters(messageSchema),
|
|
53
|
+
isReadOnly: false,
|
|
54
|
+
isConcurrencySafe: true,
|
|
55
|
+
call: async () => ({
|
|
56
|
+
ok: false,
|
|
57
|
+
error: 'AgentMessage requires session context',
|
|
58
|
+
}),
|
|
59
|
+
callWithContext: async (input, _signal, context) => {
|
|
60
|
+
const missing = needsContext(context);
|
|
61
|
+
if (missing) return missing;
|
|
62
|
+
const task = getLiveAgentTask(
|
|
63
|
+
context.sessionState!.agentTasks,
|
|
64
|
+
input.agent_id,
|
|
65
|
+
);
|
|
66
|
+
if (!task || task.status !== 'running') {
|
|
67
|
+
const saved = await loadAgentRecord(context, input.agent_id);
|
|
68
|
+
return saved
|
|
69
|
+
? {
|
|
70
|
+
ok: false,
|
|
71
|
+
error: `Agent ${input.agent_id} 当前为 ${saved.status};请使用 AgentFollowup 启动后续回合。`,
|
|
72
|
+
}
|
|
73
|
+
: { ok: false, error: `当前 Session 中不存在 Agent ${input.agent_id}` };
|
|
74
|
+
}
|
|
75
|
+
enqueueAgentMessage(task, input.message);
|
|
76
|
+
await saveAgentRecord(context, task);
|
|
77
|
+
await appendAgentEvent(context, task.id, {
|
|
78
|
+
phase: 'message',
|
|
79
|
+
message: input.message,
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
ok: true,
|
|
83
|
+
content: `消息已发送给 ${task.id},将在下一个模型边界生效。`,
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const listSchema = z.object({
|
|
89
|
+
include_completed: z.boolean().optional(),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export const agentListTool: Tool<z.infer<typeof listSchema>> = {
|
|
93
|
+
name: 'AgentList',
|
|
94
|
+
description:
|
|
95
|
+
'Lists sub-agents owned by the current Session, including stable IDs, status and latest progress.',
|
|
96
|
+
inputSchema: listSchema,
|
|
97
|
+
parameters: toToolParameters(listSchema),
|
|
98
|
+
isReadOnly: true,
|
|
99
|
+
isConcurrencySafe: true,
|
|
100
|
+
call: async () => ({
|
|
101
|
+
ok: false,
|
|
102
|
+
error: 'AgentList requires session context',
|
|
103
|
+
}),
|
|
104
|
+
callWithContext: async (input, _signal, context) => {
|
|
105
|
+
const missing = needsContext(context);
|
|
106
|
+
if (missing) return missing;
|
|
107
|
+
const records = (await listAgentRecords(context)).filter(
|
|
108
|
+
(record) =>
|
|
109
|
+
input.include_completed !== false || record.status === 'running',
|
|
110
|
+
);
|
|
111
|
+
return {
|
|
112
|
+
ok: true,
|
|
113
|
+
content: records.length
|
|
114
|
+
? JSON.stringify(records.map(snapshot), null, 2)
|
|
115
|
+
: '当前 Session 没有符合条件的子 Agent。',
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const waitSchema = z.object({
|
|
121
|
+
agent_ids: z.array(agentId).min(1).max(8),
|
|
122
|
+
timeout_ms: z.number().int().min(0).max(120_000).optional(),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export const agentWaitTool: Tool<z.infer<typeof waitSchema>> = {
|
|
126
|
+
name: 'AgentWait',
|
|
127
|
+
description:
|
|
128
|
+
'Waits efficiently until any selected running sub-agent finishes or the timeout expires, then returns fresh snapshots for all selected IDs. Do not poll repeatedly.',
|
|
129
|
+
inputSchema: waitSchema,
|
|
130
|
+
parameters: toToolParameters(waitSchema),
|
|
131
|
+
isReadOnly: true,
|
|
132
|
+
isConcurrencySafe: true,
|
|
133
|
+
interruptBehavior: () => 'cancel',
|
|
134
|
+
call: async () => ({
|
|
135
|
+
ok: false,
|
|
136
|
+
error: 'AgentWait requires session context',
|
|
137
|
+
}),
|
|
138
|
+
callWithContext: async (input, signal, context) => {
|
|
139
|
+
const missing = needsContext(context);
|
|
140
|
+
if (missing) return missing;
|
|
141
|
+
const live = input.agent_ids
|
|
142
|
+
.map((id) => getLiveAgentTask(context.sessionState!.agentTasks, id))
|
|
143
|
+
.filter((task): task is NonNullable<typeof task> =>
|
|
144
|
+
Boolean(task?.status === 'running'),
|
|
145
|
+
);
|
|
146
|
+
const timeoutMs = input.timeout_ms ?? 30_000;
|
|
147
|
+
if (live.length && timeoutMs > 0) {
|
|
148
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
149
|
+
let onAbort: (() => void) | undefined;
|
|
150
|
+
const timeout = new Promise<void>((resolve) => {
|
|
151
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
152
|
+
timer.unref?.();
|
|
153
|
+
});
|
|
154
|
+
const aborted = new Promise<void>((resolve) => {
|
|
155
|
+
if (!signal) return;
|
|
156
|
+
onAbort = () => resolve();
|
|
157
|
+
if (signal.aborted) resolve();
|
|
158
|
+
else signal.addEventListener('abort', onAbort, { once: true });
|
|
159
|
+
});
|
|
160
|
+
await Promise.race([
|
|
161
|
+
...live.map((task) => task.completion),
|
|
162
|
+
timeout,
|
|
163
|
+
aborted,
|
|
164
|
+
]);
|
|
165
|
+
if (timer) clearTimeout(timer);
|
|
166
|
+
if (signal && onAbort) signal.removeEventListener('abort', onAbort);
|
|
167
|
+
if (signal?.aborted) return { ok: false, error: 'aborted' };
|
|
168
|
+
}
|
|
169
|
+
const records: AgentTaskRecord[] = [];
|
|
170
|
+
const missingIds: string[] = [];
|
|
171
|
+
for (const id of input.agent_ids) {
|
|
172
|
+
const record =
|
|
173
|
+
getLiveAgentTask(context.sessionState!.agentTasks, id) ??
|
|
174
|
+
(await loadAgentRecord(context, id));
|
|
175
|
+
if (record) records.push(record);
|
|
176
|
+
else missingIds.push(id);
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
ok: true,
|
|
180
|
+
content: JSON.stringify(
|
|
181
|
+
{ agents: records.map(snapshot), missing: missingIds },
|
|
182
|
+
null,
|
|
183
|
+
2,
|
|
184
|
+
),
|
|
185
|
+
};
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const interruptSchema = z.object({ agent_id: agentId });
|
|
190
|
+
|
|
191
|
+
export const agentInterruptTool: Tool<z.infer<typeof interruptSchema>> = {
|
|
192
|
+
name: 'AgentInterrupt',
|
|
193
|
+
description:
|
|
194
|
+
'Interrupts one running sub-agent owned by the current Session. Its private history, progress and worktree are preserved for a later AgentFollowup.',
|
|
195
|
+
inputSchema: interruptSchema,
|
|
196
|
+
parameters: toToolParameters(interruptSchema),
|
|
197
|
+
isReadOnly: false,
|
|
198
|
+
isConcurrencySafe: true,
|
|
199
|
+
call: async () => ({
|
|
200
|
+
ok: false,
|
|
201
|
+
error: 'AgentInterrupt requires session context',
|
|
202
|
+
}),
|
|
203
|
+
callWithContext: async (input, _signal, context) => {
|
|
204
|
+
const missing = needsContext(context);
|
|
205
|
+
if (missing) return missing;
|
|
206
|
+
const live = getLiveAgentTask(
|
|
207
|
+
context.sessionState!.agentTasks,
|
|
208
|
+
input.agent_id,
|
|
209
|
+
);
|
|
210
|
+
if (!live || live.status !== 'running') {
|
|
211
|
+
const saved = await loadAgentRecord(context, input.agent_id);
|
|
212
|
+
return saved
|
|
213
|
+
? {
|
|
214
|
+
ok: true,
|
|
215
|
+
content: `Agent ${input.agent_id} 已经是 ${saved.status},未执行中断。`,
|
|
216
|
+
}
|
|
217
|
+
: { ok: false, error: `当前 Session 中不存在 Agent ${input.agent_id}` };
|
|
218
|
+
}
|
|
219
|
+
const previous = live.status;
|
|
220
|
+
live.abort.abort(new Error('interrupted-by-parent-agent'));
|
|
221
|
+
await Promise.race([
|
|
222
|
+
live.completion,
|
|
223
|
+
new Promise<void>((resolve) => setTimeout(resolve, 5_000)),
|
|
224
|
+
]);
|
|
225
|
+
return {
|
|
226
|
+
ok: true,
|
|
227
|
+
content: `已中断 ${live.id}(先前状态:${previous};当前状态:${live.status})。`,
|
|
228
|
+
};
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const followupSchema = z.object({
|
|
233
|
+
agent_id: agentId,
|
|
234
|
+
message: z.string().min(1).max(100_000),
|
|
235
|
+
run_in_background: z.boolean().optional(),
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
export const agentFollowupTool: Tool<z.infer<typeof followupSchema>> = {
|
|
239
|
+
name: 'AgentFollowup',
|
|
240
|
+
description:
|
|
241
|
+
'Adds a follow-up task to a stable sub-agent. If it is running, delivers the message at the next model boundary; if idle, resumes its persisted private history and starts a new turn.',
|
|
242
|
+
inputSchema: followupSchema,
|
|
243
|
+
parameters: toToolParameters(followupSchema),
|
|
244
|
+
isReadOnly: false,
|
|
245
|
+
isConcurrencySafe: true,
|
|
246
|
+
interruptBehavior: () => 'block',
|
|
247
|
+
call: async () => ({
|
|
248
|
+
ok: false,
|
|
249
|
+
error: 'AgentFollowup requires session context',
|
|
250
|
+
}),
|
|
251
|
+
callWithContext: async (input, signal, context) => {
|
|
252
|
+
const missing = needsContext(context);
|
|
253
|
+
if (missing) return missing;
|
|
254
|
+
const live = getLiveAgentTask(
|
|
255
|
+
context.sessionState!.agentTasks,
|
|
256
|
+
input.agent_id,
|
|
257
|
+
);
|
|
258
|
+
if (live?.status === 'running') {
|
|
259
|
+
enqueueAgentMessage(live, input.message);
|
|
260
|
+
await saveAgentRecord(context, live);
|
|
261
|
+
await appendAgentEvent(context, live.id, {
|
|
262
|
+
phase: 'followup',
|
|
263
|
+
message: input.message,
|
|
264
|
+
});
|
|
265
|
+
return { ok: true, content: `后续任务已发送给运行中的 ${live.id}。` };
|
|
266
|
+
}
|
|
267
|
+
const saved = await loadAgentRecord(context, input.agent_id);
|
|
268
|
+
if (!saved)
|
|
269
|
+
return {
|
|
270
|
+
ok: false,
|
|
271
|
+
error: `当前 Session 中不存在 Agent ${input.agent_id}`,
|
|
272
|
+
};
|
|
273
|
+
return runManagedAgentTask(
|
|
274
|
+
{
|
|
275
|
+
description: saved.description,
|
|
276
|
+
prompt: input.message,
|
|
277
|
+
resume_agent_id: input.agent_id,
|
|
278
|
+
run_in_background: input.run_in_background ?? true,
|
|
279
|
+
},
|
|
280
|
+
signal,
|
|
281
|
+
context,
|
|
282
|
+
);
|
|
283
|
+
},
|
|
284
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appendFile, mkdir } from 'node:fs/promises';
|
|
1
|
+
import { appendFile, mkdir, readdir } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { readRuntimeJson, sessionRuntimeDir, withRuntimeFileLock, writeRuntimeJson, } from '../../session/runtimeStore.js';
|
|
4
4
|
export function createAgentTaskState() {
|
|
@@ -49,6 +49,45 @@ export async function loadAgentRecord(ctx, id) {
|
|
|
49
49
|
export function getLiveAgentTask(state, id) {
|
|
50
50
|
return state?.tasks.get(id);
|
|
51
51
|
}
|
|
52
|
+
/** List the durable tasks owned by this Session, with process-local state winning. */
|
|
53
|
+
export async function listAgentRecords(ctx) {
|
|
54
|
+
const root = join(sessionRuntimeDir(ctx), 'agents');
|
|
55
|
+
const entries = await readdir(root, { withFileTypes: true }).catch(() => []);
|
|
56
|
+
const records = new Map();
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
if (!entry.isDirectory() || !/^agent_[0-9a-f-]{36}$/i.test(entry.name))
|
|
59
|
+
continue;
|
|
60
|
+
const record = await loadAgentRecord(ctx, entry.name);
|
|
61
|
+
if (record)
|
|
62
|
+
records.set(record.id, record);
|
|
63
|
+
}
|
|
64
|
+
for (const live of ctx.sessionState?.agentTasks.tasks.values() ?? [])
|
|
65
|
+
records.set(live.id, live);
|
|
66
|
+
return [...records.values()].sort((a, b) => b.updatedAt - a.updatedAt);
|
|
67
|
+
}
|
|
68
|
+
export function agentMessage(message) {
|
|
69
|
+
return {
|
|
70
|
+
role: 'user',
|
|
71
|
+
content: `[主 Agent 后续消息]\n${message}`,
|
|
72
|
+
timestamp: Date.now(),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** Deliver at the next model-message boundary without aborting the active turn. */
|
|
76
|
+
export function enqueueAgentMessage(task, message) {
|
|
77
|
+
if (task.history)
|
|
78
|
+
task.history.push(agentMessage(message));
|
|
79
|
+
else
|
|
80
|
+
task.inbox.push(message);
|
|
81
|
+
task.updatedAt = Date.now();
|
|
82
|
+
task.events.emit('change');
|
|
83
|
+
}
|
|
84
|
+
/** Attach the live private history and flush messages accepted during startup. */
|
|
85
|
+
export function attachAgentHistory(task, history) {
|
|
86
|
+
task.history = history;
|
|
87
|
+
for (const message of task.inbox.splice(0))
|
|
88
|
+
history.push(agentMessage(message));
|
|
89
|
+
task.events.emit('change');
|
|
90
|
+
}
|
|
52
91
|
export async function stopAllAgentTasks(state) {
|
|
53
92
|
const running = [...state.tasks.values()].filter((t) => t.status === 'running');
|
|
54
93
|
running.forEach((t) => t.abort.abort());
|