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
|
@@ -91,21 +91,12 @@ async function call(input) {
|
|
|
91
91
|
return { ok: false, error: '网页结果只支持 http 或 https 链接' };
|
|
92
92
|
}
|
|
93
93
|
const hasFailedFile = files.some((file) => file.verification === 'failed');
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
? 'final'
|
|
101
|
-
: files.length === 0 && links.length > 0
|
|
102
|
-
? 'final'
|
|
103
|
-
: 'draft';
|
|
104
|
-
const warnings = files.some((file) => file.verification === 'not_run')
|
|
105
|
-
? ['文件已提交,但尚未通过结构化验证。']
|
|
106
|
-
: hasFailedFile
|
|
107
|
-
? ['至少一个文件未通过结构化验证。']
|
|
108
|
-
: [];
|
|
94
|
+
// `not_run` means no *additional* format/content validator was available. It
|
|
95
|
+
// is not a failed delivery: this tool has already verified that the file exists,
|
|
96
|
+
// is inside the workspace, and captured its size and SHA-256. Only an explicit
|
|
97
|
+
// failed validator should turn an otherwise usable result into a warning.
|
|
98
|
+
const status = hasFailedFile ? 'draft' : 'final';
|
|
99
|
+
const warnings = hasFailedFile ? ['至少一个文件未通过结构化验证。'] : [];
|
|
109
100
|
const manifest = {
|
|
110
101
|
status,
|
|
111
102
|
summary: input.summary?.trim() || (files.length ? `已提交 ${files.length} 个文件` : `已提交 ${links.length} 个网页结果`),
|
|
@@ -101,21 +101,12 @@ async function call(input: PresentResultInput): Promise<ToolResult> {
|
|
|
101
101
|
return { ok: false, error: '网页结果只支持 http 或 https 链接' };
|
|
102
102
|
}
|
|
103
103
|
const hasFailedFile = files.some((file) => file.verification === 'failed');
|
|
104
|
-
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
? 'final'
|
|
111
|
-
: files.length === 0 && links.length > 0
|
|
112
|
-
? 'final'
|
|
113
|
-
: 'draft';
|
|
114
|
-
const warnings = files.some((file) => file.verification === 'not_run')
|
|
115
|
-
? ['文件已提交,但尚未通过结构化验证。']
|
|
116
|
-
: hasFailedFile
|
|
117
|
-
? ['至少一个文件未通过结构化验证。']
|
|
118
|
-
: [];
|
|
104
|
+
// `not_run` means no *additional* format/content validator was available. It
|
|
105
|
+
// is not a failed delivery: this tool has already verified that the file exists,
|
|
106
|
+
// is inside the workspace, and captured its size and SHA-256. Only an explicit
|
|
107
|
+
// failed validator should turn an otherwise usable result into a warning.
|
|
108
|
+
const status = hasFailedFile ? 'draft' : 'final';
|
|
109
|
+
const warnings = hasFailedFile ? ['至少一个文件未通过结构化验证。'] : [];
|
|
119
110
|
const manifest: DeliverableManifest = {
|
|
120
111
|
status,
|
|
121
112
|
summary: input.summary?.trim() || (files.length ? `已提交 ${files.length} 个文件` : `已提交 ${links.length} 个网页结果`),
|
package/src/tools/read/read.js
CHANGED
|
@@ -8,6 +8,7 @@ import { join } from 'node:path';
|
|
|
8
8
|
import { promisify } from 'node:util';
|
|
9
9
|
import { randomUUID } from 'node:crypto';
|
|
10
10
|
import { z } from 'zod';
|
|
11
|
+
import { dlog } from '../../utils/debugLog.js';
|
|
11
12
|
import { toToolParameters } from '../../utils/zodToJson.js';
|
|
12
13
|
import { DEFAULT_MAX_RESULT_SIZE_CHARS, MAX_FILE_SIZE_BYTES, MAX_LINES_TO_READ, } from '../types.js';
|
|
13
14
|
import { fileActivityMetadata } from '../shared/fileActivity.js';
|
|
@@ -15,6 +16,7 @@ import { hashFileBytes, recordRead, } from '../shared/fileState.js';
|
|
|
15
16
|
import { hasBinaryExtension, isBlockedDevicePath, readTextFile, resolveToolPath, } from '../shared/fileUtils.js';
|
|
16
17
|
import { filePathField } from '../shared/schemas.js';
|
|
17
18
|
import { pruneRuntimeOutputs, sessionRuntimeDir, } from '../../session/runtimeStore.js';
|
|
19
|
+
import { detectImageMime, resolveLocalImage, } from '../../images/imageResolver.js';
|
|
18
20
|
const inputSchema = z.object({
|
|
19
21
|
file_path: filePathField('读取'),
|
|
20
22
|
offset: z
|
|
@@ -46,6 +48,12 @@ const IMAGE_EXTENSIONS = new Set([
|
|
|
46
48
|
'.gif',
|
|
47
49
|
'.webp',
|
|
48
50
|
'.bmp',
|
|
51
|
+
'.tif',
|
|
52
|
+
'.tiff',
|
|
53
|
+
'.avif',
|
|
54
|
+
'.heic',
|
|
55
|
+
'.heif',
|
|
56
|
+
'.svg',
|
|
49
57
|
]);
|
|
50
58
|
const MAX_IMAGE_BYTES = 25 * 1024 * 1024;
|
|
51
59
|
const MAX_NOTEBOOK_BYTES = 32 * 1024 * 1024;
|
|
@@ -168,10 +176,15 @@ async function call(input, state, cwd, runtimeIdentity) {
|
|
|
168
176
|
};
|
|
169
177
|
}
|
|
170
178
|
const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');
|
|
171
|
-
|
|
179
|
+
// ⚠️ 释放要调**加载任务**的 destroy,不是文档代理的:pdfjs v6 的
|
|
180
|
+
// PDFDocumentProxy 上没有 destroy(只有 cleanup),写成 document.destroy()
|
|
181
|
+
// 会在 finally 里抛 TypeError,被外层 catch 成「PDF 解析失败」——
|
|
182
|
+
// 于是每一次 PDF 读取都失败,且错误文案完全指不向真因。
|
|
183
|
+
const loadingTask = pdfjs.getDocument({
|
|
172
184
|
data: new Uint8Array(bytes),
|
|
173
185
|
useSystemFonts: true,
|
|
174
|
-
})
|
|
186
|
+
});
|
|
187
|
+
const document = await loadingTask.promise;
|
|
175
188
|
try {
|
|
176
189
|
const requested = parsePdfPages(input.pages, document.numPages);
|
|
177
190
|
if (requested.length > 20)
|
|
@@ -209,7 +222,7 @@ async function call(input, state, cwd, runtimeIdentity) {
|
|
|
209
222
|
};
|
|
210
223
|
}
|
|
211
224
|
finally {
|
|
212
|
-
await
|
|
225
|
+
await loadingTask.destroy().catch(() => undefined);
|
|
213
226
|
}
|
|
214
227
|
}
|
|
215
228
|
catch (error) {
|
|
@@ -220,16 +233,29 @@ async function call(input, state, cwd, runtimeIdentity) {
|
|
|
220
233
|
};
|
|
221
234
|
}
|
|
222
235
|
}
|
|
223
|
-
|
|
236
|
+
const imageBytes = info.size <= MAX_IMAGE_BYTES
|
|
237
|
+
? await readFile(filePath).catch(() => null)
|
|
238
|
+
: null;
|
|
239
|
+
const detectedImage = imageBytes ? detectImageMime(imageBytes) : null;
|
|
240
|
+
if (IMAGE_EXTENSIONS.has(ext) || detectedImage) {
|
|
224
241
|
if (info.size > MAX_IMAGE_BYTES)
|
|
225
242
|
return { ok: false, error: `图片超过 ${MAX_IMAGE_BYTES} 字节上限` };
|
|
226
|
-
|
|
243
|
+
let resolvedImage;
|
|
244
|
+
try {
|
|
245
|
+
resolvedImage = await resolveLocalImage(filePath, {
|
|
246
|
+
maxInputBytes: MAX_IMAGE_BYTES,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
return { ok: false, error: `图片读取失败:${error.message}` };
|
|
251
|
+
}
|
|
252
|
+
const bytes = imageBytes ?? await readFile(filePath);
|
|
227
253
|
recordRead(filePath, { contentHash: hashFileBytes(bytes) }, state);
|
|
228
254
|
return {
|
|
229
255
|
ok: true,
|
|
230
|
-
content: `已读取图片:${filePath}(${info.size}
|
|
256
|
+
content: `已读取图片:${filePath}(${info.size} 字节,实际格式 ${resolvedImage.originalMime})${resolvedImage.transformed ? `;已规范化为 ${resolvedImage.mime}` : ''}。图像已附加供视觉模型直接检查。`,
|
|
231
257
|
metadata: await fileActivityMetadata(filePath, 'read', 'not_run', 'source', cwd),
|
|
232
|
-
visionImages: [{ source:
|
|
258
|
+
visionImages: [{ source: resolvedImage.path }],
|
|
233
259
|
visionNote: '这是 Read 工具读取的本地图片,请直接检查图像内容。',
|
|
234
260
|
};
|
|
235
261
|
}
|
|
@@ -347,10 +373,30 @@ async function renderPdfPages(document, filePath, pages, identity) {
|
|
|
347
373
|
const dynamicImport = new Function('specifier', 'return import(specifier)');
|
|
348
374
|
let createCanvas;
|
|
349
375
|
try {
|
|
350
|
-
|
|
376
|
+
const napi = await dynamicImport('@napi-rs/canvas');
|
|
377
|
+
createCanvas = napi.createCanvas;
|
|
378
|
+
// ⚠️ **必须在 pdfjs 之前把这几个全局占住**,否则 PDF 转图 100% 失败。
|
|
379
|
+
//
|
|
380
|
+
// pdfjs 的 legacy 构建在 Node 环境下自己往 globalThis 装一套 Path2D /
|
|
381
|
+
// DOMMatrix / ImageData polyfill。渲染时它 `new Path2D()` 拿到的是**它自己那份**,
|
|
382
|
+
// 转手调 `ctx.fill(path, 'evenodd')` 交给 @napi-rs/canvas —— 而 napi 的原生绑定
|
|
383
|
+
// 只认自己的 `Path`,于是每一页都抛
|
|
384
|
+
// `Value is none of these types \`String\`, \`Path\``
|
|
385
|
+
// 两份 polyfill 撞车,谁都没错,就是不是同一个对象。
|
|
386
|
+
//
|
|
387
|
+
// 实测:五种 render 参数组合(canvas / canvasContext / canvas:null / 带不带
|
|
388
|
+
// standardFontDataUrl)全部同样失败 —— 也就是说 `canvas` 那个类型错**不是**病因,
|
|
389
|
+
// 修类型并不能让它跑起来。把全局换成 napi 的实现之后第 1 页立刻渲染出 137KB PNG。
|
|
390
|
+
for (const name of ['Path2D', 'DOMMatrix', 'ImageData']) {
|
|
391
|
+
const impl = napi[name];
|
|
392
|
+
if (impl)
|
|
393
|
+
globalThis[name] = impl;
|
|
394
|
+
}
|
|
351
395
|
}
|
|
352
|
-
catch {
|
|
353
|
-
|
|
396
|
+
catch (e) {
|
|
397
|
+
// optional adapter:没装 @napi-rs/canvas 就回落 pdftoppm。
|
|
398
|
+
// 但「没装」和「装了却加载失败」对用户是两件事,别都吞成前者。
|
|
399
|
+
dlog('read:pdf', `@napi-rs/canvas 不可用:${e instanceof Error ? e.message : e}`);
|
|
354
400
|
}
|
|
355
401
|
if (createCanvas) {
|
|
356
402
|
try {
|
|
@@ -358,8 +404,13 @@ async function renderPdfPages(document, filePath, pages, identity) {
|
|
|
358
404
|
const page = await document.getPage(pageNumber);
|
|
359
405
|
const viewport = page.getViewport({ scale: 2 });
|
|
360
406
|
const canvas = createCanvas(Math.ceil(viewport.width), Math.ceil(viewport.height));
|
|
407
|
+
// ⚠️ pdfjs v6 的 RenderParameters 里 `canvas` 是**必填**、`canvasContext`
|
|
408
|
+
// 反而降级为可选且不推荐;文档明说「若必须用 context,canvas 必须为 null」。
|
|
409
|
+
// 只给 canvasContext 会类型不过(`bun run typecheck` 唯一的红)。
|
|
410
|
+
// 运行时它有默认值 `canvas = canvasContext.canvas` 所以不炸 —— 但类型要对。
|
|
361
411
|
await page.render({
|
|
362
412
|
canvasContext: canvas.getContext('2d'),
|
|
413
|
+
canvas: null,
|
|
363
414
|
viewport,
|
|
364
415
|
}).promise;
|
|
365
416
|
const target = join(outputDir, `${outputPrefix}-page-${pageNumber}.png`);
|
|
@@ -369,7 +420,14 @@ async function renderPdfPages(document, filePath, pages, identity) {
|
|
|
369
420
|
void pruneRuntimeOutputs();
|
|
370
421
|
return images;
|
|
371
422
|
}
|
|
372
|
-
catch {
|
|
423
|
+
catch (e) {
|
|
424
|
+
// ⚠️ 别把这里改回裸 catch。上面那个 Path2D 冲突就是被它藏住的:
|
|
425
|
+
// Canvas 每页都抛 → images 清空 → 回落 pdftoppm。装了 poppler 的机器照样
|
|
426
|
+
// 出图(只是分辨率更低、多一次进程),**完全看不出来**;没装 poppler 的机器
|
|
427
|
+
// (多数 Windows、绿色版 U 盘)则返回 `ok:true` + 0 张图 + 一句
|
|
428
|
+
// 「视觉渲染不可用(Canvas 与 pdftoppm 均不可用)」——而 Canvas 明明可用,
|
|
429
|
+
// 只是抛了。错误文案把人指向「装依赖」,真因一个字都查不到。
|
|
430
|
+
dlog('read:pdf', `Canvas 渲染失败:${e instanceof Error ? e.message : e}`);
|
|
373
431
|
images.length = 0;
|
|
374
432
|
}
|
|
375
433
|
}
|
package/src/tools/read/read.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
|
|
12
12
|
import type { Tool, ToolCallContext, ToolResult } from '../../types.js';
|
|
13
|
+
import { dlog } from '../../utils/debugLog.js';
|
|
13
14
|
import { toToolParameters } from '../../utils/zodToJson.js';
|
|
14
15
|
import {
|
|
15
16
|
DEFAULT_MAX_RESULT_SIZE_CHARS,
|
|
@@ -34,6 +35,10 @@ import {
|
|
|
34
35
|
sessionRuntimeDir,
|
|
35
36
|
type RuntimeIdentity,
|
|
36
37
|
} from '../../session/runtimeStore.js';
|
|
38
|
+
import {
|
|
39
|
+
detectImageMime,
|
|
40
|
+
resolveLocalImage,
|
|
41
|
+
} from '../../images/imageResolver.js';
|
|
37
42
|
|
|
38
43
|
const inputSchema = z.object({
|
|
39
44
|
file_path: filePathField('读取'),
|
|
@@ -70,6 +75,12 @@ const IMAGE_EXTENSIONS = new Set([
|
|
|
70
75
|
'.gif',
|
|
71
76
|
'.webp',
|
|
72
77
|
'.bmp',
|
|
78
|
+
'.tif',
|
|
79
|
+
'.tiff',
|
|
80
|
+
'.avif',
|
|
81
|
+
'.heic',
|
|
82
|
+
'.heif',
|
|
83
|
+
'.svg',
|
|
73
84
|
]);
|
|
74
85
|
const MAX_IMAGE_BYTES = 25 * 1024 * 1024;
|
|
75
86
|
const MAX_NOTEBOOK_BYTES = 32 * 1024 * 1024;
|
|
@@ -219,10 +230,15 @@ async function call(
|
|
|
219
230
|
};
|
|
220
231
|
}
|
|
221
232
|
const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');
|
|
222
|
-
|
|
233
|
+
// ⚠️ 释放要调**加载任务**的 destroy,不是文档代理的:pdfjs v6 的
|
|
234
|
+
// PDFDocumentProxy 上没有 destroy(只有 cleanup),写成 document.destroy()
|
|
235
|
+
// 会在 finally 里抛 TypeError,被外层 catch 成「PDF 解析失败」——
|
|
236
|
+
// 于是每一次 PDF 读取都失败,且错误文案完全指不向真因。
|
|
237
|
+
const loadingTask = pdfjs.getDocument({
|
|
223
238
|
data: new Uint8Array(bytes),
|
|
224
239
|
useSystemFonts: true,
|
|
225
|
-
})
|
|
240
|
+
});
|
|
241
|
+
const document = await loadingTask.promise;
|
|
226
242
|
try {
|
|
227
243
|
const requested = parsePdfPages(input.pages, document.numPages);
|
|
228
244
|
if (requested.length > 20)
|
|
@@ -279,7 +295,7 @@ async function call(
|
|
|
279
295
|
: {}),
|
|
280
296
|
};
|
|
281
297
|
} finally {
|
|
282
|
-
await
|
|
298
|
+
await loadingTask.destroy().catch(() => undefined);
|
|
283
299
|
}
|
|
284
300
|
} catch (error) {
|
|
285
301
|
const message = (error as Error).message;
|
|
@@ -290,14 +306,26 @@ async function call(
|
|
|
290
306
|
}
|
|
291
307
|
}
|
|
292
308
|
|
|
293
|
-
|
|
309
|
+
const imageBytes = info.size <= MAX_IMAGE_BYTES
|
|
310
|
+
? await readFile(filePath).catch(() => null)
|
|
311
|
+
: null;
|
|
312
|
+
const detectedImage = imageBytes ? detectImageMime(imageBytes) : null;
|
|
313
|
+
if (IMAGE_EXTENSIONS.has(ext) || detectedImage) {
|
|
294
314
|
if (info.size > MAX_IMAGE_BYTES)
|
|
295
315
|
return { ok: false, error: `图片超过 ${MAX_IMAGE_BYTES} 字节上限` };
|
|
296
|
-
|
|
316
|
+
let resolvedImage;
|
|
317
|
+
try {
|
|
318
|
+
resolvedImage = await resolveLocalImage(filePath, {
|
|
319
|
+
maxInputBytes: MAX_IMAGE_BYTES,
|
|
320
|
+
});
|
|
321
|
+
} catch (error) {
|
|
322
|
+
return { ok: false, error: `图片读取失败:${(error as Error).message}` };
|
|
323
|
+
}
|
|
324
|
+
const bytes = imageBytes ?? await readFile(filePath);
|
|
297
325
|
recordRead(filePath, { contentHash: hashFileBytes(bytes) }, state);
|
|
298
326
|
return {
|
|
299
327
|
ok: true,
|
|
300
|
-
content: `已读取图片:${filePath}(${info.size}
|
|
328
|
+
content: `已读取图片:${filePath}(${info.size} 字节,实际格式 ${resolvedImage.originalMime})${resolvedImage.transformed ? `;已规范化为 ${resolvedImage.mime}` : ''}。图像已附加供视觉模型直接检查。`,
|
|
301
329
|
metadata: await fileActivityMetadata(
|
|
302
330
|
filePath,
|
|
303
331
|
'read',
|
|
@@ -305,7 +333,7 @@ async function call(
|
|
|
305
333
|
'source',
|
|
306
334
|
cwd,
|
|
307
335
|
),
|
|
308
|
-
visionImages: [{ source:
|
|
336
|
+
visionImages: [{ source: resolvedImage.path }],
|
|
309
337
|
visionNote: '这是 Read 工具读取的本地图片,请直接检查图像内容。',
|
|
310
338
|
};
|
|
311
339
|
}
|
|
@@ -470,9 +498,28 @@ async function renderPdfPages(
|
|
|
470
498
|
}>;
|
|
471
499
|
let createCanvas: Awaited<ReturnType<typeof dynamicImport>>['createCanvas'];
|
|
472
500
|
try {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
501
|
+
const napi = await dynamicImport('@napi-rs/canvas');
|
|
502
|
+
createCanvas = napi.createCanvas;
|
|
503
|
+
// ⚠️ **必须在 pdfjs 之前把这几个全局占住**,否则 PDF 转图 100% 失败。
|
|
504
|
+
//
|
|
505
|
+
// pdfjs 的 legacy 构建在 Node 环境下自己往 globalThis 装一套 Path2D /
|
|
506
|
+
// DOMMatrix / ImageData polyfill。渲染时它 `new Path2D()` 拿到的是**它自己那份**,
|
|
507
|
+
// 转手调 `ctx.fill(path, 'evenodd')` 交给 @napi-rs/canvas —— 而 napi 的原生绑定
|
|
508
|
+
// 只认自己的 `Path`,于是每一页都抛
|
|
509
|
+
// `Value is none of these types \`String\`, \`Path\``
|
|
510
|
+
// 两份 polyfill 撞车,谁都没错,就是不是同一个对象。
|
|
511
|
+
//
|
|
512
|
+
// 实测:五种 render 参数组合(canvas / canvasContext / canvas:null / 带不带
|
|
513
|
+
// standardFontDataUrl)全部同样失败 —— 也就是说 `canvas` 那个类型错**不是**病因,
|
|
514
|
+
// 修类型并不能让它跑起来。把全局换成 napi 的实现之后第 1 页立刻渲染出 137KB PNG。
|
|
515
|
+
for (const name of ['Path2D', 'DOMMatrix', 'ImageData'] as const) {
|
|
516
|
+
const impl = (napi as Record<string, unknown>)[name];
|
|
517
|
+
if (impl) (globalThis as Record<string, unknown>)[name] = impl;
|
|
518
|
+
}
|
|
519
|
+
} catch (e) {
|
|
520
|
+
// optional adapter:没装 @napi-rs/canvas 就回落 pdftoppm。
|
|
521
|
+
// 但「没装」和「装了却加载失败」对用户是两件事,别都吞成前者。
|
|
522
|
+
dlog('read:pdf', `@napi-rs/canvas 不可用:${e instanceof Error ? e.message : e}`);
|
|
476
523
|
}
|
|
477
524
|
if (createCanvas) {
|
|
478
525
|
try {
|
|
@@ -483,8 +530,13 @@ async function renderPdfPages(
|
|
|
483
530
|
Math.ceil(viewport.width),
|
|
484
531
|
Math.ceil(viewport.height),
|
|
485
532
|
);
|
|
533
|
+
// ⚠️ pdfjs v6 的 RenderParameters 里 `canvas` 是**必填**、`canvasContext`
|
|
534
|
+
// 反而降级为可选且不推荐;文档明说「若必须用 context,canvas 必须为 null」。
|
|
535
|
+
// 只给 canvasContext 会类型不过(`bun run typecheck` 唯一的红)。
|
|
536
|
+
// 运行时它有默认值 `canvas = canvasContext.canvas` 所以不炸 —— 但类型要对。
|
|
486
537
|
await page.render({
|
|
487
538
|
canvasContext: canvas.getContext('2d') as never,
|
|
539
|
+
canvas: null,
|
|
488
540
|
viewport,
|
|
489
541
|
}).promise;
|
|
490
542
|
const target = join(
|
|
@@ -496,7 +548,14 @@ async function renderPdfPages(
|
|
|
496
548
|
}
|
|
497
549
|
void pruneRuntimeOutputs();
|
|
498
550
|
return images;
|
|
499
|
-
} catch {
|
|
551
|
+
} catch (e) {
|
|
552
|
+
// ⚠️ 别把这里改回裸 catch。上面那个 Path2D 冲突就是被它藏住的:
|
|
553
|
+
// Canvas 每页都抛 → images 清空 → 回落 pdftoppm。装了 poppler 的机器照样
|
|
554
|
+
// 出图(只是分辨率更低、多一次进程),**完全看不出来**;没装 poppler 的机器
|
|
555
|
+
// (多数 Windows、绿色版 U 盘)则返回 `ok:true` + 0 张图 + 一句
|
|
556
|
+
// 「视觉渲染不可用(Canvas 与 pdftoppm 均不可用)」——而 Canvas 明明可用,
|
|
557
|
+
// 只是抛了。错误文案把人指向「装依赖」,真因一个字都查不到。
|
|
558
|
+
dlog('read:pdf', `Canvas 渲染失败:${e instanceof Error ? e.message : e}`);
|
|
500
559
|
images.length = 0;
|
|
501
560
|
}
|
|
502
561
|
}
|
|
@@ -115,7 +115,7 @@ export function applyLineEnding(content, ending) {
|
|
|
115
115
|
// -------------------- 3.5 二进制扩展名拦截 --------------------
|
|
116
116
|
// 上游 D:\code\kakadeai\constants\files.ts:5-112 BINARY_EXTENSIONS Set 的裁剪版:
|
|
117
117
|
// 故意剔除 .log / .csv / .tsv / .dat / .data / .lock —— 这些经常被人当文本读。
|
|
118
|
-
// 保留纯二进制:图片 / 文档 / 可执行 / 编译产物 / 压缩包 / 多媒体 / 字体 /
|
|
118
|
+
// 保留纯二进制:图片 / 文档 / 可执行 / 编译产物 / 压缩包 / 多媒体 / 字体 / wasm。
|
|
119
119
|
export const BINARY_EXTENSIONS = new Set([
|
|
120
120
|
'.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.ico', '.tiff', '.tif',
|
|
121
121
|
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
|
@@ -124,7 +124,6 @@ export const BINARY_EXTENSIONS = new Set([
|
|
|
124
124
|
'.zip', '.tar', '.gz', '.bz2', '.7z', '.rar', '.iso',
|
|
125
125
|
'.mp3', '.mp4', '.mov', '.avi', '.mkv', '.wav', '.flac', '.ogg',
|
|
126
126
|
'.ttf', '.otf', '.woff', '.woff2',
|
|
127
|
-
'.sqlite', '.sqlite3', '.db',
|
|
128
127
|
'.psd', '.ai', '.bin', '.wasm',
|
|
129
128
|
]);
|
|
130
129
|
export function hasBinaryExtension(filePath) {
|
|
@@ -152,7 +152,7 @@ export function applyLineEnding(content: string, ending: LineEndingType): string
|
|
|
152
152
|
|
|
153
153
|
// 上游 D:\code\kakadeai\constants\files.ts:5-112 BINARY_EXTENSIONS Set 的裁剪版:
|
|
154
154
|
// 故意剔除 .log / .csv / .tsv / .dat / .data / .lock —— 这些经常被人当文本读。
|
|
155
|
-
// 保留纯二进制:图片 / 文档 / 可执行 / 编译产物 / 压缩包 / 多媒体 / 字体 /
|
|
155
|
+
// 保留纯二进制:图片 / 文档 / 可执行 / 编译产物 / 压缩包 / 多媒体 / 字体 / wasm。
|
|
156
156
|
export const BINARY_EXTENSIONS = new Set<string>([
|
|
157
157
|
'.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.ico', '.tiff', '.tif',
|
|
158
158
|
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
|
@@ -161,7 +161,6 @@ export const BINARY_EXTENSIONS = new Set<string>([
|
|
|
161
161
|
'.zip', '.tar', '.gz', '.bz2', '.7z', '.rar', '.iso',
|
|
162
162
|
'.mp3', '.mp4', '.mov', '.avi', '.mkv', '.wav', '.flac', '.ogg',
|
|
163
163
|
'.ttf', '.otf', '.woff', '.woff2',
|
|
164
|
-
'.sqlite', '.sqlite3', '.db',
|
|
165
164
|
'.psd', '.ai', '.bin', '.wasm',
|
|
166
165
|
]);
|
|
167
166
|
|
|
@@ -1,17 +1,48 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* 永久删除守卫:把「绕过回收站的删除」从 shell 里挡回去,逼模型改用可恢复的 Trash。
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ 可靠性边界(与 src/irreversible/rules.ts 同一标准,别高估它):
|
|
5
|
+
* 这是**防模型误删**的护栏,不是对抗性沙盒。判据是对命令文本做正则匹配,
|
|
6
|
+
* 因此下列写法一律漏过,且**有意不追**(越追越像军备竞赛,误伤率先爆):
|
|
7
|
+
* · 绝对路径调用 /bin/rm -rf x
|
|
8
|
+
* · 间接执行 xargs rm / find -exec rm / echo $(rm x)
|
|
9
|
+
* · 换语言 python -c "import shutil; shutil.rmtree('x')"
|
|
10
|
+
* · 跨 shell 转发 cmd 里 powershell -c "Remove-Item x"
|
|
11
|
+
* 真正兜底的是「模型没有恶意」+ Trash 提供了更顺手的替代路径,不是这张正则表。
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ `shell` 必须是**实际执行命令的那个 shell**,不是调用它的工具名。
|
|
14
|
+
* Bash 工具在 win32 找不到 Git Bash 时会回落 cmd.exe(见 tools/bash/bash.ts 的
|
|
15
|
+
* WIN_BASH 探测,且它的 description 会主动告诉模型「这里是 cmd」),那时模型写的是
|
|
16
|
+
* cmd 语法(del / rd),若仍按 'bash' 匹配 —— 三层防护全部落空、文件直接永久丢失。
|
|
17
|
+
*/
|
|
18
|
+
/** POSIX shell:删除动词在命令位置(串首或 ; & | 换行之后)。 */
|
|
2
19
|
const BASH_DELETE = [
|
|
3
20
|
/(?:^|[;&|\n])\s*(?:sudo\s+)?(?:rm|unlink|rmdir)\b/i,
|
|
4
21
|
/(?:^|[;&|\n])\s*find\b[^\n;&|]*\s-delete\b/i,
|
|
5
22
|
];
|
|
23
|
+
/**
|
|
24
|
+
* cmd.exe:del / erase 删文件,rd / rmdir 删目录,一律不进回收站。
|
|
25
|
+
* 分隔符**必须含 `&`** —— cmd 用 `&` / `&&` 串命令,这正是 POWERSHELL_DELETE
|
|
26
|
+
* 的分隔符类 `[;|\n]` 不能直接复用的原因。
|
|
27
|
+
* `rm` 一并收进来:Git 的 usr\bin 可能在 PATH 上(此时 rm.exe 可用),而 cmd 本身没有 rm。
|
|
28
|
+
*/
|
|
29
|
+
const CMD_DELETE = [/(?:^|[;&|\n])\s*(?:del|erase|rd|rmdir|rm)\b/i];
|
|
6
30
|
const GIT_CLEAN = /(?:^|[;&|\n])\s*git\b[^\n;&|]*\bclean\b([^\n;&|]*)/i;
|
|
7
31
|
const POWERSHELL_DELETE = [
|
|
8
32
|
/(?:^|[;|\n])\s*(?:Remove-Item|Clear-RecycleBin)\b/i,
|
|
9
33
|
/(?:^|[;|\n])\s*(?:del|erase|rd|rmdir|rm|ri)\b/i,
|
|
10
34
|
/\.Delete\s*\(/i,
|
|
11
35
|
];
|
|
36
|
+
const RULES = {
|
|
37
|
+
bash: BASH_DELETE,
|
|
38
|
+
cmd: CMD_DELETE,
|
|
39
|
+
powershell: POWERSHELL_DELETE,
|
|
40
|
+
};
|
|
12
41
|
export function permanentDeleteReason(command, shell) {
|
|
13
|
-
let hit =
|
|
14
|
-
|
|
42
|
+
let hit = RULES[shell].some((pattern) => pattern.test(command));
|
|
43
|
+
// `git clean -f` 三种 shell 里都能跑、都永久删除,所以不按 shell 分支。
|
|
44
|
+
// `-n` / `--dry-run` 是只读预演,必须放行(否则模型连"先看看会删什么"都做不到)。
|
|
45
|
+
if (!hit) {
|
|
15
46
|
const gitClean = GIT_CLEAN.exec(command);
|
|
16
47
|
const options = gitClean?.[1] ?? '';
|
|
17
48
|
const dryRun = /--dry-run\b/.test(options) ||
|
|
@@ -1,22 +1,59 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* 永久删除守卫:把「绕过回收站的删除」从 shell 里挡回去,逼模型改用可恢复的 Trash。
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ 可靠性边界(与 src/irreversible/rules.ts 同一标准,别高估它):
|
|
5
|
+
* 这是**防模型误删**的护栏,不是对抗性沙盒。判据是对命令文本做正则匹配,
|
|
6
|
+
* 因此下列写法一律漏过,且**有意不追**(越追越像军备竞赛,误伤率先爆):
|
|
7
|
+
* · 绝对路径调用 /bin/rm -rf x
|
|
8
|
+
* · 间接执行 xargs rm / find -exec rm / echo $(rm x)
|
|
9
|
+
* · 换语言 python -c "import shutil; shutil.rmtree('x')"
|
|
10
|
+
* · 跨 shell 转发 cmd 里 powershell -c "Remove-Item x"
|
|
11
|
+
* 真正兜底的是「模型没有恶意」+ Trash 提供了更顺手的替代路径,不是这张正则表。
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ `shell` 必须是**实际执行命令的那个 shell**,不是调用它的工具名。
|
|
14
|
+
* Bash 工具在 win32 找不到 Git Bash 时会回落 cmd.exe(见 tools/bash/bash.ts 的
|
|
15
|
+
* WIN_BASH 探测,且它的 description 会主动告诉模型「这里是 cmd」),那时模型写的是
|
|
16
|
+
* cmd 语法(del / rd),若仍按 'bash' 匹配 —— 三层防护全部落空、文件直接永久丢失。
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** POSIX shell:删除动词在命令位置(串首或 ; & | 换行之后)。 */
|
|
2
20
|
const BASH_DELETE = [
|
|
3
21
|
/(?:^|[;&|\n])\s*(?:sudo\s+)?(?:rm|unlink|rmdir)\b/i,
|
|
4
22
|
/(?:^|[;&|\n])\s*find\b[^\n;&|]*\s-delete\b/i,
|
|
5
23
|
];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* cmd.exe:del / erase 删文件,rd / rmdir 删目录,一律不进回收站。
|
|
27
|
+
* 分隔符**必须含 `&`** —— cmd 用 `&` / `&&` 串命令,这正是 POWERSHELL_DELETE
|
|
28
|
+
* 的分隔符类 `[;|\n]` 不能直接复用的原因。
|
|
29
|
+
* `rm` 一并收进来:Git 的 usr\bin 可能在 PATH 上(此时 rm.exe 可用),而 cmd 本身没有 rm。
|
|
30
|
+
*/
|
|
31
|
+
const CMD_DELETE = [/(?:^|[;&|\n])\s*(?:del|erase|rd|rmdir|rm)\b/i];
|
|
32
|
+
|
|
6
33
|
const GIT_CLEAN = /(?:^|[;&|\n])\s*git\b[^\n;&|]*\bclean\b([^\n;&|]*)/i;
|
|
34
|
+
|
|
7
35
|
const POWERSHELL_DELETE = [
|
|
8
36
|
/(?:^|[;|\n])\s*(?:Remove-Item|Clear-RecycleBin)\b/i,
|
|
9
37
|
/(?:^|[;|\n])\s*(?:del|erase|rd|rmdir|rm|ri)\b/i,
|
|
10
38
|
/\.Delete\s*\(/i,
|
|
11
39
|
];
|
|
40
|
+
|
|
41
|
+
export type ShellKind = 'bash' | 'cmd' | 'powershell';
|
|
42
|
+
|
|
43
|
+
const RULES: Readonly<Record<ShellKind, RegExp[]>> = {
|
|
44
|
+
bash: BASH_DELETE,
|
|
45
|
+
cmd: CMD_DELETE,
|
|
46
|
+
powershell: POWERSHELL_DELETE,
|
|
47
|
+
};
|
|
48
|
+
|
|
12
49
|
export function permanentDeleteReason(
|
|
13
50
|
command: string,
|
|
14
|
-
shell:
|
|
51
|
+
shell: ShellKind,
|
|
15
52
|
): string | undefined {
|
|
16
|
-
let hit = (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (!hit
|
|
53
|
+
let hit = RULES[shell].some((pattern) => pattern.test(command));
|
|
54
|
+
// `git clean -f` 三种 shell 里都能跑、都永久删除,所以不按 shell 分支。
|
|
55
|
+
// `-n` / `--dry-run` 是只读预演,必须放行(否则模型连"先看看会删什么"都做不到)。
|
|
56
|
+
if (!hit) {
|
|
20
57
|
const gitClean = GIT_CLEAN.exec(command);
|
|
21
58
|
const options = gitClean?.[1] ?? '';
|
|
22
59
|
const dryRun =
|
|
@@ -15,6 +15,10 @@ export class ToolOutputStore {
|
|
|
15
15
|
diskBytes = 0;
|
|
16
16
|
bytes = 0;
|
|
17
17
|
complete = true;
|
|
18
|
+
/** finish() 已调用:hash 已 digest,此后不能再 update(见 append 头注)。 */
|
|
19
|
+
sealed = false;
|
|
20
|
+
/** finish() 的结果缓存 —— digest 只能算一次,二次调用必须拿到同一份。 */
|
|
21
|
+
settled;
|
|
18
22
|
path;
|
|
19
23
|
constructor(identity) {
|
|
20
24
|
const dir = join(sessionRuntimeDir(identity), 'outputs');
|
|
@@ -22,6 +26,18 @@ export class ToolOutputStore {
|
|
|
22
26
|
this.path = join(dir, `procout_${randomUUID()}.log`);
|
|
23
27
|
}
|
|
24
28
|
append(source, value) {
|
|
29
|
+
// ⚠️ finish() 之后到达的迟到数据:**绝不能抛**。
|
|
30
|
+
//
|
|
31
|
+
// `bash.ts` 的 spawnRunner 按 `'exit'` 结算(不能等 `'close'`,否则被 daemon
|
|
32
|
+
// 继承的 pipe 会把它拖成无限挂),代价就是 exit 之后仍可能有 chunk 到达。
|
|
33
|
+
// 那时 hash 已经 digest 过,`hash.update()` 会抛 ERR_CRYPTO_HASH_FINALIZED,
|
|
34
|
+
// 而这个抛错发生在 stdout 的 'data' 回调里 —— 没有任何人接得住。
|
|
35
|
+
//
|
|
36
|
+
// 正确做法是如实记成不完整(数据确实没被算进已报出的结果),然后安静返回。
|
|
37
|
+
if (this.sealed) {
|
|
38
|
+
this.complete = false;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
25
41
|
const raw = Buffer.from(value);
|
|
26
42
|
if (this.bytes >= MAX_BYTES) {
|
|
27
43
|
this.complete = false;
|
|
@@ -70,6 +86,11 @@ export class ToolOutputStore {
|
|
|
70
86
|
this.stream?.once('drain', callback);
|
|
71
87
|
}
|
|
72
88
|
async finish() {
|
|
89
|
+
// 幂等:digest 只能算一次。二次调用返回同一份快照,但 `complete` 取最新值 ——
|
|
90
|
+
// 如果之间来过迟到数据,调用方有权知道这份输出其实不完整了。
|
|
91
|
+
if (this.settled)
|
|
92
|
+
return { ...this.settled, complete: this.complete };
|
|
93
|
+
this.sealed = true;
|
|
73
94
|
if (this.stream) {
|
|
74
95
|
try {
|
|
75
96
|
await new Promise((resolve, reject) => {
|
|
@@ -85,7 +106,7 @@ export class ToolOutputStore {
|
|
|
85
106
|
const preview = (head, tail) => this.stream && tail && !head.endsWith(tail)
|
|
86
107
|
? `${head}\n... [middle stored on disk] ...\n${tail}`
|
|
87
108
|
: head;
|
|
88
|
-
|
|
109
|
+
this.settled = {
|
|
89
110
|
stdout: preview(this.stdout, this.stdoutTail),
|
|
90
111
|
stderr: preview(this.stderr, this.stderrTail),
|
|
91
112
|
...(this.stream ? { path: this.path } : {}),
|
|
@@ -93,5 +114,6 @@ export class ToolOutputStore {
|
|
|
93
114
|
sha256: this.stream ? this.diskHash.digest('hex') : this.hash.digest('hex'),
|
|
94
115
|
complete: this.complete,
|
|
95
116
|
};
|
|
117
|
+
return this.settled;
|
|
96
118
|
}
|
|
97
119
|
}
|
|
@@ -32,6 +32,10 @@ export class ToolOutputStore {
|
|
|
32
32
|
private diskBytes = 0;
|
|
33
33
|
private bytes = 0;
|
|
34
34
|
private complete = true;
|
|
35
|
+
/** finish() 已调用:hash 已 digest,此后不能再 update(见 append 头注)。 */
|
|
36
|
+
private sealed = false;
|
|
37
|
+
/** finish() 的结果缓存 —— digest 只能算一次,二次调用必须拿到同一份。 */
|
|
38
|
+
private settled?: StoredOutput;
|
|
35
39
|
readonly path: string;
|
|
36
40
|
constructor(identity: RuntimeIdentity) {
|
|
37
41
|
const dir = join(sessionRuntimeDir(identity), 'outputs');
|
|
@@ -39,6 +43,18 @@ export class ToolOutputStore {
|
|
|
39
43
|
this.path = join(dir, `procout_${randomUUID()}.log`);
|
|
40
44
|
}
|
|
41
45
|
append(source: 'stdout' | 'stderr', value: Buffer | string): boolean {
|
|
46
|
+
// ⚠️ finish() 之后到达的迟到数据:**绝不能抛**。
|
|
47
|
+
//
|
|
48
|
+
// `bash.ts` 的 spawnRunner 按 `'exit'` 结算(不能等 `'close'`,否则被 daemon
|
|
49
|
+
// 继承的 pipe 会把它拖成无限挂),代价就是 exit 之后仍可能有 chunk 到达。
|
|
50
|
+
// 那时 hash 已经 digest 过,`hash.update()` 会抛 ERR_CRYPTO_HASH_FINALIZED,
|
|
51
|
+
// 而这个抛错发生在 stdout 的 'data' 回调里 —— 没有任何人接得住。
|
|
52
|
+
//
|
|
53
|
+
// 正确做法是如实记成不完整(数据确实没被算进已报出的结果),然后安静返回。
|
|
54
|
+
if (this.sealed) {
|
|
55
|
+
this.complete = false;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
42
58
|
const raw = Buffer.from(value);
|
|
43
59
|
if (this.bytes >= MAX_BYTES) {
|
|
44
60
|
this.complete = false;
|
|
@@ -83,6 +99,10 @@ export class ToolOutputStore {
|
|
|
83
99
|
this.stream?.once('drain', callback);
|
|
84
100
|
}
|
|
85
101
|
async finish(): Promise<StoredOutput> {
|
|
102
|
+
// 幂等:digest 只能算一次。二次调用返回同一份快照,但 `complete` 取最新值 ——
|
|
103
|
+
// 如果之间来过迟到数据,调用方有权知道这份输出其实不完整了。
|
|
104
|
+
if (this.settled) return { ...this.settled, complete: this.complete };
|
|
105
|
+
this.sealed = true;
|
|
86
106
|
if (this.stream) {
|
|
87
107
|
try {
|
|
88
108
|
await new Promise<void>((resolve, reject) => {
|
|
@@ -98,7 +118,7 @@ export class ToolOutputStore {
|
|
|
98
118
|
this.stream && tail && !head.endsWith(tail)
|
|
99
119
|
? `${head}\n... [middle stored on disk] ...\n${tail}`
|
|
100
120
|
: head;
|
|
101
|
-
|
|
121
|
+
this.settled = {
|
|
102
122
|
stdout: preview(this.stdout, this.stdoutTail),
|
|
103
123
|
stderr: preview(this.stderr, this.stderrTail),
|
|
104
124
|
...(this.stream ? { path: this.path } : {}),
|
|
@@ -106,5 +126,6 @@ export class ToolOutputStore {
|
|
|
106
126
|
sha256: this.stream ? this.diskHash!.digest('hex') : this.hash.digest('hex'),
|
|
107
127
|
complete: this.complete,
|
|
108
128
|
};
|
|
129
|
+
return this.settled;
|
|
109
130
|
}
|
|
110
131
|
}
|