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,58 @@
|
|
|
1
|
+
export function classifyBrowserFailure(error) {
|
|
2
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3
|
+
// Do not treat a generic "element not found" / selector miss as a stale
|
|
4
|
+
// browser target. Those are normal locator failures and must return the
|
|
5
|
+
// page evidence to the model instead of replaying the same action.
|
|
6
|
+
if (/\b(?:tab|target|frame)\b.*(?:closed|crashed|detached|destroyed|not found|invalid)/i.test(message) ||
|
|
7
|
+
/execution context.*(?:destroyed|not found|closed)/i.test(message) ||
|
|
8
|
+
/(?:page|window).*closed/i.test(message))
|
|
9
|
+
return 'stale-tab';
|
|
10
|
+
if (/cdp|socket|websocket|daemon|connection|disconnected|closed/i.test(message))
|
|
11
|
+
return 'browser-disconnected';
|
|
12
|
+
return 'unknown';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* agent-browser reports many daemon/target failures as an exit code instead
|
|
16
|
+
* of rejecting the runner promise. Convert only those known recoverable
|
|
17
|
+
* failures into exceptions so the runtime recovery wrapper can see them;
|
|
18
|
+
* ordinary selector/business errors must remain normal tool results.
|
|
19
|
+
*/
|
|
20
|
+
export function recoverableAgentBrowserRunFailure(result) {
|
|
21
|
+
if (result.code === 0)
|
|
22
|
+
return undefined;
|
|
23
|
+
const message = result.stderr?.trim() || result.stdout?.trim() || `exit ${result.code}`;
|
|
24
|
+
const error = new Error(message);
|
|
25
|
+
return classifyBrowserFailure(error) === 'unknown' ? undefined : error;
|
|
26
|
+
}
|
|
27
|
+
/** Retry only recoverable browser failures, rebuilding the connection/tab before retrying. */
|
|
28
|
+
export async function withBrowserRecovery(operation, hooks = {}) {
|
|
29
|
+
const maxAttempts = Math.max(1, Math.min(hooks.maxAttempts ?? 3, 4));
|
|
30
|
+
let attempt = 0;
|
|
31
|
+
let recoveredConnection = false;
|
|
32
|
+
let reboundTab = false;
|
|
33
|
+
while (true) {
|
|
34
|
+
if (hooks.signal?.aborted)
|
|
35
|
+
throw hooks.signal.reason ?? new Error('浏览器任务已中断');
|
|
36
|
+
try {
|
|
37
|
+
return await operation();
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
attempt += 1;
|
|
41
|
+
const kind = classifyBrowserFailure(error);
|
|
42
|
+
if (kind === 'unknown' || attempt >= maxAttempts)
|
|
43
|
+
throw error;
|
|
44
|
+
if (kind === 'browser-disconnected' && !recoveredConnection) {
|
|
45
|
+
await hooks.reconnect?.();
|
|
46
|
+
recoveredConnection = true;
|
|
47
|
+
reboundTab = false;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (kind === 'stale-tab' && !reboundTab) {
|
|
51
|
+
await hooks.rebindTab?.();
|
|
52
|
+
reboundTab = true;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export type BrowserRecoveryKind =
|
|
2
|
+
| 'stale-tab'
|
|
3
|
+
| 'browser-disconnected'
|
|
4
|
+
| 'unknown';
|
|
5
|
+
|
|
6
|
+
export function classifyBrowserFailure(error: unknown): BrowserRecoveryKind {
|
|
7
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8
|
+
// Do not treat a generic "element not found" / selector miss as a stale
|
|
9
|
+
// browser target. Those are normal locator failures and must return the
|
|
10
|
+
// page evidence to the model instead of replaying the same action.
|
|
11
|
+
if (
|
|
12
|
+
/\b(?:tab|target|frame)\b.*(?:closed|crashed|detached|destroyed|not found|invalid)/i.test(
|
|
13
|
+
message,
|
|
14
|
+
) ||
|
|
15
|
+
/execution context.*(?:destroyed|not found|closed)/i.test(message) ||
|
|
16
|
+
/(?:page|window).*closed/i.test(message)
|
|
17
|
+
)
|
|
18
|
+
return 'stale-tab';
|
|
19
|
+
if (
|
|
20
|
+
/cdp|socket|websocket|daemon|connection|disconnected|closed/i.test(message)
|
|
21
|
+
)
|
|
22
|
+
return 'browser-disconnected';
|
|
23
|
+
return 'unknown';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* agent-browser reports many daemon/target failures as an exit code instead
|
|
28
|
+
* of rejecting the runner promise. Convert only those known recoverable
|
|
29
|
+
* failures into exceptions so the runtime recovery wrapper can see them;
|
|
30
|
+
* ordinary selector/business errors must remain normal tool results.
|
|
31
|
+
*/
|
|
32
|
+
export function recoverableAgentBrowserRunFailure(result: {
|
|
33
|
+
code: number;
|
|
34
|
+
stdout?: string;
|
|
35
|
+
stderr?: string;
|
|
36
|
+
}): Error | undefined {
|
|
37
|
+
if (result.code === 0) return undefined;
|
|
38
|
+
const message =
|
|
39
|
+
result.stderr?.trim() || result.stdout?.trim() || `exit ${result.code}`;
|
|
40
|
+
const error = new Error(message);
|
|
41
|
+
return classifyBrowserFailure(error) === 'unknown' ? undefined : error;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type BrowserRecoveryHooks<T> = {
|
|
45
|
+
reconnect?: () => Promise<void>;
|
|
46
|
+
rebindTab?: () => Promise<void>;
|
|
47
|
+
maxAttempts?: number;
|
|
48
|
+
signal?: AbortSignal;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Retry only recoverable browser failures, rebuilding the connection/tab before retrying. */
|
|
52
|
+
export async function withBrowserRecovery<T>(
|
|
53
|
+
operation: () => Promise<T>,
|
|
54
|
+
hooks: BrowserRecoveryHooks<T> = {},
|
|
55
|
+
): Promise<T> {
|
|
56
|
+
const maxAttempts = Math.max(1, Math.min(hooks.maxAttempts ?? 3, 4));
|
|
57
|
+
let attempt = 0;
|
|
58
|
+
let recoveredConnection = false;
|
|
59
|
+
let reboundTab = false;
|
|
60
|
+
while (true) {
|
|
61
|
+
if (hooks.signal?.aborted)
|
|
62
|
+
throw hooks.signal.reason ?? new Error('浏览器任务已中断');
|
|
63
|
+
try {
|
|
64
|
+
return await operation();
|
|
65
|
+
} catch (error) {
|
|
66
|
+
attempt += 1;
|
|
67
|
+
const kind = classifyBrowserFailure(error);
|
|
68
|
+
if (kind === 'unknown' || attempt >= maxAttempts) throw error;
|
|
69
|
+
if (kind === 'browser-disconnected' && !recoveredConnection) {
|
|
70
|
+
await hooks.reconnect?.();
|
|
71
|
+
recoveredConnection = true;
|
|
72
|
+
reboundTab = false;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (kind === 'stale-tab' && !reboundTab) {
|
|
76
|
+
await hooks.rebindTab?.();
|
|
77
|
+
reboundTab = true;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -26,8 +26,8 @@ export function isLikelyIrreversibleBrowserTarget(label) {
|
|
|
26
26
|
const normalized = label.replace(/\s+/g, ' ').trim();
|
|
27
27
|
if (!normalized)
|
|
28
28
|
return false;
|
|
29
|
-
return /(?:^|[\s"'=:_-])(?:publish|submit|purchase|buy now|place order|delete|confirm delete|send)(?:$|[\s"'=:_-])/i.test(normalized)
|
|
30
|
-
|
|
29
|
+
return (/(?:^|[\s"'=:_-])(?:publish|submit|purchase|buy now|place order|delete|confirm delete|send)(?:$|[\s"'=:_-])/i.test(normalized) ||
|
|
30
|
+
/(?:发布|发表|提交|立即购买|确认购买|确认下单|删除|确认删除|发送)(?:$|[\s::))】\]])/.test(normalized));
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* Process-wide ownership registry for the shared browser profile.
|
|
@@ -40,6 +40,7 @@ export class BrowserRunRegistry {
|
|
|
40
40
|
runs = new Map();
|
|
41
41
|
conversationTabs = new Map();
|
|
42
42
|
tabOwners = new Map();
|
|
43
|
+
tabMetadata = new Map();
|
|
43
44
|
ensure(input) {
|
|
44
45
|
const key = this.key(input.conversationKey, input.runId ?? 'workbench');
|
|
45
46
|
const existing = this.runs.get(key);
|
|
@@ -57,7 +58,9 @@ export class BrowserRunRegistry {
|
|
|
57
58
|
runId: input.runId ?? randomUUID(),
|
|
58
59
|
profileId: input.profileId ?? 'default',
|
|
59
60
|
claimedTabIds: [],
|
|
60
|
-
grants: [
|
|
61
|
+
grants: [
|
|
62
|
+
...new Set(input.grants ?? ['observe', 'interact']),
|
|
63
|
+
],
|
|
61
64
|
allowedUploadRoots: [...new Set(input.allowedUploadRoots ?? [])],
|
|
62
65
|
...(input.taskGrantId ? { taskGrantId: input.taskGrantId } : {}),
|
|
63
66
|
};
|
|
@@ -75,6 +78,19 @@ export class BrowserRunRegistry {
|
|
|
75
78
|
ownerOf(tabId) {
|
|
76
79
|
return this.tabOwners.get(tabId);
|
|
77
80
|
}
|
|
81
|
+
markTab(context, tabId, metadata) {
|
|
82
|
+
this.assertTab(context, tabId);
|
|
83
|
+
const next = {
|
|
84
|
+
tabId,
|
|
85
|
+
...metadata,
|
|
86
|
+
updatedAt: Date.now(),
|
|
87
|
+
};
|
|
88
|
+
this.tabMetadata.set(tabId, next);
|
|
89
|
+
return next;
|
|
90
|
+
}
|
|
91
|
+
tabInfo(tabId) {
|
|
92
|
+
return this.tabMetadata.get(tabId);
|
|
93
|
+
}
|
|
78
94
|
claimTab(context, tabId) {
|
|
79
95
|
const owner = this.tabOwners.get(tabId);
|
|
80
96
|
if (owner && owner !== context.conversationKey) {
|
|
@@ -103,10 +119,26 @@ export class BrowserRunRegistry {
|
|
|
103
119
|
this.runs.delete(key);
|
|
104
120
|
}
|
|
105
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Invalidate every tab lease after the browser process is rebuilt. Tab ids
|
|
124
|
+
* are process-local and can be reused by the replacement daemon; retaining
|
|
125
|
+
* old owners would either block the new owner or route it across sessions.
|
|
126
|
+
* Run contexts remain alive so the active task can claim a replacement tab.
|
|
127
|
+
*/
|
|
128
|
+
invalidateAllTabs() {
|
|
129
|
+
this.tabOwners.clear();
|
|
130
|
+
this.conversationTabs.clear();
|
|
131
|
+
this.tabMetadata.clear();
|
|
132
|
+
for (const context of this.runs.values()) {
|
|
133
|
+
context.claimedTabIds = [];
|
|
134
|
+
context.activeTabId = undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
106
137
|
releaseTab(conversationKey, tabId) {
|
|
107
138
|
if (this.tabOwners.get(tabId) !== conversationKey)
|
|
108
139
|
return;
|
|
109
140
|
this.tabOwners.delete(tabId);
|
|
141
|
+
this.tabMetadata.delete(tabId);
|
|
110
142
|
const claimed = this.conversationTabs.get(conversationKey);
|
|
111
143
|
claimed?.delete(tabId);
|
|
112
144
|
if (claimed?.size === 0)
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
|
|
3
3
|
export type BrowserActionGrant = 'observe' | 'interact' | 'upload' | 'submit';
|
|
4
|
+
export type TabRetention =
|
|
5
|
+
| 'ephemeral'
|
|
6
|
+
| 'conversation'
|
|
7
|
+
| 'handoff'
|
|
8
|
+
| 'deliverable';
|
|
4
9
|
|
|
5
|
-
const NEGATED_ACTION_PREFIX =
|
|
10
|
+
const NEGATED_ACTION_PREFIX =
|
|
11
|
+
/(?:不要|不需(?:要)?|不能|不可|别|禁止|避免|无需|切勿)(?:再|自动|直接|帮我|替我|进行)?[\s\S]{0,8}$|(?:do\s+not|don't|never|must\s+not|should\s+not|without)(?:\s+\w+){0,4}\s*$/i;
|
|
6
12
|
|
|
7
13
|
function hasUnnegatedAction(text: string, pattern: RegExp): boolean {
|
|
8
14
|
for (const match of text.matchAll(pattern)) {
|
|
9
|
-
const prefix = text.slice(
|
|
15
|
+
const prefix = text.slice(
|
|
16
|
+
Math.max(0, (match.index ?? 0) - 32),
|
|
17
|
+
match.index,
|
|
18
|
+
);
|
|
10
19
|
if (!NEGATED_ACTION_PREFIX.test(prefix)) return true;
|
|
11
20
|
}
|
|
12
21
|
return false;
|
|
@@ -19,11 +28,20 @@ function hasUnnegatedAction(text: string, pattern: RegExp): boolean {
|
|
|
19
28
|
*/
|
|
20
29
|
export function inferBrowserTaskGrants(text: string): BrowserActionGrant[] {
|
|
21
30
|
const grants: BrowserActionGrant[] = ['observe', 'interact'];
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
if (
|
|
32
|
+
hasUnnegatedAction(
|
|
33
|
+
text,
|
|
34
|
+
/\b(?:upload|attach)\b(?!\s+(?:page|button|history|settings))|上传(?!页|页面|按钮|记录|历史|设置)|选择文件|添加文件/giu,
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
grants.push('upload');
|
|
38
|
+
if (
|
|
39
|
+
hasUnnegatedAction(
|
|
40
|
+
text,
|
|
41
|
+
/\b(?:publish|submit|purchase|buy|delete|place\s+(?:an?\s+)?order|send)\b(?!\s+(?:page|button|history|record|settings))|发布(?!页|页面|按钮|记录|历史|设置)|发表(?!页|页面|按钮|记录|历史|设置)|提交(?!页|页面|按钮|记录|历史|设置)|购买|下单|删除|发送/giu,
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
grants.push('submit');
|
|
27
45
|
return grants;
|
|
28
46
|
}
|
|
29
47
|
|
|
@@ -31,8 +49,14 @@ export function inferBrowserTaskGrants(text: string): BrowserActionGrant[] {
|
|
|
31
49
|
export function isLikelyIrreversibleBrowserTarget(label: string): boolean {
|
|
32
50
|
const normalized = label.replace(/\s+/g, ' ').trim();
|
|
33
51
|
if (!normalized) return false;
|
|
34
|
-
return
|
|
35
|
-
|
|
52
|
+
return (
|
|
53
|
+
/(?:^|[\s"'=:_-])(?:publish|submit|purchase|buy now|place order|delete|confirm delete|send)(?:$|[\s"'=:_-])/i.test(
|
|
54
|
+
normalized,
|
|
55
|
+
) ||
|
|
56
|
+
/(?:发布|发表|提交|立即购买|确认购买|确认下单|删除|确认删除|发送)(?:$|[\s::))】\]])/.test(
|
|
57
|
+
normalized,
|
|
58
|
+
)
|
|
59
|
+
);
|
|
36
60
|
}
|
|
37
61
|
|
|
38
62
|
export interface BrowserRunContext {
|
|
@@ -47,6 +71,13 @@ export interface BrowserRunContext {
|
|
|
47
71
|
allowedUploadRoots: string[];
|
|
48
72
|
}
|
|
49
73
|
|
|
74
|
+
export interface BrowserTabMetadata {
|
|
75
|
+
tabId: string;
|
|
76
|
+
retention: TabRetention;
|
|
77
|
+
title?: string;
|
|
78
|
+
updatedAt: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
50
81
|
type BrowserRunInput = {
|
|
51
82
|
conversationKey: string;
|
|
52
83
|
runId?: string;
|
|
@@ -67,14 +98,17 @@ export class BrowserRunRegistry {
|
|
|
67
98
|
private readonly runs = new Map<string, BrowserRunContext>();
|
|
68
99
|
private readonly conversationTabs = new Map<string, Set<string>>();
|
|
69
100
|
private readonly tabOwners = new Map<string, string>();
|
|
101
|
+
private readonly tabMetadata = new Map<string, BrowserTabMetadata>();
|
|
70
102
|
|
|
71
103
|
ensure(input: BrowserRunInput): BrowserRunContext {
|
|
72
104
|
const key = this.key(input.conversationKey, input.runId ?? 'workbench');
|
|
73
105
|
const existing = this.runs.get(key);
|
|
74
106
|
if (existing) {
|
|
75
107
|
if (input.grants) existing.grants = [...new Set(input.grants)];
|
|
76
|
-
if (input.taskGrantId !== undefined)
|
|
77
|
-
|
|
108
|
+
if (input.taskGrantId !== undefined)
|
|
109
|
+
existing.taskGrantId = input.taskGrantId;
|
|
110
|
+
if (input.allowedUploadRoots)
|
|
111
|
+
existing.allowedUploadRoots = [...new Set(input.allowedUploadRoots)];
|
|
78
112
|
return existing;
|
|
79
113
|
}
|
|
80
114
|
const context: BrowserRunContext = {
|
|
@@ -82,7 +116,9 @@ export class BrowserRunRegistry {
|
|
|
82
116
|
runId: input.runId ?? randomUUID(),
|
|
83
117
|
profileId: input.profileId ?? 'default',
|
|
84
118
|
claimedTabIds: [],
|
|
85
|
-
grants: [
|
|
119
|
+
grants: [
|
|
120
|
+
...new Set<BrowserActionGrant>(input.grants ?? ['observe', 'interact']),
|
|
121
|
+
],
|
|
86
122
|
allowedUploadRoots: [...new Set(input.allowedUploadRoots ?? [])],
|
|
87
123
|
...(input.taskGrantId ? { taskGrantId: input.taskGrantId } : {}),
|
|
88
124
|
};
|
|
@@ -92,7 +128,9 @@ export class BrowserRunRegistry {
|
|
|
92
128
|
|
|
93
129
|
find(conversationKey: string, runId?: string): BrowserRunContext | undefined {
|
|
94
130
|
if (runId) return this.runs.get(this.key(conversationKey, runId));
|
|
95
|
-
return [...this.runs.values()].find(
|
|
131
|
+
return [...this.runs.values()].find(
|
|
132
|
+
(item) => item.conversationKey === conversationKey,
|
|
133
|
+
);
|
|
96
134
|
}
|
|
97
135
|
|
|
98
136
|
claimedTabs(conversationKey: string): string[] {
|
|
@@ -103,13 +141,33 @@ export class BrowserRunRegistry {
|
|
|
103
141
|
return this.tabOwners.get(tabId);
|
|
104
142
|
}
|
|
105
143
|
|
|
144
|
+
markTab(
|
|
145
|
+
context: BrowserRunContext,
|
|
146
|
+
tabId: string,
|
|
147
|
+
metadata: Pick<BrowserTabMetadata, 'retention' | 'title'>,
|
|
148
|
+
): BrowserTabMetadata {
|
|
149
|
+
this.assertTab(context, tabId);
|
|
150
|
+
const next: BrowserTabMetadata = {
|
|
151
|
+
tabId,
|
|
152
|
+
...metadata,
|
|
153
|
+
updatedAt: Date.now(),
|
|
154
|
+
};
|
|
155
|
+
this.tabMetadata.set(tabId, next);
|
|
156
|
+
return next;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
tabInfo(tabId: string): BrowserTabMetadata | undefined {
|
|
160
|
+
return this.tabMetadata.get(tabId);
|
|
161
|
+
}
|
|
162
|
+
|
|
106
163
|
claimTab(context: BrowserRunContext, tabId: string): void {
|
|
107
164
|
const owner = this.tabOwners.get(tabId);
|
|
108
165
|
if (owner && owner !== context.conversationKey) {
|
|
109
166
|
throw new Error('该浏览器标签属于另一个会话,已阻止跨会话操作');
|
|
110
167
|
}
|
|
111
168
|
this.tabOwners.set(tabId, context.conversationKey);
|
|
112
|
-
const claimed =
|
|
169
|
+
const claimed =
|
|
170
|
+
this.conversationTabs.get(context.conversationKey) ?? new Set<string>();
|
|
113
171
|
claimed.add(tabId);
|
|
114
172
|
this.conversationTabs.set(context.conversationKey, claimed);
|
|
115
173
|
context.claimedTabIds = [...claimed];
|
|
@@ -124,16 +182,34 @@ export class BrowserRunRegistry {
|
|
|
124
182
|
* 测试里先撞上(基准集跑完不清理,把 browserTabs 的用例挤挂了),但这是生产同款问题。
|
|
125
183
|
*/
|
|
126
184
|
forgetConversation(conversationKey: string): void {
|
|
127
|
-
for (const tabId of this.claimedTabs(conversationKey))
|
|
185
|
+
for (const tabId of this.claimedTabs(conversationKey))
|
|
186
|
+
this.releaseTab(conversationKey, tabId);
|
|
128
187
|
this.conversationTabs.delete(conversationKey);
|
|
129
188
|
for (const [key, run] of [...this.runs.entries()]) {
|
|
130
189
|
if (run.conversationKey === conversationKey) this.runs.delete(key);
|
|
131
190
|
}
|
|
132
191
|
}
|
|
133
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Invalidate every tab lease after the browser process is rebuilt. Tab ids
|
|
195
|
+
* are process-local and can be reused by the replacement daemon; retaining
|
|
196
|
+
* old owners would either block the new owner or route it across sessions.
|
|
197
|
+
* Run contexts remain alive so the active task can claim a replacement tab.
|
|
198
|
+
*/
|
|
199
|
+
invalidateAllTabs(): void {
|
|
200
|
+
this.tabOwners.clear();
|
|
201
|
+
this.conversationTabs.clear();
|
|
202
|
+
this.tabMetadata.clear();
|
|
203
|
+
for (const context of this.runs.values()) {
|
|
204
|
+
context.claimedTabIds = [];
|
|
205
|
+
context.activeTabId = undefined;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
134
209
|
releaseTab(conversationKey: string, tabId: string): void {
|
|
135
210
|
if (this.tabOwners.get(tabId) !== conversationKey) return;
|
|
136
211
|
this.tabOwners.delete(tabId);
|
|
212
|
+
this.tabMetadata.delete(tabId);
|
|
137
213
|
const claimed = this.conversationTabs.get(conversationKey);
|
|
138
214
|
claimed?.delete(tabId);
|
|
139
215
|
if (claimed?.size === 0) this.conversationTabs.delete(conversationKey);
|
|
@@ -5,10 +5,36 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export class BrowserService {
|
|
7
7
|
tails = new Map();
|
|
8
|
+
async waitForPrevious(previous, signal) {
|
|
9
|
+
if (!signal)
|
|
10
|
+
return previous.catch(() => undefined);
|
|
11
|
+
if (signal.aborted)
|
|
12
|
+
throw signal.reason instanceof Error
|
|
13
|
+
? signal.reason
|
|
14
|
+
: new Error('Browser action cancelled');
|
|
15
|
+
await new Promise((resolve, reject) => {
|
|
16
|
+
let settled = false;
|
|
17
|
+
const cleanup = () => signal.removeEventListener('abort', onAbort);
|
|
18
|
+
const finish = (fn) => {
|
|
19
|
+
if (settled)
|
|
20
|
+
return;
|
|
21
|
+
settled = true;
|
|
22
|
+
cleanup();
|
|
23
|
+
fn();
|
|
24
|
+
};
|
|
25
|
+
const onAbort = () => finish(() => reject(signal.reason instanceof Error
|
|
26
|
+
? signal.reason
|
|
27
|
+
: new Error('Browser action cancelled')));
|
|
28
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
29
|
+
void previous.then(() => finish(resolve), () => finish(resolve));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
8
32
|
async exclusive(input, task) {
|
|
9
33
|
const previous = this.tails.get(input.sessionName) ?? Promise.resolve();
|
|
10
34
|
let release;
|
|
11
|
-
const current = new Promise((resolve) => {
|
|
35
|
+
const current = new Promise((resolve) => {
|
|
36
|
+
release = resolve;
|
|
37
|
+
});
|
|
12
38
|
// 链上永远接一个已消化的 previous:任何一环 reject 都不能顺着队列传下去,
|
|
13
39
|
// 否则后来者在 `await previous` 处抛出。
|
|
14
40
|
const tail = previous.catch(() => undefined).then(() => current);
|
|
@@ -19,7 +45,7 @@ export class BrowserService {
|
|
|
19
45
|
// 当前 current 只 resolve 不 reject,所以实际安全 —— 但那是一条没写下来的不变量,
|
|
20
46
|
// 谁往链上加一个 .then 都可能打破它。把 await 挪进来,安全性就不再依赖它。
|
|
21
47
|
try {
|
|
22
|
-
await
|
|
48
|
+
await this.waitForPrevious(previous, input.signal);
|
|
23
49
|
const run = async (args) => {
|
|
24
50
|
if (input.signal?.aborted) {
|
|
25
51
|
return { code: -1, stdout: '', stderr: 'Browser action cancelled' };
|
|
@@ -8,15 +8,54 @@ import type { AgentBrowserRun, AgentBrowserRunner } from './browser.js';
|
|
|
8
8
|
export class BrowserService {
|
|
9
9
|
private tails = new Map<string, Promise<void>>();
|
|
10
10
|
|
|
11
|
-
async
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
signal
|
|
16
|
-
|
|
11
|
+
private async waitForPrevious(
|
|
12
|
+
previous: Promise<void>,
|
|
13
|
+
signal?: AbortSignal,
|
|
14
|
+
): Promise<void> {
|
|
15
|
+
if (!signal) return previous.catch(() => undefined);
|
|
16
|
+
if (signal.aborted)
|
|
17
|
+
throw signal.reason instanceof Error
|
|
18
|
+
? signal.reason
|
|
19
|
+
: new Error('Browser action cancelled');
|
|
20
|
+
await new Promise<void>((resolve, reject) => {
|
|
21
|
+
let settled = false;
|
|
22
|
+
const cleanup = () => signal.removeEventListener('abort', onAbort);
|
|
23
|
+
const finish = (fn: () => void) => {
|
|
24
|
+
if (settled) return;
|
|
25
|
+
settled = true;
|
|
26
|
+
cleanup();
|
|
27
|
+
fn();
|
|
28
|
+
};
|
|
29
|
+
const onAbort = () =>
|
|
30
|
+
finish(() =>
|
|
31
|
+
reject(
|
|
32
|
+
signal.reason instanceof Error
|
|
33
|
+
? signal.reason
|
|
34
|
+
: new Error('Browser action cancelled'),
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
38
|
+
void previous.then(
|
|
39
|
+
() => finish(resolve),
|
|
40
|
+
() => finish(resolve),
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async exclusive<T>(
|
|
46
|
+
input: {
|
|
47
|
+
sessionName: string;
|
|
48
|
+
bin: string;
|
|
49
|
+
runner: AgentBrowserRunner;
|
|
50
|
+
signal?: AbortSignal;
|
|
51
|
+
},
|
|
52
|
+
task: (run: (args: string[]) => Promise<AgentBrowserRun>) => Promise<T>,
|
|
53
|
+
): Promise<T> {
|
|
17
54
|
const previous = this.tails.get(input.sessionName) ?? Promise.resolve();
|
|
18
55
|
let release!: () => void;
|
|
19
|
-
const current = new Promise<void>((resolve) => {
|
|
56
|
+
const current = new Promise<void>((resolve) => {
|
|
57
|
+
release = resolve;
|
|
58
|
+
});
|
|
20
59
|
// 链上永远接一个已消化的 previous:任何一环 reject 都不能顺着队列传下去,
|
|
21
60
|
// 否则后来者在 `await previous` 处抛出。
|
|
22
61
|
const tail = previous.catch(() => undefined).then(() => current);
|
|
@@ -28,7 +67,7 @@ export class BrowserService {
|
|
|
28
67
|
// 当前 current 只 resolve 不 reject,所以实际安全 —— 但那是一条没写下来的不变量,
|
|
29
68
|
// 谁往链上加一个 .then 都可能打破它。把 await 挪进来,安全性就不再依赖它。
|
|
30
69
|
try {
|
|
31
|
-
await
|
|
70
|
+
await this.waitForPrevious(previous, input.signal);
|
|
32
71
|
const run = async (args: string[]): Promise<AgentBrowserRun> => {
|
|
33
72
|
if (input.signal?.aborted) {
|
|
34
73
|
return { code: -1, stdout: '', stderr: 'Browser action cancelled' };
|
|
@@ -38,7 +77,8 @@ export class BrowserService {
|
|
|
38
77
|
return await task(run);
|
|
39
78
|
} finally {
|
|
40
79
|
release();
|
|
41
|
-
if (this.tails.get(input.sessionName) === tail)
|
|
80
|
+
if (this.tails.get(input.sessionName) === tail)
|
|
81
|
+
this.tails.delete(input.sessionName);
|
|
42
82
|
}
|
|
43
83
|
}
|
|
44
84
|
|