minimal-agent 0.10.0 → 0.12.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 +11 -3
- package/package.json +52 -5
- package/plugins/computer_use/.claude-plugin/plugin.json +6 -0
- package/plugins/computer_use/README.md +94 -0
- package/plugins/computer_use/app/_template.md +142 -0
- package/plugins/computer_use/app/chrome.md +165 -0
- package/plugins/computer_use/app/finder.md +76 -0
- package/plugins/computer_use/app/macos-system-settings.md +81 -0
- package/plugins/computer_use/app/microsoft-excel.md +80 -0
- package/plugins/computer_use/app/microsoft-powerpoint.md +80 -0
- package/plugins/computer_use/app/microsoft-word.md +79 -0
- package/plugins/computer_use/app/wechat.md +413 -0
- package/plugins/computer_use/app/weread.md +319 -0
- package/plugins/computer_use/commands/computeruse.md +25 -0
- package/plugins/computer_use/commands/cu.md +9 -0
- package/plugins/computer_use/plugin.js +26 -0
- package/plugins/computer_use/plugin.ts +30 -0
- package/plugins/computer_use/src/capture.js +90 -0
- package/plugins/computer_use/src/capture.ts +104 -0
- package/plugins/computer_use/src/commands.js +91 -0
- package/plugins/computer_use/src/commands.ts +103 -0
- package/plugins/computer_use/src/daemon.js +134 -0
- package/plugins/computer_use/src/daemon.ts +161 -0
- package/plugins/computer_use/src/manuals.js +135 -0
- package/plugins/computer_use/src/manuals.ts +159 -0
- package/plugins/computer_use/src/verify.js +168 -0
- package/plugins/computer_use/src/verify.ts +219 -0
- package/plugins/computer_use/test/commands.test.ts +73 -0
- package/plugins/cron-runner/plugin.ts +38 -0
- package/plugins/cron-runner/src/commands.ts +373 -0
- package/plugins/cron-runner/src/cronTime.ts +155 -0
- package/plugins/cron-runner/src/lock.ts +79 -0
- package/plugins/cron-runner/src/runs.ts +99 -0
- package/plugins/cron-runner/src/scheduleEngine.ts +76 -0
- package/plugins/cron-runner/src/scheduler.ts +376 -0
- package/plugins/cron-runner/src/schema.ts +139 -0
- package/plugins/cron-runner/src/service.ts +193 -0
- package/plugins/cron-runner/src/store.ts +333 -0
- package/plugins/cron-runner/src/types.ts +141 -0
- package/plugins/cron-runner/test/cronTime.test.ts +41 -0
- package/plugins/cron-runner/test/plugin-integration.test.ts +47 -0
- package/plugins/cron-runner/test/scheduleEngine.test.ts +41 -0
- package/plugins/cron-runner/test/service.test.ts +76 -0
- package/plugins/cron-runner/test/store-and-scheduler.test.ts +501 -0
- package/plugins/workflow-runner/plugin.js +56 -2
- package/plugins/workflow-runner/plugin.ts +113 -0
- package/plugins/workflow-runner/src/agentCommon.js +43 -4
- package/plugins/workflow-runner/src/agentCommon.ts +435 -0
- package/plugins/workflow-runner/src/controlFlow/branch.js +40 -0
- package/plugins/workflow-runner/src/controlFlow/branch.ts +46 -0
- package/plugins/workflow-runner/src/controlFlow/loop.js +211 -0
- package/plugins/workflow-runner/src/controlFlow/loop.ts +248 -0
- package/plugins/workflow-runner/src/controlFlow/pause.js +129 -0
- package/plugins/workflow-runner/src/controlFlow/pause.ts +141 -0
- package/plugins/workflow-runner/src/controlFlow/signal.js +21 -0
- package/plugins/workflow-runner/src/controlFlow/signal.ts +29 -0
- package/plugins/workflow-runner/src/controlFlow/types.js +18 -0
- package/plugins/workflow-runner/src/controlFlow/types.ts +63 -0
- package/plugins/workflow-runner/src/dslManifest.ts +127 -0
- package/plugins/workflow-runner/src/expressions.ts +622 -0
- package/plugins/workflow-runner/src/index.js +13 -4
- package/plugins/workflow-runner/src/index.ts +338 -0
- package/plugins/workflow-runner/src/jsonPayload.ts +84 -0
- package/plugins/workflow-runner/src/loader.js +23 -0
- package/plugins/workflow-runner/src/loader.ts +721 -0
- package/plugins/workflow-runner/src/multimodal.js +2 -1
- package/plugins/workflow-runner/src/multimodal.ts +64 -0
- package/plugins/workflow-runner/src/recovery.ts +149 -0
- package/plugins/workflow-runner/src/runner.js +75 -659
- package/plugins/workflow-runner/src/runner.ts +604 -0
- package/plugins/workflow-runner/src/stepExecutors/agent.ts +234 -0
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +50 -0
- package/plugins/workflow-runner/src/stepExecutors/echo.js +31 -0
- package/plugins/workflow-runner/src/stepExecutors/echo.ts +38 -0
- package/plugins/workflow-runner/src/stepExecutors/llm.ts +133 -0
- package/plugins/workflow-runner/src/stepExecutors/set.js +49 -0
- package/plugins/workflow-runner/src/stepExecutors/set.ts +56 -0
- package/plugins/workflow-runner/src/stepExecutors/skill.js +5 -6
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +140 -0
- package/plugins/workflow-runner/src/stepExecutors/tool.js +80 -8
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +256 -0
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +68 -0
- package/plugins/workflow-runner/src/stepPipeline.js +277 -0
- package/plugins/workflow-runner/src/stepPipeline.ts +366 -0
- package/plugins/workflow-runner/src/taskIdentity.ts +405 -0
- package/plugins/workflow-runner/src/toolRuntime.js +48 -0
- package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
- package/plugins/workflow-runner/src/types.js +37 -1
- package/plugins/workflow-runner/src/types.ts +602 -0
- package/plugins/workflow-runner/src/workflowState.ts +733 -0
- package/plugins/workflow-runner/test/agent.test.ts +587 -0
- package/plugins/workflow-runner/test/agentLlmError.test.ts +373 -0
- package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +65 -0
- package/plugins/workflow-runner/test/captureMisbind.test.ts +99 -0
- package/plugins/workflow-runner/test/cli.e2e.test.ts +147 -0
- package/plugins/workflow-runner/test/context-files.test.ts +320 -0
- package/plugins/workflow-runner/test/e2e.test.ts +370 -0
- package/plugins/workflow-runner/test/engineRobustness.test.ts +226 -0
- package/plugins/workflow-runner/test/expressions.test.ts +397 -0
- package/plugins/workflow-runner/test/foundation.test.ts +126 -0
- package/plugins/workflow-runner/test/graceful.test.ts +139 -0
- package/plugins/workflow-runner/test/greedyInputs.load.test.ts +47 -0
- package/plugins/workflow-runner/test/hostCapability.test.ts +108 -0
- package/plugins/workflow-runner/test/illustrate.load.test.ts +183 -0
- package/plugins/workflow-runner/test/jsonPayload.test.ts +83 -0
- package/plugins/workflow-runner/test/loader.test.ts +617 -0
- package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +33 -0
- package/plugins/workflow-runner/test/multimodal.test.ts +342 -0
- package/plugins/workflow-runner/test/output-schema.test.ts +213 -0
- package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +87 -0
- package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +269 -0
- package/plugins/workflow-runner/test/ppt.load.test.ts +1220 -0
- package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +220 -0
- package/plugins/workflow-runner/test/recovery.test.ts +265 -0
- package/plugins/workflow-runner/test/runner.test.ts +911 -0
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +379 -0
- package/plugins/workflow-runner/test/skillTurnDone.test.ts +71 -0
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +150 -0
- package/plugins/workflow-runner/test/stepPipeline.test.ts +114 -0
- package/plugins/workflow-runner/test/taskArbitration.test.ts +338 -0
- package/plugins/workflow-runner/test/taskIdentity.test.ts +237 -0
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +270 -0
- package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +221 -0
- package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +29 -0
- package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
- package/plugins/workflow-runner/test/webppt.load.test.ts +250 -0
- package/plugins/workflow-runner/test/word.load.test.ts +208 -0
- package/plugins/workflow-runner/test/workflows.smoke.test.ts +76 -0
- package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +207 -0
- package/skills/browser-automation/SKILL.md +9 -19
- package/skills/web-crawler/SKILL.md +9 -11
- package/src/askBridge.ts +249 -0
- package/src/bootstrap/cwdArg.js +10 -3
- package/src/bootstrap/cwdArg.ts +31 -0
- package/src/bootstrap/workingDir.ts +48 -0
- package/src/cli/args.js +70 -7
- package/src/cli/args.ts +146 -0
- package/src/cli/computerUse.js +185 -0
- package/src/cli/computerUse.ts +221 -0
- package/src/cli/configMenu.js +274 -202
- package/src/cli/configMenu.tsx +521 -0
- package/src/cli/memoryAccess.js +45 -0
- package/src/cli/memoryAccess.ts +53 -0
- package/src/cli/print.js +119 -5
- package/src/cli/print.ts +597 -0
- package/src/cli/quickModelSetup.js +60 -0
- package/src/cli/quickModelSetup.tsx +96 -0
- package/src/cli/streamJson.js +80 -4
- package/src/cli/streamJson.ts +433 -0
- package/src/cli/wizards/advisorWizard.js +1 -0
- package/src/cli/wizards/advisorWizard.tsx +195 -0
- package/src/cli/wizards/agentRuntimeWizard.js +84 -0
- package/src/cli/wizards/agentRuntimeWizard.tsx +134 -0
- package/src/cli/wizards/browserWizard.js +55 -0
- package/src/cli/wizards/browserWizard.tsx +77 -0
- package/src/cli/wizards/contextAdvisorWizard.js +84 -0
- package/src/cli/wizards/contextAdvisorWizard.tsx +124 -0
- package/src/cli/wizards/fieldForm.js +7 -4
- package/src/cli/wizards/fieldForm.tsx +250 -0
- package/src/cli/wizards/imageWizard.js +4 -5
- package/src/cli/wizards/imageWizard.tsx +96 -0
- package/src/cli/wizards/openRouterWizard.tsx +48 -0
- package/src/cli/wizards/providerPresets.js +1 -8
- package/src/cli/wizards/providerPresets.ts +106 -0
- package/src/cli/wizards/providerWizard.js +152 -205
- package/src/cli/wizards/providerWizard.tsx +266 -0
- package/src/cli/wizards/searchWizard.tsx +47 -0
- package/src/cli/wizards/ttsWizard.js +4 -3
- package/src/cli/wizards/ttsWizard.tsx +61 -0
- package/src/config/configDefaults.js +25 -0
- package/src/config/configDefaults.ts +33 -0
- package/src/config/configDiagnostics.js +117 -0
- package/src/config/configDiagnostics.ts +166 -0
- package/src/config/configFile.js +52 -32
- package/src/config/configFile.ts +633 -0
- package/src/config/configManagement.js +126 -0
- package/src/config/configManagement.ts +159 -0
- package/src/config/configService.js +66 -11
- package/src/config/configService.ts +554 -0
- package/src/config/numericLimits.js +61 -0
- package/src/config/numericLimits.ts +80 -0
- package/src/config.ts +109 -0
- package/src/context/agentHome.ts +11 -0
- package/src/context/archive.ts +144 -0
- package/src/context/budgetPlan.js +54 -0
- package/src/context/budgetPlan.ts +88 -0
- package/src/context/compact.js +179 -106
- package/src/context/compact.ts +584 -0
- package/src/context/contextBundle.js +119 -0
- package/src/context/contextBundle.ts +176 -0
- package/src/context/contextLock.ts +84 -0
- package/src/context/microCompactLite.ts +218 -0
- package/src/context/persistContext.ts +177 -0
- package/src/context/reactiveCompact.js +19 -4
- package/src/context/reactiveCompact.ts +240 -0
- package/src/context/recentDirs.ts +42 -0
- package/src/context/sessionPath.ts +206 -0
- package/src/context/sessionRegistry.js +8 -2
- package/src/context/sessionRegistry.ts +414 -0
- package/src/context/snipCompact.ts +163 -0
- package/src/context/tokenCounter.ts +106 -0
- package/src/context/workspaceIndex.js +2 -2
- package/src/context/workspaceIndex.ts +751 -0
- package/src/entry.compiled.tsx +20 -0
- package/src/harness/claimValidation.js +9 -0
- package/src/harness/claimValidation.ts +22 -0
- package/src/harness/evidenceLedger.js +92 -0
- package/src/harness/evidenceLedger.ts +96 -0
- package/src/harness/stallGuard.js +60 -0
- package/src/harness/stallGuard.ts +81 -0
- package/src/humanIntervention.js +129 -0
- package/src/humanIntervention.ts +216 -0
- package/src/infra/verification.ts +295 -0
- package/src/irreversible/rules.js +216 -0
- package/src/irreversible/rules.ts +261 -0
- package/src/llm/client.js +40 -3
- package/src/llm/client.ts +621 -0
- package/src/llm/promptCache.js +27 -0
- package/src/llm/promptCache.ts +41 -0
- package/src/llm/providers/anthropicFormat.js +41 -5
- package/src/llm/providers/anthropicFormat.ts +290 -0
- package/src/llm/providers/openaiFormat.js +186 -8
- package/src/llm/providers/openaiFormat.ts +428 -0
- package/src/loop.js +614 -63
- package/src/loop.ts +1454 -0
- package/src/main.js +67 -10
- package/src/main.tsx +344 -0
- package/src/memory/codec.js +204 -0
- package/src/memory/codec.ts +249 -0
- package/src/memory/constants.js +34 -0
- package/src/memory/constants.ts +50 -0
- package/src/memory/episodicMemory.js +684 -0
- package/src/memory/episodicMemory.ts +815 -0
- package/src/memory/fileStore.js +148 -0
- package/src/memory/fileStore.ts +184 -0
- package/src/memory/memoryService.js +570 -0
- package/src/memory/memoryService.ts +859 -0
- package/src/memory/migration.js +128 -0
- package/src/memory/migration.ts +160 -0
- package/src/memory/repository.js +789 -0
- package/src/memory/repository.ts +893 -0
- package/src/memory/retrieval.js +108 -0
- package/src/memory/retrieval.ts +145 -0
- package/src/memory/searchIndex.js +107 -0
- package/src/memory/searchIndex.ts +153 -0
- package/src/memory/topicView.js +82 -0
- package/src/memory/topicView.ts +95 -0
- package/src/memory/turnJournal.js +95 -0
- package/src/memory/turnJournal.ts +120 -0
- package/src/memory/types.js +2 -0
- package/src/memory/types.ts +186 -0
- package/src/messageQueue.ts +123 -0
- package/src/multimodal.js +128 -46
- package/src/multimodal.ts +349 -0
- package/src/plugin-sdk.js +21 -1
- package/src/plugin-sdk.ts +134 -0
- package/src/plugins/builtinPluginIds.js +47 -0
- package/src/plugins/builtinPluginIds.ts +51 -0
- package/src/plugins/builtinRegistry.ts +43 -0
- package/src/plugins/commandRouter.js +40 -26
- package/src/plugins/commandRouter.ts +261 -0
- package/src/plugins/hookEngine.ts +341 -0
- package/src/plugins/pluginApi.ts +133 -0
- package/src/plugins/pluginLifecycle.ts +34 -0
- package/src/plugins/pluginLoader.ts +147 -0
- package/src/plugins/pluginManifest.js +127 -0
- package/src/plugins/pluginManifest.ts +152 -0
- package/src/plugins/pluginRunner.js +47 -2
- package/src/plugins/pluginRunner.ts +207 -0
- package/src/plugins/transcript.ts +208 -0
- package/src/prompts/lint.js +169 -0
- package/src/prompts/lint.ts +220 -0
- package/src/prompts/pluginList.js +32 -16
- package/src/prompts/pluginList.ts +86 -0
- package/src/prompts/policyRegistry.js +318 -0
- package/src/prompts/policyRegistry.ts +358 -0
- package/src/prompts/projectInstructions.js +24 -0
- package/src/prompts/projectInstructions.ts +81 -0
- package/src/prompts/promptAssembly.js +311 -202
- package/src/prompts/promptAssembly.ts +510 -0
- package/src/prompts/skillList.js +106 -45
- package/src/prompts/skillList.ts +198 -0
- package/src/prompts/system.js +31 -6
- package/src/prompts/system.ts +81 -0
- package/src/runtime/agentRuntimeContext.js +53 -0
- package/src/runtime/agentRuntimeContext.ts +112 -0
- package/src/session/runAgentTurn.js +71 -12
- package/src/session/runAgentTurn.ts +181 -0
- package/src/session/runTurn.js +29 -0
- package/src/session/runTurn.ts +160 -0
- package/src/session/runtimeStore.js +149 -0
- package/src/session/runtimeStore.ts +197 -0
- package/src/session/sessionState.js +79 -3
- package/src/session/sessionState.ts +134 -0
- package/src/toolGate.ts +50 -0
- package/src/tools/advisor/advisor.js +76 -15
- package/src/tools/advisor/advisor.ts +512 -0
- package/src/tools/agent/agent.js +245 -29
- package/src/tools/agent/agent.ts +837 -0
- package/src/tools/agent/agentTaskService.js +60 -0
- package/src/tools/agent/agentTaskService.ts +113 -0
- package/src/tools/agent/agentTypes.js +20 -8
- package/src/tools/agent/agentTypes.ts +94 -0
- package/src/tools/agent/coordinator.ts +111 -0
- package/src/tools/agent/worktree.ts +296 -0
- package/src/tools/ask/ask.js +17 -0
- package/src/tools/ask/ask.ts +122 -0
- package/src/tools/bash/attachImage.js +223 -0
- package/src/tools/bash/attachImage.ts +281 -0
- package/src/tools/bash/bash.js +132 -14
- package/src/tools/bash/bash.ts +642 -0
- package/src/tools/bash/semantics.ts +103 -0
- package/src/tools/bash/warnings.js +17 -11
- package/src/tools/bash/warnings.ts +117 -0
- package/src/tools/capabilities.js +42 -0
- package/src/tools/capabilities.ts +69 -0
- package/src/tools/computeruse/accessibilityPermission.js +261 -0
- package/src/tools/computeruse/accessibilityPermission.ts +294 -0
- package/src/tools/computeruse/axSearch.js +96 -0
- package/src/tools/computeruse/axSearch.ts +120 -0
- package/src/tools/computeruse/axSnap.js +272 -0
- package/src/tools/computeruse/axSnap.ts +349 -0
- package/src/tools/computeruse/capabilityLabels.js +95 -0
- package/src/tools/computeruse/capabilityLabels.ts +115 -0
- package/src/tools/computeruse/computerUseService.js +2235 -0
- package/src/tools/computeruse/computerUseService.ts +2686 -0
- package/src/tools/computeruse/computeruse.js +2742 -0
- package/src/tools/computeruse/computeruse.ts +3265 -0
- package/src/tools/computeruse/diagnosticTrace.js +154 -0
- package/src/tools/computeruse/diagnosticTrace.ts +195 -0
- package/src/tools/computeruse/diagnostics.js +369 -0
- package/src/tools/computeruse/diagnostics.ts +453 -0
- package/src/tools/computeruse/grants.js +53 -0
- package/src/tools/computeruse/grants.ts +70 -0
- package/src/tools/computeruse/humanBoundary.js +159 -0
- package/src/tools/computeruse/humanBoundary.ts +199 -0
- package/src/tools/computeruse/intervention.js +77 -0
- package/src/tools/computeruse/intervention.ts +110 -0
- package/src/tools/computeruse/manualSchema.js +709 -0
- package/src/tools/computeruse/manualSchema.ts +916 -0
- package/src/tools/computeruse/observationImages.js +705 -0
- package/src/tools/computeruse/observationImages.ts +889 -0
- package/src/tools/computeruse/protocol.js +60 -0
- package/src/tools/computeruse/protocol.ts +515 -0
- package/src/tools/computeruse/telemetryLog.js +57 -0
- package/src/tools/computeruse/telemetryLog.ts +67 -0
- package/src/tools/computeruse/vision/appGuide.js +325 -0
- package/src/tools/computeruse/vision/appGuide.ts +482 -0
- package/src/tools/computeruse/vision/focus.js +291 -0
- package/src/tools/computeruse/vision/focus.ts +382 -0
- package/src/tools/computeruse/vision/frames.js +248 -0
- package/src/tools/computeruse/vision/frames.ts +329 -0
- package/src/tools/computeruse/vision/geometry.js +131 -0
- package/src/tools/computeruse/vision/geometry.ts +163 -0
- package/src/tools/computeruse/vision/imaging.js +201 -0
- package/src/tools/computeruse/vision/imaging.ts +281 -0
- package/src/tools/computeruse/vision/png.js +274 -0
- package/src/tools/computeruse/vision/png.ts +317 -0
- package/src/tools/computeruse/vision/raster.js +320 -0
- package/src/tools/computeruse/vision/raster.ts +408 -0
- package/src/tools/computeruse/vision/settle.js +131 -0
- package/src/tools/computeruse/vision/settle.ts +220 -0
- package/src/tools/computeruse/vision/telemetry.js +196 -0
- package/src/tools/computeruse/vision/telemetry.ts +241 -0
- package/src/tools/computeruse/visionOcr.js +115 -0
- package/src/tools/computeruse/visionOcr.ts +128 -0
- package/src/tools/computeruse/waitFor.js +237 -0
- package/src/tools/computeruse/waitFor.ts +291 -0
- package/src/tools/download/download.js +10 -6
- package/src/tools/download/download.ts +187 -0
- package/src/tools/edit/edit.js +30 -18
- package/src/tools/edit/edit.ts +349 -0
- package/src/tools/edit/multi-edit.js +25 -14
- package/src/tools/edit/multi-edit.ts +223 -0
- package/src/tools/effectiveToolSet.js +73 -0
- package/src/tools/effectiveToolSet.ts +91 -0
- package/src/tools/genimage/genimage.ts +257 -0
- package/src/tools/genspeech/genspeech.ts +83 -0
- package/src/tools/glob/glob.js +176 -29
- package/src/tools/glob/glob.ts +283 -0
- package/src/tools/grep/grep.js +61 -19
- package/src/tools/grep/grep.ts +239 -0
- package/src/tools/grep/rgPath.ts +206 -0
- package/src/tools/index.js +99 -5
- package/src/tools/index.ts +293 -0
- package/src/tools/lsp/config.js +113 -0
- package/src/tools/lsp/config.ts +113 -0
- package/src/tools/lsp/lsp.js +256 -0
- package/src/tools/lsp/lsp.ts +274 -0
- package/src/tools/lsp/service.js +303 -0
- package/src/tools/lsp/service.ts +296 -0
- package/src/tools/memory/memory.js +520 -0
- package/src/tools/memory/memory.ts +625 -0
- package/src/tools/memory/policy.js +53 -0
- package/src/tools/memory/policy.ts +72 -0
- package/src/tools/notebook/notebookEdit.js +114 -0
- package/src/tools/notebook/notebookEdit.ts +126 -0
- package/src/tools/powershell/powershell.js +210 -0
- package/src/tools/powershell/powershell.ts +239 -0
- package/src/tools/presentresult/presentresult.js +145 -0
- package/src/tools/presentresult/presentresult.ts +156 -0
- package/src/tools/read/read.js +371 -163
- package/src/tools/read/read.ts +558 -0
- package/src/tools/shared/diffSummary.js +44 -0
- package/src/tools/shared/diffSummary.ts +63 -0
- package/src/tools/shared/fileActivity.js +51 -0
- package/src/tools/shared/fileActivity.ts +66 -0
- package/src/tools/shared/fileMutationBus.js +13 -0
- package/src/tools/shared/fileMutationBus.ts +14 -0
- package/src/tools/shared/fileState.js +73 -65
- package/src/tools/shared/fileState.ts +145 -0
- package/src/tools/shared/fileUtils.js +81 -4
- package/src/tools/shared/fileUtils.ts +504 -0
- package/src/tools/shared/killTree.ts +50 -0
- package/src/tools/shared/permanentDeleteGuard.js +26 -0
- package/src/tools/shared/permanentDeleteGuard.ts +34 -0
- package/src/tools/shared/schemas.ts +20 -0
- package/src/tools/shared/toolOutputStore.js +97 -0
- package/src/tools/shared/toolOutputStore.ts +110 -0
- package/src/tools/skill/skill.js +86 -0
- package/src/tools/skill/skill.ts +94 -0
- package/src/tools/snip/snip.js +67 -0
- package/src/tools/snip/snip.ts +80 -0
- package/src/tools/tasks/backgroundTaskService.js +275 -0
- package/src/tools/tasks/backgroundTaskService.ts +369 -0
- package/src/tools/tasks/processEvidence.js +24 -0
- package/src/tools/tasks/processEvidence.ts +34 -0
- package/src/tools/tasks/taskManagement.js +163 -0
- package/src/tools/tasks/taskManagement.ts +180 -0
- package/src/tools/tasks/taskOutput.js +113 -0
- package/src/tools/tasks/taskOutput.ts +145 -0
- package/src/tools/tasks/taskStop.js +74 -0
- package/src/tools/tasks/taskStop.ts +99 -0
- package/src/tools/tasks/taskStore.js +46 -0
- package/src/tools/tasks/taskStore.ts +77 -0
- package/src/tools/terminal/terminalCapture.js +167 -0
- package/src/tools/terminal/terminalCapture.ts +179 -0
- package/src/tools/trash/trash.js +233 -0
- package/src/tools/trash/trash.ts +283 -0
- package/src/tools/types.js +1 -1
- package/src/tools/types.ts +24 -0
- package/src/tools/webbrowser/agentBrowserPath.ts +286 -0
- package/src/tools/webbrowser/browser.js +214 -11
- package/src/tools/webbrowser/browser.ts +293 -0
- package/src/tools/webbrowser/browserControl.js +153 -48
- package/src/tools/webbrowser/browserControl.ts +262 -0
- package/src/tools/webbrowser/browserDaemon.js +147 -39
- package/src/tools/webbrowser/browserDaemon.ts +270 -0
- package/src/tools/webbrowser/browserOutcome.js +273 -0
- package/src/tools/webbrowser/browserOutcome.ts +306 -0
- package/src/tools/webbrowser/browserRunRegistry.js +150 -0
- package/src/tools/webbrowser/browserRunRegistry.ts +179 -0
- package/src/tools/webbrowser/browserService.js +20 -6
- package/src/tools/webbrowser/browserService.ts +56 -0
- package/src/tools/webbrowser/browserStream.js +30 -1
- package/src/tools/webbrowser/browserStream.ts +216 -0
- package/src/tools/webbrowser/browserTabs.js +143 -0
- package/src/tools/webbrowser/browserTabs.ts +210 -0
- package/src/tools/webbrowser/webbrowser.js +831 -150
- package/src/tools/webbrowser/webbrowser.ts +1537 -0
- package/src/tools/webfetch/preapproved.ts +274 -0
- package/src/tools/webfetch/webfetch.js +24 -23
- package/src/tools/webfetch/webfetch.ts +383 -0
- package/src/tools/websearch/websearch.js +12 -11
- package/src/tools/websearch/websearch.ts +292 -0
- package/src/tools/worktree/sessionWorktree.js +128 -0
- package/src/tools/worktree/sessionWorktree.ts +158 -0
- package/src/tools/write/write.js +27 -44
- package/src/tools/write/write.ts +136 -0
- package/src/types.ts +960 -0
- package/src/ui/App.js +216 -12
- package/src/ui/App.tsx +879 -0
- package/src/ui/AskQuestion.tsx +300 -0
- package/src/ui/InputBox.js +77 -1
- package/src/ui/InputBox.tsx +567 -0
- package/src/ui/LiveArea.tsx +344 -0
- package/src/ui/MessageList.js +27 -6
- package/src/ui/MessageList.tsx +559 -0
- package/src/ui/ModelPicker.tsx +107 -0
- package/src/ui/QueuedMessagesBanner.tsx +51 -0
- package/src/ui/Root.js +6 -8
- package/src/ui/Root.tsx +100 -0
- package/src/ui/SessionPicker.tsx +169 -0
- package/src/ui/StatusLine.js +22 -1
- package/src/ui/StatusLine.tsx +145 -0
- package/src/ui/WorkspacePicker.tsx +84 -0
- package/src/ui/advisorCommand.js +54 -0
- package/src/ui/advisorCommand.ts +63 -0
- package/src/ui/computerCommand.js +19 -0
- package/src/ui/computerCommand.ts +35 -0
- package/src/ui/figures.ts +45 -0
- package/src/ui/hooks/askSlot.ts +112 -0
- package/src/ui/hooks/useChat.js +271 -7
- package/src/ui/hooks/useChat.ts +1578 -0
- package/src/ui/hooks/useInputHistory.ts +235 -0
- package/src/ui/hooks/usePasteHandler.ts +220 -0
- package/src/ui/hooks/useTerminalSize.ts +41 -0
- package/src/ui/hooks/useTextBuffer.ts +108 -0
- package/src/ui/hooks/useTokenUsage.ts +44 -0
- package/src/ui/liveViewport.ts +189 -0
- package/src/ui/markdown.ts +230 -0
- package/src/ui/textBuffer.ts +239 -0
- package/src/ui/theme.ts +35 -0
- package/src/utils/clipboardImage.js +358 -0
- package/src/utils/clipboardImage.ts +420 -0
- package/src/utils/debugLog.ts +50 -0
- package/src/utils/greenRoot.ts +35 -0
- package/src/utils/packageRoot.ts +40 -0
- package/src/utils/pathContains.js +28 -0
- package/src/utils/pathContains.ts +31 -0
- package/src/utils/resourcePaths.ts +71 -0
- package/src/utils/stripThink.ts +49 -0
- package/src/utils/zodToJson.ts +73 -0
- package/src/web/browserBridge.js +365 -66
- package/src/web/browserBridge.ts +672 -0
- package/src/web/protocol.ts +1804 -0
- package/vendor/computer-use/arm64-darwin/cu-helperd +0 -0
- package/workflows/ppt.yaml +13 -2
- package/workflows/wechat-reading-shots.yaml +315 -0
- package/workflows/word.yaml +11 -1
- package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
- package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
- package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
- /package/skills/ai-image/scripts/{generate_test.mjs → generate.selftest.mjs} +0 -0
- /package/skills/ai-image/scripts/{generate_from_plan_test.mjs → generate_from_plan.selftest.mjs} +0 -0
package/src/multimodal.js
CHANGED
|
@@ -11,6 +11,7 @@ import { readFile, stat } from 'node:fs/promises';
|
|
|
11
11
|
import { extname, resolve } from 'node:path';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
13
|
import { getWorkingDir } from './bootstrap/workingDir.js';
|
|
14
|
+
import { removeManagedClipboardTemp } from './utils/clipboardImage.js';
|
|
14
15
|
export const MAX_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
15
16
|
const IMAGE_MIME_BY_EXT = {
|
|
16
17
|
'.jpg': 'image/jpeg',
|
|
@@ -18,8 +19,101 @@ const IMAGE_MIME_BY_EXT = {
|
|
|
18
19
|
'.png': 'image/png',
|
|
19
20
|
'.gif': 'image/gif',
|
|
20
21
|
'.webp': 'image/webp',
|
|
22
|
+
// Read can attach BMP files directly even though inline drag/drop parsing
|
|
23
|
+
// intentionally remains limited to formats accepted by most providers.
|
|
24
|
+
'.bmp': 'image/bmp',
|
|
21
25
|
};
|
|
22
26
|
const IMAGE_EXT_PATTERN = String.raw `(?:png|jpe?g|gif|webp)`;
|
|
27
|
+
/**
|
|
28
|
+
* 「路径 token」允许的字符:非空白,或 `\x` 这样的转义对——macOS Terminal / iTerm2
|
|
29
|
+
* 拖入文件时空格默认转义成 `\ `,不认它就会把 `/Users/me/My\ Pic.png` 切成两半。
|
|
30
|
+
* 反斜杠**只能**作为转义对的首字符出现(不写成 `(?:\\.|\S)`),这样两个分支互斥、
|
|
31
|
+
* 不会因重叠产生指数级回溯。
|
|
32
|
+
*/
|
|
33
|
+
const PATH_RUN = String.raw `(?:\\.|[^\s\\])`;
|
|
34
|
+
/**
|
|
35
|
+
* 一次扫描认出所有「行内图片来源」的写法。分支顺序即优先级(同一起点按 alternation 左优先):
|
|
36
|
+
*
|
|
37
|
+
* ① `@"..."` / `@'...'` / `@bare` —— 显式语法,用户主动指定
|
|
38
|
+
* ② `"..."` / `'...'` —— 裸引号包裹:Windows Terminal 拖入含空格路径时自带引号
|
|
39
|
+
* ③ `https://…` / `file://…` —— 裸 URL;GNOME Terminal / VTE 拖入给的正是 file:// URI
|
|
40
|
+
* ④ `C:\…` / `C:/…` / `\\server\…` —— 裸 Windows 盘符与 UNC:Windows Terminal 拖入形态
|
|
41
|
+
* ⑤ `./` `../` `/` `~/` 开头的路径 —— 裸 POSIX 路径(允许 `\ ` 转义空格)
|
|
42
|
+
*
|
|
43
|
+
* 为什么这几种写法必须全认:**终端把「拖进窗口的文件」翻译成一次「粘贴文件路径文本」**,
|
|
44
|
+
* stdin 上没有任何图片字节。所以"支持拖拽"= 认得出各终端各自的路径写法,然后自己去读盘。
|
|
45
|
+
* 各终端形态不统一(mac 转义空格 / Windows 加引号 / GNOME 给 URI),少认一种就是那个平台不能用。
|
|
46
|
+
*
|
|
47
|
+
* 一次拖多张图(空格或换行分隔)不需要额外切分:全局扫描天然逐个命中,
|
|
48
|
+
* 分隔符会被下一次匹配的 `(^|\s)` 前缀吃掉并原样留在文本里。
|
|
49
|
+
*/
|
|
50
|
+
const INLINE_SOURCE_RE = new RegExp(String.raw `(^|\s)(?:` +
|
|
51
|
+
[
|
|
52
|
+
String.raw `@"(?<atDouble>[^"\n]+)"`,
|
|
53
|
+
String.raw `@'(?<atSingle>[^'\n]+)'`,
|
|
54
|
+
String.raw `@(?<atBare>${PATH_RUN}+)`,
|
|
55
|
+
String.raw `"(?<quotedDouble>[^"\n]+\.${IMAGE_EXT_PATTERN})"`,
|
|
56
|
+
String.raw `'(?<quotedSingle>[^'\n]+\.${IMAGE_EXT_PATTERN})'`,
|
|
57
|
+
String.raw `(?<url>(?:https?|file):\/\/${PATH_RUN}+?\.${IMAGE_EXT_PATTERN}(?:[?#]\S*)?)`,
|
|
58
|
+
String.raw `(?<winPath>[A-Za-z]:[\\\/]${PATH_RUN}+?\.${IMAGE_EXT_PATTERN})(?!\w)`,
|
|
59
|
+
String.raw `(?<uncPath>\\\\${PATH_RUN}+?\.${IMAGE_EXT_PATTERN})(?!\w)`,
|
|
60
|
+
String.raw `(?<posixPath>(?:\.{1,2}\/|\/|~\/)${PATH_RUN}+?\.${IMAGE_EXT_PATTERN})(?!\w)`,
|
|
61
|
+
].join('|') +
|
|
62
|
+
String.raw `)`, 'gi');
|
|
63
|
+
/**
|
|
64
|
+
* 去掉 shell 转义反斜杠(`My\ Pic.png` → `My Pic.png`)。
|
|
65
|
+
*
|
|
66
|
+
* ⚠️ 判据只看**字符串长相**、绝不看 `process.platform`:同一个函数在 `-p` / stream-json
|
|
67
|
+
* 下会在 Linux 宿主上收到 Windows 路径,按平台判会把 `C:\Users\bills` 吃成 `C:Usersbills`。
|
|
68
|
+
* Windows 盘符 / UNC 开头的串里反斜杠是路径分隔符,原样返回。
|
|
69
|
+
*/
|
|
70
|
+
function unescapePathToken(value) {
|
|
71
|
+
if (/^[A-Za-z]:[\\/]/.test(value) || value.startsWith('\\\\'))
|
|
72
|
+
return value;
|
|
73
|
+
return value.replace(/\\(.)/g, '$1');
|
|
74
|
+
}
|
|
75
|
+
function scanInlineImages(input, defaults) {
|
|
76
|
+
const segments = [];
|
|
77
|
+
const images = [];
|
|
78
|
+
const raws = [];
|
|
79
|
+
let last = 0;
|
|
80
|
+
for (const m of input.matchAll(INLINE_SOURCE_RE)) {
|
|
81
|
+
const g = m.groups ?? {};
|
|
82
|
+
// 引号包裹的内容是字面量,不做反转义;裸 token 才可能带 shell 转义。
|
|
83
|
+
const quoted = g.atDouble ?? g.atSingle ?? g.quotedDouble ?? g.quotedSingle;
|
|
84
|
+
const bare = g.atBare ?? g.url ?? g.winPath ?? g.uncPath ?? g.posixPath;
|
|
85
|
+
const raw = quoted ?? bare;
|
|
86
|
+
if (raw === undefined)
|
|
87
|
+
continue;
|
|
88
|
+
const source = quoted !== undefined
|
|
89
|
+
? stripWrappingQuotes(quoted.trim())
|
|
90
|
+
: unescapePathToken(stripWrappingQuotes(bare.trim()));
|
|
91
|
+
// 认不出是图片源(如 @某个词、.bmp)就当普通文字原样留着,不吞。
|
|
92
|
+
if (!isSupportedImageSource(source))
|
|
93
|
+
continue;
|
|
94
|
+
const start = (m.index ?? 0) + (m[1] ?? '').length;
|
|
95
|
+
const end = (m.index ?? 0) + m[0].length;
|
|
96
|
+
segments.push(input.slice(last, start), { image: images.length });
|
|
97
|
+
raws.push(input.slice(start, end));
|
|
98
|
+
images.push({
|
|
99
|
+
source,
|
|
100
|
+
detail: defaults.detail,
|
|
101
|
+
maxLongSidePixel: defaults.maxLongSidePixel,
|
|
102
|
+
});
|
|
103
|
+
last = end;
|
|
104
|
+
}
|
|
105
|
+
segments.push(input.slice(last));
|
|
106
|
+
return { segments, images, raws };
|
|
107
|
+
}
|
|
108
|
+
/** 把扫描结果拼回文本:被采纳的图占位抹掉,未采纳的原位还原成原始 token。 */
|
|
109
|
+
function renderSegments(scan, kept) {
|
|
110
|
+
return scan.segments
|
|
111
|
+
.map((seg) => typeof seg === 'string' ? seg : kept(seg.image) ? '' : (scan.raws[seg.image] ?? ''))
|
|
112
|
+
.join('');
|
|
113
|
+
}
|
|
114
|
+
function normalizeExtractedText(text) {
|
|
115
|
+
return text.replace(/[ \t]{2,}/g, ' ').replace(/\n{3,}/g, '\n\n').trim();
|
|
116
|
+
}
|
|
23
117
|
export function isSupportedImageSource(source) {
|
|
24
118
|
const clean = stripWrappingQuotes(source.trim());
|
|
25
119
|
if (isHttpUrl(clean)) {
|
|
@@ -75,64 +169,52 @@ export function contentToDisplayText(content) {
|
|
|
75
169
|
return [text, ...badges].filter(Boolean).join('\n');
|
|
76
170
|
}
|
|
77
171
|
export function extractInlineImageInputs(input, defaults = {}) {
|
|
78
|
-
const
|
|
79
|
-
let text = input;
|
|
80
|
-
const consume = (raw) => {
|
|
81
|
-
const source = stripWrappingQuotes(raw.trim());
|
|
82
|
-
if (!source || !isSupportedImageSource(source))
|
|
83
|
-
return raw;
|
|
84
|
-
images.push({
|
|
85
|
-
source,
|
|
86
|
-
detail: defaults.detail,
|
|
87
|
-
maxLongSidePixel: defaults.maxLongSidePixel,
|
|
88
|
-
});
|
|
89
|
-
return '';
|
|
90
|
-
};
|
|
91
|
-
// @"/path with spaces/a.png" / @'path' / @./a.png / @https://x/a.png
|
|
92
|
-
text = text.replace(/(^|\s)@(?:"([^"]+)"|'([^']+)'|(\S+))/g, (match, prefix, dq, sq, bare) => {
|
|
93
|
-
const before = prefix ?? '';
|
|
94
|
-
const source = dq ?? sq ?? bare ?? '';
|
|
95
|
-
const out = consume(source);
|
|
96
|
-
return out === '' ? before : match;
|
|
97
|
-
});
|
|
98
|
-
// 裸 URL 图片。
|
|
99
|
-
text = text.replace(new RegExp(String.raw `(^|\s)(https?:\/\/\S+?\.${IMAGE_EXT_PATTERN}(?:[?#]\S*)?)`, 'gi'), (match, prefix, source) => {
|
|
100
|
-
const out = consume(source);
|
|
101
|
-
return out === '' ? prefix : match;
|
|
102
|
-
});
|
|
103
|
-
// 裸本地图片路径只支持无空格写法;含空格请用 @"/path with spaces/a.png"。
|
|
104
|
-
text = text.replace(new RegExp(String.raw `(^|\s)((?:\.{1,2}\/|\/|~\/)\S+?\.${IMAGE_EXT_PATTERN})`, 'gi'), (match, prefix, source) => {
|
|
105
|
-
const out = consume(source);
|
|
106
|
-
return out === '' ? prefix : match;
|
|
107
|
-
});
|
|
172
|
+
const scan = scanInlineImages(input, defaults);
|
|
108
173
|
return {
|
|
109
|
-
text:
|
|
110
|
-
images,
|
|
174
|
+
text: normalizeExtractedText(renderSegments(scan, () => true)),
|
|
175
|
+
images: scan.images,
|
|
111
176
|
};
|
|
112
177
|
}
|
|
113
178
|
export async function buildUserContent(args) {
|
|
114
179
|
let text = args.text;
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
const parsed = extractInlineImageInputs(text, {
|
|
180
|
+
const scan = args.parseInlineImages
|
|
181
|
+
? scanInlineImages(args.text, {
|
|
118
182
|
detail: args.detail,
|
|
119
183
|
maxLongSidePixel: args.maxLongSidePixel,
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
|
|
184
|
+
})
|
|
185
|
+
: null;
|
|
186
|
+
// 显式传入的图(`-p --image` / 浏览器实况截图 visionImages)读失败**照旧抛错**:
|
|
187
|
+
// 调用方明确要求了这张图,静默降级等于假装做了。
|
|
188
|
+
const explicitParts = [];
|
|
189
|
+
for (const image of args.images ?? []) {
|
|
190
|
+
explicitParts.push(await imageToContentPart(image));
|
|
123
191
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
192
|
+
const inlineParts = [];
|
|
193
|
+
if (scan) {
|
|
194
|
+
const resolved = [];
|
|
195
|
+
for (const image of scan.images) {
|
|
196
|
+
try {
|
|
197
|
+
resolved.push(await imageToContentPart(image));
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
// 行内图片是**我们从文本里猜**出来的:路径不存在 / 过大 / 格式不认时绝不能打死整个回合
|
|
201
|
+
// (loop.ts 对 buildUserContent 抛错的处理是 yield error + return,整轮当场死)。
|
|
202
|
+
// 把原始 token 原位还回文本当普通文字,模型照样看得见这个路径、可以自己 Read 或去生成它。
|
|
203
|
+
resolved.push(null);
|
|
204
|
+
args.onInlineImageSkipped?.(image.source, e.message);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
text = normalizeExtractedText(renderSegments(scan, (i) => resolved[i] !== null));
|
|
208
|
+
inlineParts.push(...resolved.filter((p) => p !== null));
|
|
131
209
|
}
|
|
132
|
-
|
|
210
|
+
const parts = [...explicitParts, ...inlineParts];
|
|
211
|
+
if (parts.length === 0)
|
|
212
|
+
return text;
|
|
213
|
+
return [{ type: 'text', text: text.trim() || '请分析这张图片。' }, ...parts];
|
|
133
214
|
}
|
|
134
215
|
export async function imageToContentPart(input) {
|
|
135
216
|
const url = await imageSourceToUrl(input.source);
|
|
217
|
+
await removeManagedClipboardTemp(input.source);
|
|
136
218
|
const image_url = {
|
|
137
219
|
url,
|
|
138
220
|
detail: input.detail ?? 'default',
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/multimodal.ts —— OpenAI 兼容多模态输入辅助
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* v1 支持图片:本地路径 → data URL,公网 URL → 原样 image_url。
|
|
6
|
+
* 这里不调用模型,只负责把 TUI / -p / stream-json 的输入规整成
|
|
7
|
+
* Chat Completions 的 content blocks。
|
|
8
|
+
* ============================================================
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
12
|
+
import { extname, resolve } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
import { getWorkingDir } from './bootstrap/workingDir.js';
|
|
16
|
+
import type {
|
|
17
|
+
ChatContentPart,
|
|
18
|
+
ImageDetail,
|
|
19
|
+
ImageInputSpec,
|
|
20
|
+
Message,
|
|
21
|
+
} from './types.js';
|
|
22
|
+
import { removeManagedClipboardTemp } from './utils/clipboardImage.js';
|
|
23
|
+
|
|
24
|
+
export const MAX_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
25
|
+
|
|
26
|
+
const IMAGE_MIME_BY_EXT: Record<string, string> = {
|
|
27
|
+
'.jpg': 'image/jpeg',
|
|
28
|
+
'.jpeg': 'image/jpeg',
|
|
29
|
+
'.png': 'image/png',
|
|
30
|
+
'.gif': 'image/gif',
|
|
31
|
+
'.webp': 'image/webp',
|
|
32
|
+
// Read can attach BMP files directly even though inline drag/drop parsing
|
|
33
|
+
// intentionally remains limited to formats accepted by most providers.
|
|
34
|
+
'.bmp': 'image/bmp',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const IMAGE_EXT_PATTERN = String.raw`(?:png|jpe?g|gif|webp)`;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 「路径 token」允许的字符:非空白,或 `\x` 这样的转义对——macOS Terminal / iTerm2
|
|
41
|
+
* 拖入文件时空格默认转义成 `\ `,不认它就会把 `/Users/me/My\ Pic.png` 切成两半。
|
|
42
|
+
* 反斜杠**只能**作为转义对的首字符出现(不写成 `(?:\\.|\S)`),这样两个分支互斥、
|
|
43
|
+
* 不会因重叠产生指数级回溯。
|
|
44
|
+
*/
|
|
45
|
+
const PATH_RUN = String.raw`(?:\\.|[^\s\\])`;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 一次扫描认出所有「行内图片来源」的写法。分支顺序即优先级(同一起点按 alternation 左优先):
|
|
49
|
+
*
|
|
50
|
+
* ① `@"..."` / `@'...'` / `@bare` —— 显式语法,用户主动指定
|
|
51
|
+
* ② `"..."` / `'...'` —— 裸引号包裹:Windows Terminal 拖入含空格路径时自带引号
|
|
52
|
+
* ③ `https://…` / `file://…` —— 裸 URL;GNOME Terminal / VTE 拖入给的正是 file:// URI
|
|
53
|
+
* ④ `C:\…` / `C:/…` / `\\server\…` —— 裸 Windows 盘符与 UNC:Windows Terminal 拖入形态
|
|
54
|
+
* ⑤ `./` `../` `/` `~/` 开头的路径 —— 裸 POSIX 路径(允许 `\ ` 转义空格)
|
|
55
|
+
*
|
|
56
|
+
* 为什么这几种写法必须全认:**终端把「拖进窗口的文件」翻译成一次「粘贴文件路径文本」**,
|
|
57
|
+
* stdin 上没有任何图片字节。所以"支持拖拽"= 认得出各终端各自的路径写法,然后自己去读盘。
|
|
58
|
+
* 各终端形态不统一(mac 转义空格 / Windows 加引号 / GNOME 给 URI),少认一种就是那个平台不能用。
|
|
59
|
+
*
|
|
60
|
+
* 一次拖多张图(空格或换行分隔)不需要额外切分:全局扫描天然逐个命中,
|
|
61
|
+
* 分隔符会被下一次匹配的 `(^|\s)` 前缀吃掉并原样留在文本里。
|
|
62
|
+
*/
|
|
63
|
+
const INLINE_SOURCE_RE = new RegExp(
|
|
64
|
+
String.raw`(^|\s)(?:` +
|
|
65
|
+
[
|
|
66
|
+
String.raw`@"(?<atDouble>[^"\n]+)"`,
|
|
67
|
+
String.raw`@'(?<atSingle>[^'\n]+)'`,
|
|
68
|
+
String.raw`@(?<atBare>${PATH_RUN}+)`,
|
|
69
|
+
String.raw`"(?<quotedDouble>[^"\n]+\.${IMAGE_EXT_PATTERN})"`,
|
|
70
|
+
String.raw`'(?<quotedSingle>[^'\n]+\.${IMAGE_EXT_PATTERN})'`,
|
|
71
|
+
String.raw`(?<url>(?:https?|file):\/\/${PATH_RUN}+?\.${IMAGE_EXT_PATTERN}(?:[?#]\S*)?)`,
|
|
72
|
+
String.raw`(?<winPath>[A-Za-z]:[\\\/]${PATH_RUN}+?\.${IMAGE_EXT_PATTERN})(?!\w)`,
|
|
73
|
+
String.raw`(?<uncPath>\\\\${PATH_RUN}+?\.${IMAGE_EXT_PATTERN})(?!\w)`,
|
|
74
|
+
String.raw`(?<posixPath>(?:\.{1,2}\/|\/|~\/)${PATH_RUN}+?\.${IMAGE_EXT_PATTERN})(?!\w)`,
|
|
75
|
+
].join('|') +
|
|
76
|
+
String.raw`)`,
|
|
77
|
+
'gi',
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 去掉 shell 转义反斜杠(`My\ Pic.png` → `My Pic.png`)。
|
|
82
|
+
*
|
|
83
|
+
* ⚠️ 判据只看**字符串长相**、绝不看 `process.platform`:同一个函数在 `-p` / stream-json
|
|
84
|
+
* 下会在 Linux 宿主上收到 Windows 路径,按平台判会把 `C:\Users\bills` 吃成 `C:Usersbills`。
|
|
85
|
+
* Windows 盘符 / UNC 开头的串里反斜杠是路径分隔符,原样返回。
|
|
86
|
+
*/
|
|
87
|
+
function unescapePathToken(value: string): string {
|
|
88
|
+
if (/^[A-Za-z]:[\\/]/.test(value) || value.startsWith('\\\\')) return value;
|
|
89
|
+
return value.replace(/\\(.)/g, '$1');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** 文本片段与图片占位交替;`{ image: i }` 指向 InlineScan.images 的下标。 */
|
|
93
|
+
type InlineSegment = string | { image: number };
|
|
94
|
+
|
|
95
|
+
interface InlineScan {
|
|
96
|
+
segments: InlineSegment[];
|
|
97
|
+
images: ImageInputSpec[];
|
|
98
|
+
/** 与 images 同下标的原始匹配文本。读图失败时用它**原位**还原,不丢用户写的东西。 */
|
|
99
|
+
raws: string[];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function scanInlineImages(
|
|
103
|
+
input: string,
|
|
104
|
+
defaults: { detail?: ImageDetail; maxLongSidePixel?: number },
|
|
105
|
+
): InlineScan {
|
|
106
|
+
const segments: InlineSegment[] = [];
|
|
107
|
+
const images: ImageInputSpec[] = [];
|
|
108
|
+
const raws: string[] = [];
|
|
109
|
+
let last = 0;
|
|
110
|
+
|
|
111
|
+
for (const m of input.matchAll(INLINE_SOURCE_RE)) {
|
|
112
|
+
const g = m.groups ?? {};
|
|
113
|
+
// 引号包裹的内容是字面量,不做反转义;裸 token 才可能带 shell 转义。
|
|
114
|
+
const quoted = g.atDouble ?? g.atSingle ?? g.quotedDouble ?? g.quotedSingle;
|
|
115
|
+
const bare = g.atBare ?? g.url ?? g.winPath ?? g.uncPath ?? g.posixPath;
|
|
116
|
+
const raw = quoted ?? bare;
|
|
117
|
+
if (raw === undefined) continue;
|
|
118
|
+
const source =
|
|
119
|
+
quoted !== undefined
|
|
120
|
+
? stripWrappingQuotes(quoted.trim())
|
|
121
|
+
: unescapePathToken(stripWrappingQuotes(bare!.trim()));
|
|
122
|
+
// 认不出是图片源(如 @某个词、.bmp)就当普通文字原样留着,不吞。
|
|
123
|
+
if (!isSupportedImageSource(source)) continue;
|
|
124
|
+
|
|
125
|
+
const start = (m.index ?? 0) + (m[1] ?? '').length;
|
|
126
|
+
const end = (m.index ?? 0) + m[0].length;
|
|
127
|
+
segments.push(input.slice(last, start), { image: images.length });
|
|
128
|
+
raws.push(input.slice(start, end));
|
|
129
|
+
images.push({
|
|
130
|
+
source,
|
|
131
|
+
detail: defaults.detail,
|
|
132
|
+
maxLongSidePixel: defaults.maxLongSidePixel,
|
|
133
|
+
});
|
|
134
|
+
last = end;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
segments.push(input.slice(last));
|
|
138
|
+
return { segments, images, raws };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** 把扫描结果拼回文本:被采纳的图占位抹掉,未采纳的原位还原成原始 token。 */
|
|
142
|
+
function renderSegments(scan: InlineScan, kept: (index: number) => boolean): string {
|
|
143
|
+
return scan.segments
|
|
144
|
+
.map((seg) =>
|
|
145
|
+
typeof seg === 'string' ? seg : kept(seg.image) ? '' : (scan.raws[seg.image] ?? ''),
|
|
146
|
+
)
|
|
147
|
+
.join('');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function normalizeExtractedText(text: string): string {
|
|
151
|
+
return text.replace(/[ \t]{2,}/g, ' ').replace(/\n{3,}/g, '\n\n').trim();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function isSupportedImageSource(source: string): boolean {
|
|
155
|
+
const clean = stripWrappingQuotes(source.trim());
|
|
156
|
+
if (isHttpUrl(clean)) {
|
|
157
|
+
try {
|
|
158
|
+
const u = new URL(clean);
|
|
159
|
+
return new RegExp(`\\.(${IMAGE_EXT_PATTERN})(?:$|[?#])`, 'i').test(
|
|
160
|
+
u.pathname,
|
|
161
|
+
);
|
|
162
|
+
} catch {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return Object.hasOwn(IMAGE_MIME_BY_EXT, extname(clean).toLowerCase());
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function normalizeImageDetail(value: string | undefined): ImageDetail {
|
|
170
|
+
if (value === 'low' || value === 'high' || value === 'default') return value;
|
|
171
|
+
return 'default';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function contentToPlainText(content: Message['content']): string {
|
|
175
|
+
if (typeof content === 'string') return content;
|
|
176
|
+
if (content === null) return '';
|
|
177
|
+
return content
|
|
178
|
+
.map((part) => {
|
|
179
|
+
if (part.type === 'text') return part.text;
|
|
180
|
+
if (part.type === 'image_url') return '[图片]';
|
|
181
|
+
if (part.type === 'video_url') return '[视频]';
|
|
182
|
+
return '';
|
|
183
|
+
})
|
|
184
|
+
.filter(Boolean)
|
|
185
|
+
.join(' ');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function contentToDisplayText(content: Message['content']): string {
|
|
189
|
+
if (typeof content === 'string') return content;
|
|
190
|
+
if (content === null) return '';
|
|
191
|
+
const imageCount = content.filter((p) => p.type === 'image_url').length;
|
|
192
|
+
const videoCount = content.filter((p) => p.type === 'video_url').length;
|
|
193
|
+
const text = content
|
|
194
|
+
.filter((p): p is Extract<ChatContentPart, { type: 'text' }> => p.type === 'text')
|
|
195
|
+
.map((p) => p.text)
|
|
196
|
+
.join('\n')
|
|
197
|
+
.trim();
|
|
198
|
+
const badges = [
|
|
199
|
+
imageCount > 0 ? `[已附加 ${imageCount} 张图片]` : '',
|
|
200
|
+
videoCount > 0 ? `[已附加 ${videoCount} 个视频]` : '',
|
|
201
|
+
].filter(Boolean);
|
|
202
|
+
return [text, ...badges].filter(Boolean).join('\n');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function extractInlineImageInputs(
|
|
206
|
+
input: string,
|
|
207
|
+
defaults: { detail?: ImageDetail; maxLongSidePixel?: number } = {},
|
|
208
|
+
): { text: string; images: ImageInputSpec[] } {
|
|
209
|
+
const scan = scanInlineImages(input, defaults);
|
|
210
|
+
return {
|
|
211
|
+
text: normalizeExtractedText(renderSegments(scan, () => true)),
|
|
212
|
+
images: scan.images,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export async function buildUserContent(args: {
|
|
217
|
+
text: string;
|
|
218
|
+
images?: ImageInputSpec[];
|
|
219
|
+
parseInlineImages?: boolean;
|
|
220
|
+
detail?: ImageDetail;
|
|
221
|
+
maxLongSidePixel?: number;
|
|
222
|
+
/**
|
|
223
|
+
* 行内猜出来的图片读取失败时的通知(宿主据此发 warning)。不提供则静默还原成文本——
|
|
224
|
+
* 但内核调用方(loop.ts)应当提供,让降级可见。
|
|
225
|
+
*/
|
|
226
|
+
onInlineImageSkipped?: (source: string, reason: string) => void;
|
|
227
|
+
}): Promise<string | ChatContentPart[]> {
|
|
228
|
+
let text = args.text;
|
|
229
|
+
const scan = args.parseInlineImages
|
|
230
|
+
? scanInlineImages(args.text, {
|
|
231
|
+
detail: args.detail,
|
|
232
|
+
maxLongSidePixel: args.maxLongSidePixel,
|
|
233
|
+
})
|
|
234
|
+
: null;
|
|
235
|
+
|
|
236
|
+
// 显式传入的图(`-p --image` / 浏览器实况截图 visionImages)读失败**照旧抛错**:
|
|
237
|
+
// 调用方明确要求了这张图,静默降级等于假装做了。
|
|
238
|
+
const explicitParts: ChatContentPart[] = [];
|
|
239
|
+
for (const image of args.images ?? []) {
|
|
240
|
+
explicitParts.push(await imageToContentPart(image));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const inlineParts: ChatContentPart[] = [];
|
|
244
|
+
if (scan) {
|
|
245
|
+
const resolved: (ChatContentPart | null)[] = [];
|
|
246
|
+
for (const image of scan.images) {
|
|
247
|
+
try {
|
|
248
|
+
resolved.push(await imageToContentPart(image));
|
|
249
|
+
} catch (e) {
|
|
250
|
+
// 行内图片是**我们从文本里猜**出来的:路径不存在 / 过大 / 格式不认时绝不能打死整个回合
|
|
251
|
+
// (loop.ts 对 buildUserContent 抛错的处理是 yield error + return,整轮当场死)。
|
|
252
|
+
// 把原始 token 原位还回文本当普通文字,模型照样看得见这个路径、可以自己 Read 或去生成它。
|
|
253
|
+
resolved.push(null);
|
|
254
|
+
args.onInlineImageSkipped?.(image.source, (e as Error).message);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
text = normalizeExtractedText(renderSegments(scan, (i) => resolved[i] !== null));
|
|
258
|
+
inlineParts.push(...resolved.filter((p): p is ChatContentPart => p !== null));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const parts = [...explicitParts, ...inlineParts];
|
|
262
|
+
if (parts.length === 0) return text;
|
|
263
|
+
return [{ type: 'text', text: text.trim() || '请分析这张图片。' }, ...parts];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function imageToContentPart(
|
|
267
|
+
input: ImageInputSpec,
|
|
268
|
+
): Promise<Extract<ChatContentPart, { type: 'image_url' }>> {
|
|
269
|
+
const url = await imageSourceToUrl(input.source);
|
|
270
|
+
await removeManagedClipboardTemp(input.source);
|
|
271
|
+
const image_url: Extract<ChatContentPart, { type: 'image_url' }>['image_url'] = {
|
|
272
|
+
url,
|
|
273
|
+
detail: input.detail ?? 'default',
|
|
274
|
+
};
|
|
275
|
+
if (typeof input.maxLongSidePixel === 'number' && input.maxLongSidePixel > 0) {
|
|
276
|
+
image_url.max_long_side_pixel = Math.floor(input.maxLongSidePixel);
|
|
277
|
+
}
|
|
278
|
+
return { type: 'image_url', image_url };
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 按文件头魔数嗅探图片 MIME(扩展名认不出时的回退)。只认 JPEG/PNG/GIF/WEBP——
|
|
283
|
+
* 与 IMAGE_MIME_BY_EXT 支持集一致(OpenAI 兼容接口可吃这四类)。非图返回 null。
|
|
284
|
+
* 动机:下载来的图常带非标准/无扩展名(如爬虫/配图工作流把候选存成 cand_K.img),
|
|
285
|
+
* 仅按扩展名查会误判「不支持」而拒图——魔数嗅探让任意扩展名的真图都能用。
|
|
286
|
+
*/
|
|
287
|
+
export function sniffImageMime(buf: Buffer): string | null {
|
|
288
|
+
if (buf.length >= 4 && buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47)
|
|
289
|
+
return 'image/png';
|
|
290
|
+
if (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) return 'image/jpeg';
|
|
291
|
+
if (buf.length >= 4 && buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x38)
|
|
292
|
+
return 'image/gif';
|
|
293
|
+
if (
|
|
294
|
+
buf.length >= 12 &&
|
|
295
|
+
buf.toString('ascii', 0, 4) === 'RIFF' &&
|
|
296
|
+
buf.toString('ascii', 8, 12) === 'WEBP'
|
|
297
|
+
)
|
|
298
|
+
return 'image/webp';
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async function imageSourceToUrl(source: string): Promise<string> {
|
|
303
|
+
const clean = stripWrappingQuotes(source.trim());
|
|
304
|
+
if (isHttpUrl(clean) || clean.startsWith('data:image/')) return clean;
|
|
305
|
+
|
|
306
|
+
const filePath = resolveImagePath(clean);
|
|
307
|
+
const st = await stat(filePath).catch((e) => {
|
|
308
|
+
throw new Error(`无法读取图片:${source}(${(e as Error).message})`);
|
|
309
|
+
});
|
|
310
|
+
if (!st.isFile()) throw new Error(`图片路径不是文件:${source}`);
|
|
311
|
+
if (st.size > MAX_IMAGE_BYTES) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`图片过大:${source} ${(st.size / 1024 / 1024).toFixed(1)}MB,` +
|
|
314
|
+
`OpenAI 兼容接口单图上限按 10MB 处理。`,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const data = await readFile(filePath);
|
|
319
|
+
// 先按扩展名查 MIME;认不出(无扩展名 / .img / 扩展名错)再按文件头魔数嗅探,都不认才拒。
|
|
320
|
+
const mime = IMAGE_MIME_BY_EXT[extname(filePath).toLowerCase()] ?? sniffImageMime(data);
|
|
321
|
+
if (!mime) {
|
|
322
|
+
throw new Error(
|
|
323
|
+
`不支持的图片格式:${source}。仅支持 JPEG/PNG/GIF/WEBP(按扩展名与文件头都未识别)。`,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
return `data:${mime};base64,${data.toString('base64')}`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function resolveImagePath(source: string): string {
|
|
330
|
+
if (source.startsWith('file://')) return fileURLToPath(source);
|
|
331
|
+
if (source.startsWith('~/')) {
|
|
332
|
+
return resolve(process.env.HOME ?? '', source.slice(2));
|
|
333
|
+
}
|
|
334
|
+
return resolve(getWorkingDir(), source);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function isHttpUrl(source: string): boolean {
|
|
338
|
+
return /^https?:\/\//i.test(source);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function stripWrappingQuotes(value: string): string {
|
|
342
|
+
if (
|
|
343
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
344
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
345
|
+
) {
|
|
346
|
+
return value.slice(1, -1);
|
|
347
|
+
}
|
|
348
|
+
return value;
|
|
349
|
+
}
|
package/src/plugin-sdk.js
CHANGED
|
@@ -18,7 +18,24 @@
|
|
|
18
18
|
export { runQuery } from './loop.js';
|
|
19
19
|
export { runAgentTurn } from './session/runAgentTurn.js';
|
|
20
20
|
export { chat } from './llm/client.js';
|
|
21
|
+
export { derivePromptCacheContext, } from './llm/promptCache.js';
|
|
21
22
|
export { ALL_TOOLS, executeTool, getToolByName } from './tools/index.js';
|
|
23
|
+
export { shutdownAllLsp } from './tools/lsp/service.js';
|
|
24
|
+
// --- Computer Use ---
|
|
25
|
+
//
|
|
26
|
+
// 桌面操作的**知识与命令面**住在 `plugins/computer_use/`,可信执行层住在内核
|
|
27
|
+
// (`src/tools/computeruse/`)。这里**只**开放只读诊断门面:
|
|
28
|
+
//
|
|
29
|
+
// ⚠️ 曾经这里导出的是完整的 `computerUseService`,而那个对象的 `request()`
|
|
30
|
+
// 能派发 click / type / file_drop —— 一个「只做诊断与知识管理」的插件不该
|
|
31
|
+
// 拿得到操作鼠标键盘的能力。注释当时宣称「只开放这几件事」,代码没做到。
|
|
32
|
+
// 别再把 service 加回来。
|
|
33
|
+
export { computerUseDiagnostics } from './tools/computeruse/diagnostics.js';
|
|
34
|
+
export { describeCapabilities } from './tools/computeruse/capabilityLabels.js';
|
|
35
|
+
export { readRecentComputerUseAudit } from './cli/computerUse.js';
|
|
36
|
+
export { readComputerUseTelemetryReport } from './tools/computeruse/telemetryLog.js';
|
|
37
|
+
export { matchGuide, describeMatch, loadAllAppManuals, } from './tools/computeruse/vision/appGuide.js';
|
|
38
|
+
export { EMPTY_MANUAL, validateManual, } from './tools/computeruse/manualSchema.js';
|
|
22
39
|
// pre-read 守卫状态登记:tool 步覆盖「声明产物」时用来解除内核「先读后写 / 截断禁写」
|
|
23
40
|
// 守卫(确定性步的 args 写死、模型没机会先 Read)。详见 stepExecutors/tool.ts。
|
|
24
41
|
export { recordRead } from './tools/shared/fileState.js';
|
|
@@ -27,7 +44,10 @@ export { dlog } from './utils/debugLog.js';
|
|
|
27
44
|
// <think> 思维链剥离:内核与 workflow-runner 共享(原住 agentCommon.ts,
|
|
28
45
|
// Agent 子任务工具也需要 → 上移内核,插件经此 re-export)。
|
|
29
46
|
export { stripThinkBlocks } from './utils/stripThink.js';
|
|
30
|
-
|
|
47
|
+
// buildFullSystemPrompt is retained for compatibility. New long-running hosts should keep
|
|
48
|
+
// buildBaseSystemPrompt stable and pass buildTransientContext separately for each turn.
|
|
49
|
+
export { buildBaseSystemPrompt, buildTransientContext, buildTransientPromptBundle, } from './prompts/system.js';
|
|
50
|
+
export { createAgentRuntimeContext, filterToolsForRuntime, } from './runtime/agentRuntimeContext.js';
|
|
31
51
|
export { getResourceSearchPaths } from './utils/resourcePaths.js';
|
|
32
52
|
// 确定性检查设施:verify 步(shell/file_exists/file_contains/test_count)经此跑校验。
|
|
33
53
|
export { parseCheckString, runVerification } from './infra/verification.js';
|