minimal-agent 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/package.json +60 -4
- package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
- package/plugins/cron-runner/src/commands.js +4 -4
- package/plugins/cron-runner/src/commands.ts +4 -4
- package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
- package/plugins/workflow-runner/commands/workflow.md +57 -90
- package/plugins/workflow-runner/commands/workflows.md +4 -4
- package/plugins/workflow-runner/plugin.js +24 -13
- package/plugins/workflow-runner/plugin.ts +28 -13
- package/plugins/workflow-runner/src/discovery.js +160 -0
- package/plugins/workflow-runner/src/discovery.ts +194 -0
- package/plugins/workflow-runner/src/goalAnchor.js +53 -0
- package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
- package/plugins/workflow-runner/src/index.js +197 -206
- package/plugins/workflow-runner/src/index.ts +248 -249
- package/plugins/workflow-runner/src/inputCoercion.js +40 -0
- package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
- package/plugins/workflow-runner/src/journal.js +224 -0
- package/plugins/workflow-runner/src/journal.ts +274 -0
- package/plugins/workflow-runner/src/jsonSchema.js +132 -0
- package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
- package/plugins/workflow-runner/src/moduleResolution.js +64 -0
- package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
- package/plugins/workflow-runner/src/resourceInputs.js +220 -0
- package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
- package/plugins/workflow-runner/src/runner.js +400 -489
- package/plugins/workflow-runner/src/runner.ts +488 -560
- package/plugins/workflow-runner/src/runtime/config.js +46 -0
- package/plugins/workflow-runner/src/runtime/config.ts +69 -0
- package/plugins/workflow-runner/src/runtime/context.js +1304 -0
- package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
- package/plugins/workflow-runner/src/runtime/exec.js +34 -0
- package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
- package/plugins/workflow-runner/src/runtime/flow.js +8 -0
- package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
- package/plugins/workflow-runner/src/runtime/fs.js +27 -0
- package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
- package/plugins/workflow-runner/src/runtime/model.js +6 -0
- package/plugins/workflow-runner/src/runtime/model.ts +8 -0
- package/plugins/workflow-runner/src/runtime/output.js +7 -0
- package/plugins/workflow-runner/src/runtime/output.ts +21 -0
- package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
- package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
- package/plugins/workflow-runner/src/runtime/state.js +16 -0
- package/plugins/workflow-runner/src/runtime/state.ts +19 -0
- package/plugins/workflow-runner/src/runtime/tools.js +3 -0
- package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
- package/plugins/workflow-runner/src/sandbox.js +141 -0
- package/plugins/workflow-runner/src/sandbox.ts +182 -0
- package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
- package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
- package/plugins/workflow-runner/src/taskIdentity.js +151 -307
- package/plugins/workflow-runner/src/taskIdentity.ts +207 -355
- package/plugins/workflow-runner/src/toolRuntime.js +29 -8
- package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
- package/plugins/workflow-runner/src/types.js +7 -211
- package/plugins/workflow-runner/src/types.ts +154 -623
- package/plugins/workflow-runner/src/util.js +47 -0
- package/plugins/workflow-runner/src/util.ts +51 -0
- package/plugins/workflow-runner/src/workflowContext.js +20 -0
- package/plugins/workflow-runner/src/workflowContext.ts +58 -0
- package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
- package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
- package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
- package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
- package/plugins/workflow-runner/src/workflowState.js +474 -802
- package/plugins/workflow-runner/src/workflowState.ts +575 -970
- package/plugins/workflow-runner/test/api.test.ts +223 -0
- package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
- package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
- package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
- package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
- package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
- package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
- package/plugins/workflow-runner/test/discovery.test.ts +234 -0
- package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
- package/plugins/workflow-runner/test/goalController.test.ts +317 -0
- package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
- package/plugins/workflow-runner/test/listResources.test.ts +106 -0
- package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
- package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
- package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
- package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
- package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
- package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
- package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
- package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
- package/skills/pptx/examples/assets/cover-bg.svg +23 -0
- package/skills/pptx/examples/assets/icon-spark.svg +5 -0
- package/skills/ui-design/SKILL.md +5 -2
- package/skills/ui-design/references/layout-composition.md +1 -1
- package/skills/workflow-creator/SKILL.md +344 -0
- package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
- package/skills/workflow-creator/references/api.md +216 -0
- package/skills/workflow-creator/references/migration.md +103 -0
- package/skills/workflow-creator/references/patterns.md +779 -0
- package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
- package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
- package/skills/workflow-creator/scripts/validate.mjs +275 -0
- package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
- package/src/advisor/advisorCoordinator.js +536 -0
- package/src/advisor/advisorCoordinator.ts +693 -0
- package/src/cli/configMenu.tsx +0 -1
- package/src/cli/print.js +27 -6
- package/src/cli/print.ts +48 -4
- package/src/cli/quickModelSetup.js +12 -1
- package/src/cli/quickModelSetup.tsx +15 -3
- 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/cli/wizards/providerPresets.js +18 -8
- package/src/cli/wizards/providerPresets.ts +18 -8
- package/src/collaboration/taskFlow.js +115 -0
- package/src/collaboration/taskFlow.ts +169 -0
- package/src/config/configDefaults.js +41 -1
- package/src/config/configDefaults.ts +43 -1
- package/src/config/configDiagnostics.ts +2 -2
- package/src/config/configFile.js +40 -9
- package/src/config/configFile.ts +60 -11
- package/src/config/configService.js +419 -74
- package/src/config/configService.ts +703 -145
- package/src/config/numericLimits.js +22 -0
- package/src/config/numericLimits.ts +24 -0
- package/src/config.js +1 -0
- package/src/config.ts +3 -2
- package/src/context/budgetPlan.js +1 -1
- package/src/context/budgetPlan.ts +1 -1
- package/src/context/compact.js +7 -2
- package/src/context/compact.ts +9 -2
- package/src/context/microCompactLite.js +212 -13
- package/src/context/microCompactLite.ts +261 -12
- package/src/context/sessionRegistry.js +8 -1
- package/src/context/sessionRegistry.ts +18 -5
- package/src/context/toolResultArchive.js +72 -0
- package/src/context/toolResultArchive.ts +95 -0
- package/src/goal/errors.js +8 -0
- package/src/goal/errors.ts +18 -0
- package/src/goal/goalRuntime.js +344 -0
- package/src/goal/goalRuntime.ts +466 -0
- package/src/goal/goalStore.js +200 -0
- package/src/goal/goalStore.ts +219 -0
- package/src/goal/index.js +4 -0
- package/src/goal/index.ts +4 -0
- package/src/goal/statusMapping.js +71 -0
- package/src/goal/statusMapping.ts +95 -0
- package/src/goal/types.js +7 -0
- package/src/goal/types.ts +179 -0
- package/src/goal/workflowController.js +306 -0
- package/src/goal/workflowController.ts +360 -0
- package/src/harness/evidenceLedger.js +2 -0
- package/src/harness/evidenceLedger.ts +1 -0
- package/src/harness/stallGuard.js +9 -3
- package/src/harness/stallGuard.ts +7 -3
- package/src/images/imageResolver.js +161 -0
- package/src/images/imageResolver.ts +189 -0
- package/src/infra/verification.js +34 -5
- package/src/infra/verification.ts +39 -4
- package/src/llm/client.js +79 -21
- package/src/llm/client.ts +182 -35
- package/src/llm/gatewayClient.js +201 -0
- package/src/llm/gatewayClient.ts +299 -0
- package/src/llm/messageProvenance.js +46 -0
- package/src/llm/messageProvenance.ts +58 -0
- package/src/llm/modelProvider.js +1 -0
- package/src/llm/modelProvider.ts +22 -0
- package/src/llm/providers/anthropicFormat.js +108 -1
- package/src/llm/providers/anthropicFormat.ts +115 -4
- package/src/llm/providers/openaiFormat.js +9 -0
- package/src/llm/providers/openaiFormat.ts +12 -3
- package/src/loop.js +328 -55
- package/src/loop.ts +382 -58
- package/src/main.js +4 -1
- package/src/main.tsx +4 -1
- package/src/managedExecution/index.js +2 -0
- package/src/managedExecution/index.ts +2 -0
- package/src/managedExecution/registry.js +15 -0
- package/src/managedExecution/registry.ts +20 -0
- package/src/managedExecution/types.js +1 -0
- package/src/managedExecution/types.ts +86 -0
- package/src/modelRef.js +9 -0
- package/src/modelRef.ts +31 -0
- package/src/multimodal.js +14 -28
- package/src/multimodal.ts +17 -33
- package/src/net/lanDiscovery.js +65 -0
- package/src/net/lanDiscovery.ts +82 -0
- package/src/plugin-sdk.js +28 -3
- package/src/plugin-sdk.ts +39 -6
- package/src/plugins/pluginApi.ts +16 -1
- package/src/plugins/pluginLoader.js +4 -0
- package/src/plugins/pluginLoader.ts +6 -0
- package/src/plugins/pluginRunner.js +139 -7
- package/src/plugins/pluginRunner.ts +173 -9
- package/src/plugins/slashFallback.js +113 -0
- package/src/plugins/slashFallback.ts +137 -0
- package/src/prompts/policyRegistry.js +12 -32
- package/src/prompts/policyRegistry.ts +15 -32
- package/src/prompts/promptAssembly.js +103 -12
- package/src/prompts/promptAssembly.ts +114 -12
- package/src/prompts/skillList.js +21 -3
- package/src/prompts/skillList.ts +27 -2
- package/src/prompts/stableSystemPrompt.js +18 -0
- package/src/prompts/stableSystemPrompt.ts +33 -0
- package/src/runtime/agentRuntimeContext.js +8 -2
- package/src/runtime/agentRuntimeContext.ts +17 -4
- package/src/session/runAgentTurn.js +78 -12
- package/src/session/runAgentTurn.ts +105 -23
- package/src/session/runTurn.js +10 -9
- package/src/session/runTurn.ts +17 -8
- package/src/session/sessionState.ts +2 -0
- package/src/team/auth.js +186 -0
- package/src/team/auth.ts +298 -0
- package/src/team/cli.js +105 -0
- package/src/team/cli.ts +122 -0
- package/src/team/onlineToolContract.js +64 -0
- package/src/team/onlineToolContract.ts +109 -0
- package/src/team/toolPolicy.js +36 -0
- package/src/team/toolPolicy.ts +75 -0
- package/src/tools/advisor/advisor.js +115 -50
- package/src/tools/advisor/advisor.ts +124 -51
- package/src/tools/advisor/advisorDecision.js +71 -0
- package/src/tools/advisor/advisorDecision.ts +85 -0
- package/src/tools/agent/agent.js +18 -5
- package/src/tools/agent/agent.ts +21 -7
- package/src/tools/agent/agentControl.js +246 -0
- package/src/tools/agent/agentControl.ts +284 -0
- package/src/tools/agent/agentTaskService.js +40 -1
- package/src/tools/agent/agentTaskService.ts +44 -1
- package/src/tools/agent/agentTypes.js +14 -2
- package/src/tools/agent/agentTypes.ts +15 -2
- package/src/tools/applypatch/applyPatch.js +362 -0
- package/src/tools/applypatch/applyPatch.ts +428 -0
- package/src/tools/bash/bash.js +22 -15
- package/src/tools/bash/bash.ts +23 -17
- package/src/tools/capabilities.js +10 -5
- package/src/tools/capabilities.ts +10 -5
- package/src/tools/computeruse/computerIntent.js +32 -0
- package/src/tools/computeruse/computerIntent.ts +46 -0
- package/src/tools/computeruse/telemetryLog.js +12 -0
- package/src/tools/computeruse/telemetryLog.ts +16 -0
- package/src/tools/computeruse/vision/telemetry.js +1 -0
- package/src/tools/computeruse/vision/telemetry.ts +1 -0
- package/src/tools/effectiveToolSet.js +4 -0
- package/src/tools/effectiveToolSet.ts +4 -0
- package/src/tools/index.js +21 -2
- package/src/tools/index.ts +33 -2
- package/src/tools/presentresult/presentresult.js +6 -15
- package/src/tools/presentresult/presentresult.ts +6 -15
- package/src/tools/read/read.js +62 -9
- package/src/tools/read/read.ts +63 -9
- package/src/tools/shared/fileUtils.js +1 -2
- package/src/tools/shared/fileUtils.ts +1 -2
- package/src/tools/shared/toolOutputStore.js +23 -1
- package/src/tools/shared/toolOutputStore.ts +22 -1
- package/src/tools/skill/skill.js +9 -3
- package/src/tools/skill/skill.ts +8 -4
- package/src/tools/toolPromptGuides.js +125 -0
- package/src/tools/toolPromptGuides.ts +146 -0
- package/src/tools/webbrowser/agentBrowserPath.js +19 -9
- package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
- package/src/tools/webbrowser/browser.js +27 -11
- package/src/tools/webbrowser/browser.ts +68 -24
- package/src/tools/webbrowser/browserAutomation.js +329 -0
- package/src/tools/webbrowser/browserAutomation.ts +467 -0
- package/src/tools/webbrowser/browserAxState.js +97 -0
- package/src/tools/webbrowser/browserAxState.ts +142 -0
- package/src/tools/webbrowser/browserBatch.js +1578 -0
- package/src/tools/webbrowser/browserBatch.ts +2382 -0
- package/src/tools/webbrowser/browserCdp.js +208 -0
- package/src/tools/webbrowser/browserCdp.ts +255 -0
- package/src/tools/webbrowser/browserConditions.js +24 -0
- package/src/tools/webbrowser/browserConditions.ts +45 -0
- package/src/tools/webbrowser/browserContent.js +281 -0
- package/src/tools/webbrowser/browserContent.ts +438 -0
- package/src/tools/webbrowser/browserControl.js +131 -34
- package/src/tools/webbrowser/browserControl.ts +210 -56
- package/src/tools/webbrowser/browserDaemon.js +22 -14
- package/src/tools/webbrowser/browserDaemon.ts +54 -21
- package/src/tools/webbrowser/browserFrames.js +47 -0
- package/src/tools/webbrowser/browserFrames.ts +54 -0
- package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
- package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
- package/src/tools/webbrowser/browserLocator.js +140 -0
- package/src/tools/webbrowser/browserLocator.ts +194 -0
- package/src/tools/webbrowser/browserOutcome.js +10 -17
- package/src/tools/webbrowser/browserOutcome.ts +48 -31
- package/src/tools/webbrowser/browserRecovery.js +58 -0
- package/src/tools/webbrowser/browserRecovery.ts +83 -0
- package/src/tools/webbrowser/browserRunRegistry.js +35 -3
- package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
- package/src/tools/webbrowser/browserService.js +28 -2
- package/src/tools/webbrowser/browserService.ts +49 -9
- package/src/tools/webbrowser/browserStream.js +132 -33
- package/src/tools/webbrowser/browserStream.ts +224 -50
- package/src/tools/webbrowser/browserTabs.js +48 -15
- package/src/tools/webbrowser/browserTabs.ts +178 -96
- package/src/tools/webbrowser/webbrowser.js +679 -144
- package/src/tools/webbrowser/webbrowser.ts +975 -236
- package/src/tools/webfetch/preapproved.js +0 -2
- package/src/tools/webfetch/preapproved.ts +0 -2
- package/src/tools/websearch/websearch.js +129 -8
- package/src/tools/websearch/websearch.ts +138 -6
- package/src/types.ts +459 -40
- package/src/ui/App.js +377 -58
- package/src/ui/App.tsx +576 -111
- package/src/ui/LiveArea.js +14 -8
- package/src/ui/LiveArea.tsx +14 -10
- package/src/ui/MessageList.js +63 -20
- package/src/ui/MessageList.tsx +101 -16
- package/src/ui/ModelPicker.js +7 -4
- package/src/ui/ModelPicker.tsx +55 -8
- package/src/ui/hooks/useChat.js +152 -163
- package/src/ui/hooks/useChat.ts +396 -334
- package/src/ui/loginCommand.js +56 -0
- package/src/ui/loginCommand.ts +63 -0
- package/src/ui/toolProgress.js +41 -0
- package/src/ui/toolProgress.ts +44 -0
- package/src/web/browserBridge.js +20 -2
- package/src/web/browserBridge.ts +20 -2
- package/src/web/protocol.ts +1897 -194
- package/workflows/adaptive-research/workflow.mjs +86 -0
- package/workflows/e2e-capabilities/workflow.mjs +102 -0
- package/workflows/e2e-llm/workflow.mjs +52 -0
- package/workflows/e2e-smoke/workflow.mjs +59 -0
- package/workflows/e2e-vision-schema/README.md +48 -0
- package/workflows/e2e-vision-schema/workflow.mjs +149 -0
- package/workflows/e2e-write-greet/workflow.mjs +37 -0
- package/workflows/file-census/README.md +14 -0
- package/workflows/file-census/scripts/.gitkeep +2 -0
- package/workflows/file-census/workflow.mjs +110 -0
- package/workflows/goal/README.md +166 -0
- package/workflows/goal/prompts/audit.md +57 -0
- package/workflows/goal/prompts/contract.md +56 -0
- package/workflows/goal/prompts/iterate.md +37 -0
- package/workflows/goal/workflow.mjs +501 -0
- package/workflows/illustrate/README.md +90 -0
- package/workflows/illustrate/assets/engines.json +20 -0
- package/workflows/illustrate/assets/extract-baidu.js +21 -0
- package/workflows/illustrate/assets/extract-bing.js +33 -0
- package/workflows/illustrate/assets/extract-google.js +31 -0
- package/workflows/illustrate/workflow.mjs +301 -0
- package/workflows/plan/workflow.mjs +389 -0
- package/workflows/question-crops/README.md +35 -0
- package/workflows/question-crops/prompts/inventory.md +23 -0
- package/workflows/question-crops/scripts/image-ops.mjs +305 -0
- package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
- package/workflows/question-crops/workflow.mjs +172 -0
- package/workflows/ralph-loop/workflow.mjs +162 -0
- package/workflows/research/README.md +74 -0
- package/workflows/research/prompts/s0-goal.md +43 -0
- package/workflows/research/prompts/s1-early-exit.md +22 -0
- package/workflows/research/prompts/s1-map.md +58 -0
- package/workflows/research/prompts/s2-options.md +53 -0
- package/workflows/research/prompts/s3-deep-dive.md +56 -0
- package/workflows/research/prompts/s4-verify.md +74 -0
- package/workflows/research/prompts/s5-synthesis.md +81 -0
- package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
- package/workflows/research/prompts/s6-package.md +32 -0
- package/workflows/research/scripts/build-report.mjs +98 -0
- package/workflows/research/workflow.mjs +288 -0
- package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
- package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
- package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
- package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
- package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
- package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
- package/workflows/wechat-reading-shots/workflow.mjs +266 -0
- package/workflows/wrong-question-docx/README.md +55 -0
- package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
- package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
- package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
- package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
- package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
- package/workflows/wrong-question-docx/workflow.mjs +485 -0
- package/plugins/workflow-runner/src/agentCommon.js +0 -354
- package/plugins/workflow-runner/src/agentCommon.ts +0 -435
- package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
- package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
- package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
- package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
- package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
- package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
- package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
- package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
- package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
- package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
- package/plugins/workflow-runner/src/dslManifest.js +0 -72
- package/plugins/workflow-runner/src/dslManifest.ts +0 -127
- package/plugins/workflow-runner/src/expressions.js +0 -639
- package/plugins/workflow-runner/src/expressions.ts +0 -640
- package/plugins/workflow-runner/src/loader.js +0 -573
- package/plugins/workflow-runner/src/loader.ts +0 -721
- package/plugins/workflow-runner/src/multimodal.js +0 -38
- package/plugins/workflow-runner/src/multimodal.ts +0 -64
- package/plugins/workflow-runner/src/recovery.js +0 -126
- package/plugins/workflow-runner/src/recovery.ts +0 -156
- package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
- package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
- package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -42
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -52
- package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
- package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
- package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
- package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
- package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
- package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
- package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
- package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -227
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -274
- package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -61
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -72
- package/plugins/workflow-runner/src/stepPipeline.js +0 -277
- package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
- package/plugins/workflow-runner/test/agent.test.ts +0 -587
- package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
- package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
- package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
- package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -167
- package/plugins/workflow-runner/test/context-files.test.ts +0 -320
- package/plugins/workflow-runner/test/e2e.test.ts +0 -370
- package/plugins/workflow-runner/test/e2eNewLayout.test.ts +0 -314
- package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -234
- package/plugins/workflow-runner/test/expressions.test.ts +0 -439
- package/plugins/workflow-runner/test/foundation.test.ts +0 -126
- package/plugins/workflow-runner/test/graceful.test.ts +0 -139
- package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
- package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
- package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
- package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
- package/plugins/workflow-runner/test/loader.test.ts +0 -617
- package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
- package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
- package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
- package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -106
- package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
- package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
- package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -221
- package/plugins/workflow-runner/test/recovery.test.ts +0 -265
- package/plugins/workflow-runner/test/research.load.test.ts +0 -328
- package/plugins/workflow-runner/test/runner.test.ts +0 -911
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -436
- package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -198
- package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
- package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -354
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -324
- package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
- package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
- package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
- package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
- package/plugins/workflow-runner/test/word.load.test.ts +0 -208
- package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
- package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
- package/src/advisorState.js +0 -22
- package/src/cli/configWizard.js +0 -293
- package/src/tools/toolProgressBridge.js +0 -48
- package/workflows/adaptive-research.yaml +0 -50
- package/workflows/book-review-short.yaml +0 -125
- package/workflows/e2e-write-greet.yaml +0 -31
- package/workflows/illustrate.yaml +0 -227
- package/workflows/plan.yaml +0 -470
- package/workflows/ppt.yaml +0 -1670
- package/workflows/ralph-loop.yaml +0 -201
- package/workflows/research.yaml +0 -873
- package/workflows/schema.json +0 -176
- package/workflows/webppt.yaml +0 -496
- package/workflows/wechat-reading-shots.yaml +0 -315
- package/workflows/word.yaml +0 -720
- package/workflows/youtube-shorts.yaml +0 -202
- /package/workflows/{research.md → research/DESIGN.md} +0 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser locators shared by BrowserBatch and future visible-DOM adapters.
|
|
3
|
+
*
|
|
4
|
+
* This deliberately emits a small, self-contained browser expression instead
|
|
5
|
+
* of retaining DOM node handles. A locator is resolved again for every step,
|
|
6
|
+
* so a navigation or React re-render cannot silently reuse a stale element.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type BrowserLocator = {
|
|
10
|
+
by:
|
|
11
|
+
| 'css'
|
|
12
|
+
| 'ax'
|
|
13
|
+
| 'role'
|
|
14
|
+
| 'text'
|
|
15
|
+
| 'label'
|
|
16
|
+
| 'placeholder'
|
|
17
|
+
| 'alt'
|
|
18
|
+
| 'title'
|
|
19
|
+
| 'testid';
|
|
20
|
+
value: string;
|
|
21
|
+
/** AX node id (or numeric index printed by the latest snapshot). */
|
|
22
|
+
generation?: number;
|
|
23
|
+
name?: string;
|
|
24
|
+
exact?: boolean;
|
|
25
|
+
strict?: boolean;
|
|
26
|
+
nth?: number;
|
|
27
|
+
within?: BrowserLocator;
|
|
28
|
+
has_text?: string;
|
|
29
|
+
frame?: BrowserLocator[];
|
|
30
|
+
framePath?: BrowserLocator[];
|
|
31
|
+
frame_path?: BrowserLocator[];
|
|
32
|
+
/** CDP frame id returned by frameInfo; enables cross-origin frame context. */
|
|
33
|
+
frameId?: string;
|
|
34
|
+
frame_id?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const quote = (value: string) => JSON.stringify(value);
|
|
38
|
+
|
|
39
|
+
/** Build an expression returning `Element[]` in the selected document. */
|
|
40
|
+
export function buildLocatorExpression(
|
|
41
|
+
locator: BrowserLocator | undefined,
|
|
42
|
+
selector: string | undefined,
|
|
43
|
+
rootExpression = 'document',
|
|
44
|
+
): string {
|
|
45
|
+
if (selector) {
|
|
46
|
+
return `Array.from(${rootExpression}.querySelectorAll(${quote(selector)}))`;
|
|
47
|
+
}
|
|
48
|
+
if (!locator) throw new Error('step requires selector or locator');
|
|
49
|
+
|
|
50
|
+
if (locator.by === 'ax') {
|
|
51
|
+
throw new Error(
|
|
52
|
+
'AX locator must be resolved against the live accessibility tree first',
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const root = locator.within
|
|
57
|
+
? `${buildLocatorExpression(locator.within, undefined, rootExpression)}.flatMap((scope) => Array.from(scope.querySelectorAll('*')))`
|
|
58
|
+
: `Array.from(${rootExpression}.querySelectorAll('*'))`;
|
|
59
|
+
const value = quote(locator.value);
|
|
60
|
+
const exact = locator.exact === true;
|
|
61
|
+
const compare = (expression: string, expected = value) =>
|
|
62
|
+
exact
|
|
63
|
+
? `${expression}.trim()===${expected}`
|
|
64
|
+
: `${expression}.includes(${expected})`;
|
|
65
|
+
|
|
66
|
+
let predicate: string;
|
|
67
|
+
switch (locator.by) {
|
|
68
|
+
case 'css':
|
|
69
|
+
return applyFilters(
|
|
70
|
+
`Array.from(${rootExpression}.querySelectorAll(${value}))`,
|
|
71
|
+
locator,
|
|
72
|
+
);
|
|
73
|
+
case 'role': {
|
|
74
|
+
const name = locator.name ? quote(locator.name) : value;
|
|
75
|
+
const nameCompare = locator.name
|
|
76
|
+
? `(function(name){return ${exact ? `name.trim()===${name}` : `name.includes(${name})`};})(__name)`
|
|
77
|
+
: 'true';
|
|
78
|
+
predicate = `(__role===${value}&&${nameCompare})`;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case 'label':
|
|
82
|
+
predicate = `Array.from(__el.labels||[]).some((label)=>${compare("(label.textContent||'')")})`;
|
|
83
|
+
break;
|
|
84
|
+
case 'placeholder':
|
|
85
|
+
predicate = compare(`__el.getAttribute('placeholder')||''`);
|
|
86
|
+
break;
|
|
87
|
+
case 'alt':
|
|
88
|
+
predicate = compare(`__el.getAttribute('alt')||''`);
|
|
89
|
+
break;
|
|
90
|
+
case 'title':
|
|
91
|
+
predicate = compare(`__el.getAttribute('title')||''`);
|
|
92
|
+
break;
|
|
93
|
+
case 'testid':
|
|
94
|
+
predicate = compare(`__el.getAttribute('data-testid')||''`);
|
|
95
|
+
break;
|
|
96
|
+
case 'text':
|
|
97
|
+
predicate = `(!__el.children.length&&${compare("(__el.textContent||'')")})`;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const expression = `(() => {
|
|
102
|
+
const __accessibleName = (el) => {
|
|
103
|
+
const labelled = el.getAttribute('aria-label');
|
|
104
|
+
if (labelled) return labelled;
|
|
105
|
+
const ids = (el.getAttribute('aria-labelledby')||'').split(/\\s+/).filter(Boolean);
|
|
106
|
+
if (ids.length) return ids.map((id)=>${rootExpression}.getElementById(id)?.textContent||'').join(' ').trim();
|
|
107
|
+
if (el.labels?.length) return Array.from(el.labels).map((label)=>label.textContent||'').join(' ').trim();
|
|
108
|
+
return (el.innerText||el.textContent||'').trim();
|
|
109
|
+
};
|
|
110
|
+
const __implicitRole = (el) => {
|
|
111
|
+
const explicit = el.getAttribute('role');
|
|
112
|
+
if (explicit) return explicit;
|
|
113
|
+
const tag = el.tagName.toLowerCase();
|
|
114
|
+
if (tag === 'button') return 'button';
|
|
115
|
+
if (tag === 'a' && el.hasAttribute('href')) return 'link';
|
|
116
|
+
if (tag === 'textarea') return 'textbox';
|
|
117
|
+
if (tag === 'select') return 'combobox';
|
|
118
|
+
if (tag === 'input') {
|
|
119
|
+
const type = (el.getAttribute('type')||'text').toLowerCase();
|
|
120
|
+
if (type === 'checkbox') return 'checkbox';
|
|
121
|
+
if (type === 'radio') return 'radio';
|
|
122
|
+
if (type === 'button' || type === 'submit' || type === 'reset') return 'button';
|
|
123
|
+
return 'textbox';
|
|
124
|
+
}
|
|
125
|
+
return '';
|
|
126
|
+
};
|
|
127
|
+
return ${root}.filter((__el) => {
|
|
128
|
+
const __role = __implicitRole(__el);
|
|
129
|
+
const __name = __accessibleName(__el);
|
|
130
|
+
return ${predicate};
|
|
131
|
+
});
|
|
132
|
+
})()`;
|
|
133
|
+
|
|
134
|
+
const filtered = applyFilters(expression, locator);
|
|
135
|
+
const framePath = locator.frame ?? locator.framePath ?? locator.frame_path;
|
|
136
|
+
if (!framePath?.length) return filtered;
|
|
137
|
+
|
|
138
|
+
// Same-origin frame traversal is explicit. Cross-origin frames cannot be
|
|
139
|
+
// inspected through Runtime.evaluate and must be handled by a backend that
|
|
140
|
+
// can attach to the child target; never pretend the parent matched instead.
|
|
141
|
+
let frameRoot = rootExpression;
|
|
142
|
+
for (const frame of framePath) {
|
|
143
|
+
const frameSelector =
|
|
144
|
+
frame.by === 'css'
|
|
145
|
+
? quote(frame.value)
|
|
146
|
+
: quote(`iframe[title*="${frame.value.replaceAll('"', '\\"')}"]`);
|
|
147
|
+
frameRoot = `(() => { const frame = ${frameRoot}.querySelector(${frameSelector}); if (!frame || !frame.contentDocument) throw new Error('frameLocator could not resolve a same-origin iframe'); return frame.contentDocument; })()`;
|
|
148
|
+
}
|
|
149
|
+
return buildLocatorExpression(
|
|
150
|
+
{
|
|
151
|
+
...locator,
|
|
152
|
+
frame: undefined,
|
|
153
|
+
framePath: undefined,
|
|
154
|
+
frame_path: undefined,
|
|
155
|
+
frameId: undefined,
|
|
156
|
+
frame_id: undefined,
|
|
157
|
+
},
|
|
158
|
+
selector,
|
|
159
|
+
frameRoot,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function applyFilters(expression: string, locator: BrowserLocator): string {
|
|
164
|
+
const filters: string[] = [];
|
|
165
|
+
if (locator.has_text)
|
|
166
|
+
filters.push(`(__el.textContent||'').includes(${quote(locator.has_text)})`);
|
|
167
|
+
return filters.length
|
|
168
|
+
? `Array.from(${expression}).filter((__el)=>${filters.join('&&')})`
|
|
169
|
+
: expression;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function buildTargetExpression(
|
|
173
|
+
locator: BrowserLocator | undefined,
|
|
174
|
+
selector: string | undefined,
|
|
175
|
+
): string {
|
|
176
|
+
const elements = buildLocatorExpression(locator, selector);
|
|
177
|
+
const strict = locator?.strict !== false;
|
|
178
|
+
const nth = locator?.nth ?? 0;
|
|
179
|
+
return `(() => {
|
|
180
|
+
const __matches = ${elements}.filter((__candidate) => {
|
|
181
|
+
const __style = getComputedStyle(__candidate);
|
|
182
|
+
const __rect = __candidate.getBoundingClientRect();
|
|
183
|
+
return __style.display!=='none' && __style.visibility!=='hidden' && __style.pointerEvents!=='none' && (!!__rect.width || !!__rect.height);
|
|
184
|
+
});
|
|
185
|
+
if (!__matches.length) throw new Error('Locator matched no elements');
|
|
186
|
+
if (__matches.length > 1 && ${strict}) throw new Error('Locator matched '+__matches.length+' elements; use nth or strict:false');
|
|
187
|
+
const __el = __matches[${nth}];
|
|
188
|
+
if (!__el) throw new Error('Locator nth is outside the matched elements');
|
|
189
|
+
const __style = getComputedStyle(__el);
|
|
190
|
+
const __rect = __el.getBoundingClientRect();
|
|
191
|
+
if (__style.display==='none'||__style.visibility==='hidden'||__style.pointerEvents==='none'||(!__rect.width&&!__rect.height)) throw new Error('Locator matched a hidden element');
|
|
192
|
+
return __el;
|
|
193
|
+
})()`;
|
|
194
|
+
}
|
|
@@ -85,16 +85,9 @@ export function describeEffect(verdict, action) {
|
|
|
85
85
|
case 'changed':
|
|
86
86
|
return null; // 正常路径不加噪声
|
|
87
87
|
case 'unchanged':
|
|
88
|
-
return
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
+ `请对照下面的最新快照确认目标是否正确,不要用同一个选择器直接重试。`
|
|
92
|
-
// 豁免了行才提。这不是免责声明,是让模型知道这个结论有多硬:
|
|
93
|
-
// 被豁免的位置上,「自己在跳」和「被我改了又在跳」不可区分。
|
|
94
|
-
+ (verdict.maskedLines > 0
|
|
95
|
-
? `\n(说明:页面上有 ${verdict.maskedLines} 行在持续自行变化(时钟/轮询一类),`
|
|
96
|
-
+ `已从判定中排除。若你这次操作本该影响的正是这些位置,这个结论可能不准。)`
|
|
97
|
-
: ''));
|
|
88
|
+
return `⚠️ 动作已发出,但页面结构在动作前后**完全没有变化**。\n这通常意味着三种情况之一:${action} 命中的不是可交互元素、点击被遮挡层拦住了、或者这个操作本来就不改变页面(比如点了一个纯展示文本)。\n请对照下面的最新快照确认目标是否正确,不要用同一个选择器直接重试。${verdict.maskedLines > 0
|
|
89
|
+
? `\n(说明:页面上有 ${verdict.maskedLines} 行在持续自行变化(时钟/轮询一类),已从判定中排除。若你这次操作本该影响的正是这些位置,这个结论可能不准。)`
|
|
90
|
+
: ''}`;
|
|
98
91
|
case 'unknown':
|
|
99
92
|
return '(未能取得动作前后的页面快照,无法判断这次操作是否生效——请自行核对。)';
|
|
100
93
|
}
|
|
@@ -179,7 +172,7 @@ export function suggestSelectorCandidates(snapshot, selector, limit = 5) {
|
|
|
179
172
|
export class BrowserAttemptLedger {
|
|
180
173
|
counts = new Map();
|
|
181
174
|
static key(conversationKey, action, selector) {
|
|
182
|
-
return `${conversationKey}
|
|
175
|
+
return `${conversationKey}\u0000${action}\u0000${selector ?? ''}`;
|
|
183
176
|
}
|
|
184
177
|
recordFailure(conversationKey, action, selector) {
|
|
185
178
|
const key = BrowserAttemptLedger.key(conversationKey, action, selector);
|
|
@@ -191,12 +184,12 @@ export class BrowserAttemptLedger {
|
|
|
191
184
|
this.counts.delete(BrowserAttemptLedger.key(conversationKey, action, selector));
|
|
192
185
|
}
|
|
193
186
|
failures(conversationKey, action, selector) {
|
|
194
|
-
return this.counts.get(BrowserAttemptLedger.key(conversationKey, action, selector)) ?? 0;
|
|
187
|
+
return (this.counts.get(BrowserAttemptLedger.key(conversationKey, action, selector)) ?? 0);
|
|
195
188
|
}
|
|
196
189
|
/** 会话结束时清账,避免长期驻留。 */
|
|
197
190
|
forget(conversationKey) {
|
|
198
191
|
for (const key of [...this.counts.keys()]) {
|
|
199
|
-
if (key.startsWith(`${conversationKey}
|
|
192
|
+
if (key.startsWith(`${conversationKey}\u0000`))
|
|
200
193
|
this.counts.delete(key);
|
|
201
194
|
}
|
|
202
195
|
}
|
|
@@ -228,7 +221,9 @@ export function budgetSnapshot(snapshot, opts = {}) {
|
|
|
228
221
|
if (lines.length <= maxLines && snapshot.length <= maxChars)
|
|
229
222
|
return snapshot;
|
|
230
223
|
const hasRef = (line) => /@e\d+|\bref=e\d+/.test(line);
|
|
231
|
-
const clip = (line) => line.length <= MAX_SNAPSHOT_LINE_CHARS
|
|
224
|
+
const clip = (line) => line.length <= MAX_SNAPSHOT_LINE_CHARS
|
|
225
|
+
? line
|
|
226
|
+
: `${line.slice(0, MAX_SNAPSHOT_LINE_CHARS)}…`;
|
|
232
227
|
// 两轮**只挑不排**:先在原序上标记要留的可操作行,再用余量补纯文本行。
|
|
233
228
|
const keep = new Array(lines.length).fill(false);
|
|
234
229
|
let kept = 0;
|
|
@@ -267,7 +262,5 @@ export function budgetSnapshot(snapshot, opts = {}) {
|
|
|
267
262
|
}
|
|
268
263
|
flushGap();
|
|
269
264
|
const dropped = lines.length - kept;
|
|
270
|
-
return
|
|
271
|
-
+ `… 为控制上下文,共省略 ${dropped} 行(按原顺序保留,优先保住可引用的元素)。`
|
|
272
|
-
+ '需要完整页面请单独调用 snapshot,或用 getContent 读取正文。');
|
|
265
|
+
return `${out.join('\n')}\n… 为控制上下文,共省略 ${dropped} 行(按原顺序保留,优先保住可引用的元素)。需要完整页面请单独调用 snapshot,或用 getContent 读取正文。`;
|
|
273
266
|
}
|
|
@@ -82,36 +82,35 @@ export function classifyPageEffect(
|
|
|
82
82
|
after: string | undefined,
|
|
83
83
|
noiseAfter?: string,
|
|
84
84
|
): BrowserEffectVerdict {
|
|
85
|
-
if (before === undefined || after === undefined)
|
|
85
|
+
if (before === undefined || after === undefined)
|
|
86
|
+
return { effect: 'unknown', maskedLines: 0 };
|
|
86
87
|
const actual = differingLineIndexes(before, after);
|
|
87
88
|
if (actual === null) return { effect: 'changed', maskedLines: 0 }; // 行数变了 = 结构变了
|
|
88
89
|
if (actual.size === 0) return { effect: 'unchanged', maskedLines: 0 };
|
|
89
|
-
const noisy =
|
|
90
|
+
const noisy =
|
|
91
|
+
noiseAfter !== undefined ? differingLineIndexes(after, noiseAfter) : null;
|
|
90
92
|
// 没有可用的噪声基线时不做扣除:宁可把「生效了」判宽,也不能把一次真实变化
|
|
91
93
|
// 误报成没变——那会让模型放弃一条本来走通了的路。
|
|
92
94
|
if (noisy === null) return { effect: 'changed', maskedLines: 0 };
|
|
93
|
-
for (const index of actual)
|
|
95
|
+
for (const index of actual)
|
|
96
|
+
if (!noisy.has(index)) return { effect: 'changed', maskedLines: 0 };
|
|
94
97
|
return { effect: 'unchanged', maskedLines: actual.size };
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
/** 给模型的效果说明。措辞要让它知道**下一步该干什么**,不只是知道结果。 */
|
|
98
|
-
export function describeEffect(
|
|
101
|
+
export function describeEffect(
|
|
102
|
+
verdict: BrowserEffectVerdict,
|
|
103
|
+
action: string,
|
|
104
|
+
): string | null {
|
|
99
105
|
switch (verdict.effect) {
|
|
100
106
|
case 'changed':
|
|
101
107
|
return null; // 正常路径不加噪声
|
|
102
108
|
case 'unchanged':
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// 豁免了行才提。这不是免责声明,是让模型知道这个结论有多硬:
|
|
109
|
-
// 被豁免的位置上,「自己在跳」和「被我改了又在跳」不可区分。
|
|
110
|
-
+ (verdict.maskedLines > 0
|
|
111
|
-
? `\n(说明:页面上有 ${verdict.maskedLines} 行在持续自行变化(时钟/轮询一类),`
|
|
112
|
-
+ `已从判定中排除。若你这次操作本该影响的正是这些位置,这个结论可能不准。)`
|
|
113
|
-
: '')
|
|
114
|
-
);
|
|
109
|
+
return `⚠️ 动作已发出,但页面结构在动作前后**完全没有变化**。\n这通常意味着三种情况之一:${action} 命中的不是可交互元素、点击被遮挡层拦住了、或者这个操作本来就不改变页面(比如点了一个纯展示文本)。\n请对照下面的最新快照确认目标是否正确,不要用同一个选择器直接重试。${
|
|
110
|
+
verdict.maskedLines > 0
|
|
111
|
+
? `\n(说明:页面上有 ${verdict.maskedLines} 行在持续自行变化(时钟/轮询一类),已从判定中排除。若你这次操作本该影响的正是这些位置,这个结论可能不准。)`
|
|
112
|
+
: ''
|
|
113
|
+
}`;
|
|
115
114
|
case 'unknown':
|
|
116
115
|
return '(未能取得动作前后的页面快照,无法判断这次操作是否生效——请自行核对。)';
|
|
117
116
|
}
|
|
@@ -206,29 +205,47 @@ export function suggestSelectorCandidates(
|
|
|
206
205
|
export class BrowserAttemptLedger {
|
|
207
206
|
private counts = new Map<string, number>();
|
|
208
207
|
|
|
209
|
-
private static key(
|
|
210
|
-
|
|
208
|
+
private static key(
|
|
209
|
+
conversationKey: string,
|
|
210
|
+
action: string,
|
|
211
|
+
selector?: string,
|
|
212
|
+
): string {
|
|
213
|
+
return `${conversationKey}\u0000${action}\u0000${selector ?? ''}`;
|
|
211
214
|
}
|
|
212
215
|
|
|
213
|
-
recordFailure(
|
|
216
|
+
recordFailure(
|
|
217
|
+
conversationKey: string,
|
|
218
|
+
action: string,
|
|
219
|
+
selector?: string,
|
|
220
|
+
): number {
|
|
214
221
|
const key = BrowserAttemptLedger.key(conversationKey, action, selector);
|
|
215
222
|
const next = (this.counts.get(key) ?? 0) + 1;
|
|
216
223
|
this.counts.set(key, next);
|
|
217
224
|
return next;
|
|
218
225
|
}
|
|
219
226
|
|
|
220
|
-
recordSuccess(
|
|
221
|
-
|
|
227
|
+
recordSuccess(
|
|
228
|
+
conversationKey: string,
|
|
229
|
+
action: string,
|
|
230
|
+
selector?: string,
|
|
231
|
+
): void {
|
|
232
|
+
this.counts.delete(
|
|
233
|
+
BrowserAttemptLedger.key(conversationKey, action, selector),
|
|
234
|
+
);
|
|
222
235
|
}
|
|
223
236
|
|
|
224
237
|
failures(conversationKey: string, action: string, selector?: string): number {
|
|
225
|
-
return
|
|
238
|
+
return (
|
|
239
|
+
this.counts.get(
|
|
240
|
+
BrowserAttemptLedger.key(conversationKey, action, selector),
|
|
241
|
+
) ?? 0
|
|
242
|
+
);
|
|
226
243
|
}
|
|
227
244
|
|
|
228
245
|
/** 会话结束时清账,避免长期驻留。 */
|
|
229
246
|
forget(conversationKey: string): void {
|
|
230
247
|
for (const key of [...this.counts.keys()]) {
|
|
231
|
-
if (key.startsWith(`${conversationKey}
|
|
248
|
+
if (key.startsWith(`${conversationKey}\u0000`)) this.counts.delete(key);
|
|
232
249
|
}
|
|
233
250
|
}
|
|
234
251
|
}
|
|
@@ -267,7 +284,9 @@ export function budgetSnapshot(
|
|
|
267
284
|
|
|
268
285
|
const hasRef = (line: string): boolean => /@e\d+|\bref=e\d+/.test(line);
|
|
269
286
|
const clip = (line: string): string =>
|
|
270
|
-
line.length <= MAX_SNAPSHOT_LINE_CHARS
|
|
287
|
+
line.length <= MAX_SNAPSHOT_LINE_CHARS
|
|
288
|
+
? line
|
|
289
|
+
: `${line.slice(0, MAX_SNAPSHOT_LINE_CHARS)}…`;
|
|
271
290
|
|
|
272
291
|
// 两轮**只挑不排**:先在原序上标记要留的可操作行,再用余量补纯文本行。
|
|
273
292
|
const keep = new Array<boolean>(lines.length).fill(false);
|
|
@@ -292,15 +311,13 @@ export function budgetSnapshot(
|
|
|
292
311
|
gap = 0;
|
|
293
312
|
};
|
|
294
313
|
for (let i = 0; i < lines.length; i += 1) {
|
|
295
|
-
if (keep[i]) {
|
|
296
|
-
|
|
314
|
+
if (keep[i]) {
|
|
315
|
+
flushGap();
|
|
316
|
+
out.push(clip(lines[i]));
|
|
317
|
+
} else gap += 1;
|
|
297
318
|
}
|
|
298
319
|
flushGap();
|
|
299
320
|
|
|
300
321
|
const dropped = lines.length - kept;
|
|
301
|
-
return (
|
|
302
|
-
`${out.join('\n')}\n`
|
|
303
|
-
+ `… 为控制上下文,共省略 ${dropped} 行(按原顺序保留,优先保住可引用的元素)。`
|
|
304
|
-
+ '需要完整页面请单独调用 snapshot,或用 getContent 读取正文。'
|
|
305
|
-
);
|
|
322
|
+
return `${out.join('\n')}\n… 为控制上下文,共省略 ${dropped} 行(按原顺序保留,优先保住可引用的元素)。需要完整页面请单独调用 snapshot,或用 getContent 读取正文。`;
|
|
306
323
|
}
|
|
@@ -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)
|