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
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/plugins/hookEngine.ts —— Claude Code 风格 hook 派发引擎
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* 统一加载所有插件目录下的 hooks/hooks.json,按事件名建表,
|
|
6
|
+
* 在 minimal-agent 的关键节点(Stop / PreToolUse / PostToolUse /
|
|
7
|
+
* UserPromptSubmit / SessionStart / SessionEnd / PreCompact /
|
|
8
|
+
* Notification / SubagentStop)触发对应的 shell 命令。
|
|
9
|
+
*
|
|
10
|
+
* 契约(与 Claude Code 对齐):
|
|
11
|
+
* - hook 命令通过 stdin 收到 JSON payload(包含 session_id、
|
|
12
|
+
* transcript_path、cwd、hook_event_name 等基础字段 + 事件特定字段)
|
|
13
|
+
* - 命令在 stdout 输出 JSON:
|
|
14
|
+
* { decision: "block" | "approve" | "pass",
|
|
15
|
+
* reason?, systemMessage?, output?, suppressOutput? }
|
|
16
|
+
* - 解析失败 / 非 0 退出 / 空输出 → 视为 pass
|
|
17
|
+
* - 多 hook 串行执行,遇到第一个 block 立即短路返回
|
|
18
|
+
*
|
|
19
|
+
* Windows 兼容:
|
|
20
|
+
* - 以 .sh 结尾的命令直接跳过,并 warn 一次(bash 通常不可用)
|
|
21
|
+
* - 其他命令(.ps1 / .cmd / .bat / 可执行文件)走 shell:true
|
|
22
|
+
* - POSIX 上一律 `bash -c <command>`,避免依赖文件可执行位
|
|
23
|
+
*
|
|
24
|
+
* Matcher 支持(PreToolUse / PostToolUse):
|
|
25
|
+
* - 在 matcher 组级别声明 `matcher: 'Bash|Edit'` 等正则
|
|
26
|
+
* - payload.tool_name 不匹配则跳过这组所有命令
|
|
27
|
+
* - 缺省 matcher 视为通配
|
|
28
|
+
* ============================================================
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { spawn } from 'node:child_process';
|
|
32
|
+
import { readFile } from 'node:fs/promises';
|
|
33
|
+
import { join } from 'node:path';
|
|
34
|
+
|
|
35
|
+
import { sanitizeCredentialEnv } from '../config/configService.js';
|
|
36
|
+
|
|
37
|
+
export type HookEventName =
|
|
38
|
+
| 'PreToolUse'
|
|
39
|
+
| 'PostToolUse'
|
|
40
|
+
| 'Stop'
|
|
41
|
+
| 'SubagentStop'
|
|
42
|
+
| 'UserPromptSubmit'
|
|
43
|
+
| 'SessionStart'
|
|
44
|
+
| 'SessionEnd'
|
|
45
|
+
| 'PreCompact'
|
|
46
|
+
| 'Notification';
|
|
47
|
+
|
|
48
|
+
export interface HookDecision {
|
|
49
|
+
/** 'block' = 阻止当前事件 / 注入下一轮 prompt;'approve' = 显式通过;'pass'/undefined = 默认通过 */
|
|
50
|
+
decision?: 'block' | 'approve' | 'pass';
|
|
51
|
+
/** block 时,作为下一轮 user input 注入;approve 时仅日志 */
|
|
52
|
+
reason?: string;
|
|
53
|
+
/** UI/日志展示的提示 */
|
|
54
|
+
systemMessage?: string;
|
|
55
|
+
/** 输出给用户的额外文本(PreToolUse/PostToolUse 等) */
|
|
56
|
+
output?: string;
|
|
57
|
+
/** 是否抑制工具默认输出 */
|
|
58
|
+
suppressOutput?: boolean;
|
|
59
|
+
/** 多 hook 时,第一个返回 'block' 即短路 */
|
|
60
|
+
blockedBy?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface HookPayloadBase {
|
|
64
|
+
session_id: string;
|
|
65
|
+
transcript_path: string;
|
|
66
|
+
cwd: string;
|
|
67
|
+
hook_event_name: HookEventName;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface HookCommand {
|
|
71
|
+
/** ${CLAUDE_PLUGIN_ROOT} 已替换为绝对路径 */
|
|
72
|
+
command: string;
|
|
73
|
+
/** 命令所属的插件根目录(用于 spawn 时设置 CLAUDE_PLUGIN_ROOT env) */
|
|
74
|
+
pluginRoot: string;
|
|
75
|
+
/** 来源插件名(用于日志 / blockedBy) */
|
|
76
|
+
pluginName: string;
|
|
77
|
+
/** 可选 matcher 正则字符串(仅对 PreToolUse / PostToolUse 有意义) */
|
|
78
|
+
matcher?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const HOOK_EVENT_NAMES: ReadonlySet<HookEventName> = new Set<HookEventName>([
|
|
82
|
+
'PreToolUse',
|
|
83
|
+
'PostToolUse',
|
|
84
|
+
'Stop',
|
|
85
|
+
'SubagentStop',
|
|
86
|
+
'UserPromptSubmit',
|
|
87
|
+
'SessionStart',
|
|
88
|
+
'SessionEnd',
|
|
89
|
+
'PreCompact',
|
|
90
|
+
'Notification',
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
const HOOK_TIMEOUT_MS = 30_000;
|
|
94
|
+
|
|
95
|
+
const hookTable = new Map<HookEventName, HookCommand[]>();
|
|
96
|
+
let warnedWindowsShellSkip = false;
|
|
97
|
+
|
|
98
|
+
function pluginNameFromRoot(pluginRoot: string): string {
|
|
99
|
+
const norm = pluginRoot.replace(/\\/g, '/');
|
|
100
|
+
const parts = norm.split('/').filter(Boolean);
|
|
101
|
+
return parts[parts.length - 1] ?? pluginRoot;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function loadOnePluginHooks(pluginRoot: string): Promise<void> {
|
|
105
|
+
const hooksJsonPath = join(pluginRoot, 'hooks', 'hooks.json');
|
|
106
|
+
|
|
107
|
+
let parsed: unknown;
|
|
108
|
+
try {
|
|
109
|
+
const raw = await readFile(hooksJsonPath, 'utf8');
|
|
110
|
+
parsed = JSON.parse(raw);
|
|
111
|
+
} catch {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!parsed || typeof parsed !== 'object') return;
|
|
116
|
+
const hooksObj = (parsed as { hooks?: unknown }).hooks;
|
|
117
|
+
if (!hooksObj || typeof hooksObj !== 'object') return;
|
|
118
|
+
|
|
119
|
+
const pluginName = pluginNameFromRoot(pluginRoot);
|
|
120
|
+
|
|
121
|
+
for (const [eventKey, groups] of Object.entries(hooksObj as Record<string, unknown>)) {
|
|
122
|
+
if (!HOOK_EVENT_NAMES.has(eventKey as HookEventName)) continue;
|
|
123
|
+
if (!Array.isArray(groups)) continue;
|
|
124
|
+
|
|
125
|
+
const eventName = eventKey as HookEventName;
|
|
126
|
+
|
|
127
|
+
for (const group of groups) {
|
|
128
|
+
if (!group || typeof group !== 'object') continue;
|
|
129
|
+
const matcher = typeof (group as { matcher?: unknown }).matcher === 'string'
|
|
130
|
+
? ((group as { matcher: string }).matcher)
|
|
131
|
+
: undefined;
|
|
132
|
+
const innerHooks = (group as { hooks?: unknown }).hooks;
|
|
133
|
+
if (!Array.isArray(innerHooks)) continue;
|
|
134
|
+
|
|
135
|
+
for (const h of innerHooks) {
|
|
136
|
+
if (!h || typeof h !== 'object') continue;
|
|
137
|
+
const type = (h as { type?: unknown }).type;
|
|
138
|
+
const command = (h as { command?: unknown }).command;
|
|
139
|
+
if (type !== 'command' || typeof command !== 'string' || !command) continue;
|
|
140
|
+
|
|
141
|
+
const resolved = command.replaceAll('${CLAUDE_PLUGIN_ROOT}', pluginRoot);
|
|
142
|
+
|
|
143
|
+
const entry: HookCommand = {
|
|
144
|
+
command: resolved,
|
|
145
|
+
pluginRoot,
|
|
146
|
+
pluginName,
|
|
147
|
+
matcher,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const list = hookTable.get(eventName) ?? [];
|
|
151
|
+
list.push(entry);
|
|
152
|
+
hookTable.set(eventName, list);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** 加载所有插件目录的 hooks.json,按事件名建表(模块级缓存)。 */
|
|
159
|
+
export async function loadHooks(pluginRoots: string[]): Promise<void> {
|
|
160
|
+
hookTable.clear();
|
|
161
|
+
for (const root of pluginRoots) {
|
|
162
|
+
await loadOnePluginHooks(root);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** 重置 hook 缓存(测试用) */
|
|
167
|
+
export function _resetHookEngine(): void {
|
|
168
|
+
hookTable.clear();
|
|
169
|
+
warnedWindowsShellSkip = false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function matcherAllows(matcher: string | undefined, toolName: unknown): boolean {
|
|
173
|
+
if (!matcher) return true;
|
|
174
|
+
if (typeof toolName !== 'string' || !toolName) return true;
|
|
175
|
+
try {
|
|
176
|
+
const re = new RegExp(`^(${matcher})$`);
|
|
177
|
+
return re.test(toolName);
|
|
178
|
+
} catch {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function isPosixShellScript(command: string): boolean {
|
|
184
|
+
// 仅按文件后缀判断,命令字符串可能带参数,截取首段
|
|
185
|
+
const head = command.trim().split(/\s+/)[0] ?? '';
|
|
186
|
+
return head.toLowerCase().endsWith('.sh');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function runOneHook(
|
|
190
|
+
hook: HookCommand,
|
|
191
|
+
payload: Record<string, unknown>,
|
|
192
|
+
): Promise<HookDecision> {
|
|
193
|
+
return new Promise((resolve) => {
|
|
194
|
+
const isWin = process.platform === 'win32';
|
|
195
|
+
|
|
196
|
+
if (isWin && isPosixShellScript(hook.command)) {
|
|
197
|
+
if (!warnedWindowsShellSkip) {
|
|
198
|
+
warnedWindowsShellSkip = true;
|
|
199
|
+
console.warn(
|
|
200
|
+
`[minimal-agent] Skipping POSIX shell hook on Windows: ${hook.command}`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
resolve({ decision: 'pass' });
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const env = {
|
|
208
|
+
...sanitizeCredentialEnv(),
|
|
209
|
+
CLAUDE_PLUGIN_ROOT: hook.pluginRoot,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
let child: ReturnType<typeof spawn>;
|
|
213
|
+
try {
|
|
214
|
+
if (isWin) {
|
|
215
|
+
// Windows 上其他类型脚本(.ps1 / .cmd / .bat / 可执行文件)走 shell
|
|
216
|
+
child = spawn(hook.command, { env, shell: true });
|
|
217
|
+
} else {
|
|
218
|
+
// POSIX 一律 bash -c,避开文件可执行位 / shebang 问题
|
|
219
|
+
child = spawn('bash', ['-c', hook.command], { env });
|
|
220
|
+
}
|
|
221
|
+
} catch {
|
|
222
|
+
resolve({ decision: 'pass' });
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let stdout = '';
|
|
227
|
+
let settled = false;
|
|
228
|
+
|
|
229
|
+
const finalize = (decision: HookDecision) => {
|
|
230
|
+
if (settled) return;
|
|
231
|
+
settled = true;
|
|
232
|
+
clearTimeout(timer);
|
|
233
|
+
resolve(decision);
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const timer = setTimeout(() => {
|
|
237
|
+
try {
|
|
238
|
+
child.kill('SIGTERM');
|
|
239
|
+
} catch {
|
|
240
|
+
// ignore
|
|
241
|
+
}
|
|
242
|
+
console.warn(`[minimal-agent] Hook timeout (>${HOOK_TIMEOUT_MS}ms): ${hook.command}`);
|
|
243
|
+
finalize({ decision: 'pass' });
|
|
244
|
+
}, HOOK_TIMEOUT_MS);
|
|
245
|
+
|
|
246
|
+
child.stdout?.on('data', (data: Buffer) => {
|
|
247
|
+
stdout += data.toString();
|
|
248
|
+
});
|
|
249
|
+
// stderr 内容不消费但必须排空:spawn 默认 pipe,无 reader 时 hook 往 stderr
|
|
250
|
+
// 写满 ~64KB 管道缓冲会阻塞,硬等到 30s 超时被 SIGTERM。
|
|
251
|
+
child.stderr?.resume();
|
|
252
|
+
|
|
253
|
+
child.on('error', () => {
|
|
254
|
+
finalize({ decision: 'pass' });
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
child.on('close', (code) => {
|
|
258
|
+
if (code !== 0) {
|
|
259
|
+
finalize({ decision: 'pass' });
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const trimmed = stdout.trim();
|
|
264
|
+
if (!trimmed) {
|
|
265
|
+
finalize({ decision: 'pass' });
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const parsed = JSON.parse(trimmed) as Record<string, unknown>;
|
|
271
|
+
const decision = parsed.decision;
|
|
272
|
+
|
|
273
|
+
if (decision === 'block' || decision === 'approve') {
|
|
274
|
+
const result: HookDecision = {
|
|
275
|
+
decision,
|
|
276
|
+
reason: typeof parsed.reason === 'string' ? parsed.reason : undefined,
|
|
277
|
+
systemMessage:
|
|
278
|
+
typeof parsed.systemMessage === 'string' ? parsed.systemMessage : undefined,
|
|
279
|
+
output: typeof parsed.output === 'string' ? parsed.output : undefined,
|
|
280
|
+
suppressOutput:
|
|
281
|
+
typeof parsed.suppressOutput === 'boolean' ? parsed.suppressOutput : undefined,
|
|
282
|
+
};
|
|
283
|
+
if (decision === 'block') {
|
|
284
|
+
result.blockedBy = hook.pluginName;
|
|
285
|
+
}
|
|
286
|
+
finalize(result);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
finalize({ decision: 'pass' });
|
|
291
|
+
} catch {
|
|
292
|
+
finalize({ decision: 'pass' });
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
child.stdin?.write(JSON.stringify(payload));
|
|
298
|
+
child.stdin?.end();
|
|
299
|
+
} catch {
|
|
300
|
+
// 子进程已挂掉等情况,等 'close' / 'error' 处理
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* 触发某事件。payload 会与 base 字段合并写入 stdin。
|
|
307
|
+
* 多 hook 同事件 → 按声明顺序串行执行,遇到 block 立即短路返回。
|
|
308
|
+
* Windows 上 .sh 命令直接跳过 + 一次性 warn。
|
|
309
|
+
*/
|
|
310
|
+
export async function triggerHook(
|
|
311
|
+
eventName: HookEventName,
|
|
312
|
+
payload: Omit<HookPayloadBase, 'hook_event_name'> & Record<string, unknown>,
|
|
313
|
+
): Promise<HookDecision> {
|
|
314
|
+
const hooks = hookTable.get(eventName);
|
|
315
|
+
if (!hooks || hooks.length === 0) {
|
|
316
|
+
return { decision: 'pass' };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const fullPayload: Record<string, unknown> = {
|
|
320
|
+
...payload,
|
|
321
|
+
hook_event_name: eventName,
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
let lastDecision: HookDecision = { decision: 'pass' };
|
|
325
|
+
|
|
326
|
+
for (const hook of hooks) {
|
|
327
|
+
if (eventName === 'PreToolUse' || eventName === 'PostToolUse') {
|
|
328
|
+
if (!matcherAllows(hook.matcher, fullPayload.tool_name)) continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const result = await runOneHook(hook, fullPayload);
|
|
332
|
+
|
|
333
|
+
if (result.decision === 'block') {
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
lastDecision = result;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return lastDecision;
|
|
341
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/plugins/pluginApi.ts —— 插件 plugin.ts 的契约
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* 双层插件契约的"代码层"接口:
|
|
6
|
+
*
|
|
7
|
+
* plugins/<id>/.claude-plugin/plugin.json → 元数据
|
|
8
|
+
* plugins/<id>/commands/*.md → 声明式 slash 命令(默认走 LLM)
|
|
9
|
+
* plugins/<id>/hooks/hooks.json → 声明式 hook
|
|
10
|
+
* plugins/<id>/plugin.ts → 可选!要接管命令分发时才写
|
|
11
|
+
*
|
|
12
|
+
* 纯 markdown 插件(无 plugin.ts)零修改 drop-in,覆盖 99% Anthropic 上游用例;
|
|
13
|
+
* 富插件写 plugin.ts 接管 runCommand,把全部逻辑放在自己目录里,外部 src/ 一行不改。
|
|
14
|
+
*
|
|
15
|
+
* PluginContext 是框架"愿意暴露给插件"的最小运行时上下文:
|
|
16
|
+
* - provider / history / signal / sessionState 都是 runQuery 本来就接受的
|
|
17
|
+
* - 插件作者通过 src/plugin-sdk.ts 取 runQuery、chat、executeTool 等
|
|
18
|
+
*
|
|
19
|
+
* PluginEvent 是事件层的"开放契约":插件可以 yield 任意带 type 字段的自定义事件
|
|
20
|
+
* ——框架原样透传,UI 不识别就静默忽略。让插件不必把私有事件挤进 LoopEvent。
|
|
21
|
+
* ============================================================
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { ContextBundle } from '../context/contextBundle.js';
|
|
25
|
+
import type { AgentRuntimeContext } from '../runtime/agentRuntimeContext.js';
|
|
26
|
+
import type {
|
|
27
|
+
RunAgentTurnOptions,
|
|
28
|
+
RunAgentTurnResult,
|
|
29
|
+
} from '../session/runAgentTurn.js';
|
|
30
|
+
import type { SessionState } from '../session/sessionState.js';
|
|
31
|
+
import type {
|
|
32
|
+
LoopEvent,
|
|
33
|
+
Message,
|
|
34
|
+
PromptCacheContext,
|
|
35
|
+
Provider,
|
|
36
|
+
ToolCallContext,
|
|
37
|
+
ToolResult,
|
|
38
|
+
} from '../types.js';
|
|
39
|
+
|
|
40
|
+
/** 框架传给插件的运行时上下文(仅暴露执行 LLM/工具 所需的最小集合)。 */
|
|
41
|
+
export interface PluginContext {
|
|
42
|
+
provider: Provider;
|
|
43
|
+
/** 调用方持有的消息历史,插件可读可写(mutate 同一引用) */
|
|
44
|
+
history: Message[];
|
|
45
|
+
/** 用户取消信号 */
|
|
46
|
+
signal?: AbortSignal;
|
|
47
|
+
/** 多租户场景注入的隔离 session 状态;缺省走模块单例 */
|
|
48
|
+
sessionState?: SessionState;
|
|
49
|
+
/** maxTurns 透传 */
|
|
50
|
+
maxTurns?: number;
|
|
51
|
+
/** 当前回合的瞬时外部上下文;插件不得把它持久化到 history。 */
|
|
52
|
+
transientContext?: string;
|
|
53
|
+
transientBundle?: ContextBundle;
|
|
54
|
+
promptCache?: PromptCacheContext;
|
|
55
|
+
/** Canonical host-issued trust context; plugins may narrow it but never elevate it. */
|
|
56
|
+
runtimeContext?: AgentRuntimeContext;
|
|
57
|
+
/** Host tool names are an upper bound. Plugins may narrow, never expand. */
|
|
58
|
+
allowedTools?: readonly string[];
|
|
59
|
+
/**
|
|
60
|
+
* Host tool dispatcher. Rich plugins must use the same injected executor as
|
|
61
|
+
* the parent run so tests, embedders and host-specific dispatch remain on one
|
|
62
|
+
* execution path.
|
|
63
|
+
*/
|
|
64
|
+
executeTool?: (
|
|
65
|
+
name: string,
|
|
66
|
+
rawArgsJson: string,
|
|
67
|
+
signal?: AbortSignal,
|
|
68
|
+
context?: ToolCallContext,
|
|
69
|
+
) => Promise<ToolResult>;
|
|
70
|
+
/**
|
|
71
|
+
* 宿主的**授权与可见性上下文**,原样转交给工具层(`executeTool` 第四参)。
|
|
72
|
+
*
|
|
73
|
+
* ⚠️ 为什么是**一个** `Pick<ToolCallContext, …>` 而不是逐个平铺四个字段:
|
|
74
|
+
* · 它就是「工具接收什么」那个**已有**抽象的子集 —— 用 `Pick` 绑定,
|
|
75
|
+
* `ToolCallContext` 改了这里跟着改,不会出现两份各自漂移的定义;
|
|
76
|
+
* · 将来新增宿主能力(移动端、远程桌面…)时这个接口零改动(Open/Closed)。
|
|
77
|
+
*
|
|
78
|
+
* ⚠️ 此前缺席的后果:`pluginRunner` 的三处下传里,两条 runQuery 路径都带了
|
|
79
|
+
* browserRun/computerRun,**唯独 `api.runCommand(...)` 那一处漏了** —— 于是
|
|
80
|
+
* `PluginContext` 比 `RunQueryOptions` 窄,富插件(如 workflow-runner)永远拿不到
|
|
81
|
+
* 宿主授权。表现是 workflow 里调 ComputerUse 恒返回「当前宿主未启用 Computer Use」,
|
|
82
|
+
* 而同一个工具在主循环里好好的。
|
|
83
|
+
*
|
|
84
|
+
* 插件**不应**无条件把它转给每一步:能力要由各步显式声明后按需转发
|
|
85
|
+
* (见 workflow-runner 的 `StepDef.uses`),否则一份无人值守的脚本就隐式拿到了
|
|
86
|
+
* 完整的机器控制权。
|
|
87
|
+
*/
|
|
88
|
+
host?: Pick<
|
|
89
|
+
ToolCallContext,
|
|
90
|
+
'browserRun' | 'computerRun' | 'computerIntervention' | 'toolGate'
|
|
91
|
+
>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** TUI/App 生命周期传给富插件的宿主能力。 */
|
|
95
|
+
export interface PluginRuntimeContext {
|
|
96
|
+
provider: Provider;
|
|
97
|
+
signal?: AbortSignal;
|
|
98
|
+
runAgentTurn: (opts: RunAgentTurnOptions) => Promise<RunAgentTurnResult>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 插件自定义事件的开放形状。
|
|
103
|
+
*
|
|
104
|
+
* 任何带 `type: string` 字段的对象都允许;payload 字段由插件自己定义。
|
|
105
|
+
* 框架 yield 时原样透传,UI 的 handleEvent 用 switch 派发——
|
|
106
|
+
* 无 case 匹配的 type 走 default no-op,等同静默忽略。
|
|
107
|
+
*
|
|
108
|
+
* 例:workflow-runner 的 WorkflowEvent(`type: 'workflow_start'` 等)
|
|
109
|
+
* 在结构上自动满足 PluginEvent,无需 cast。
|
|
110
|
+
*/
|
|
111
|
+
export interface PluginEvent {
|
|
112
|
+
type: string;
|
|
113
|
+
[key: string]: unknown;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 插件 plugin.ts 的 default export 形状。
|
|
118
|
+
*
|
|
119
|
+
* `runCommand` 接管声明式 markdown fallback:返回 AsyncGenerator 让框架直接 yield* 出去。
|
|
120
|
+
* 返回类型允许 LoopEvent(框架已识别)+ PluginEvent(插件私有事件)混合 yield。
|
|
121
|
+
* 不实现 runCommand → 框架回退到 buildCommandInput + runQuery(纯 prompt 模式)。
|
|
122
|
+
*/
|
|
123
|
+
export interface PluginApi {
|
|
124
|
+
activate?(
|
|
125
|
+
ctx: PluginRuntimeContext,
|
|
126
|
+
): // biome-ignore lint/suspicious/noConfusingVoidType: lifecycle hooks intentionally accept synchronous void or an async disposer.
|
|
127
|
+
Promise<void | (() => void)> | void | (() => void);
|
|
128
|
+
runCommand?(
|
|
129
|
+
commandName: string,
|
|
130
|
+
args: string,
|
|
131
|
+
ctx: PluginContext,
|
|
132
|
+
): AsyncGenerator<LoopEvent | PluginEvent, void, void>;
|
|
133
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { discoverPlugins } from './commandRouter.js';
|
|
2
|
+
import { loadPluginApi } from './pluginLoader.js';
|
|
3
|
+
import type { PluginRuntimeContext } from './pluginApi.js';
|
|
4
|
+
|
|
5
|
+
export async function activatePlugins(
|
|
6
|
+
ctx: PluginRuntimeContext,
|
|
7
|
+
): Promise<() => void> {
|
|
8
|
+
const manifests = await discoverPlugins();
|
|
9
|
+
const cleanups: Array<() => void> = [];
|
|
10
|
+
|
|
11
|
+
for (const manifest of manifests) {
|
|
12
|
+
const api = await loadPluginApi(manifest.root);
|
|
13
|
+
if (!api?.activate) continue;
|
|
14
|
+
try {
|
|
15
|
+
const cleanup = await api.activate(ctx);
|
|
16
|
+
if (typeof cleanup === 'function') cleanups.push(cleanup);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.warn(
|
|
19
|
+
`[minimal-agent] plugin activate failed (${manifest.name}): ${
|
|
20
|
+
err instanceof Error ? err.message : String(err)
|
|
21
|
+
}`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return () => {
|
|
27
|
+
for (const cleanup of cleanups.reverse()) {
|
|
28
|
+
try {
|
|
29
|
+
cleanup();
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/plugins/pluginLoader.ts —— plugin entry dynamic import 缓存层
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* 按 pluginRoot 路径 lazy import 插件目录下的 plugin.js(优先)
|
|
6
|
+
* 或 plugin.ts(回退)。
|
|
7
|
+
*
|
|
8
|
+
* 双候选探测的原因:
|
|
9
|
+
* - install 模式(npm 包内):tsc 编译后只剩 plugin.js,Node + Bun
|
|
10
|
+
* 都能加载
|
|
11
|
+
* - dev 模式(仓库源码):物理只有 plugin.ts,Bun 直接吃
|
|
12
|
+
* - 用户 cwd 自带插件:可能写 .ts(须 Bun 跑)或 .js(Node + Bun 都行)
|
|
13
|
+
*
|
|
14
|
+
* 契约:
|
|
15
|
+
* - 两个文件都不存在 → 缓存 null(纯 markdown 声明式插件)
|
|
16
|
+
* - 两个文件都存在 + plugin.ts mtime > plugin.js mtime → console.warn
|
|
17
|
+
* 提示 stale(dev 模式跑过 build 后又改 .ts 源码的常见坑),加载行为
|
|
18
|
+
* 不变仍优先 plugin.js
|
|
19
|
+
* - import 失败(语法错 / 运行时抛错)→ console.warn + 缓存 null
|
|
20
|
+
* - import 成功但 default 不是对象 → 缓存 null
|
|
21
|
+
* - 缓存 key = pluginRoot 绝对路径
|
|
22
|
+
*
|
|
23
|
+
* 框架 hot-reload 由测试调用 _resetPluginLoader() 实现。
|
|
24
|
+
* ============================================================
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { existsSync, statSync } from 'node:fs';
|
|
28
|
+
import { basename, join } from 'node:path';
|
|
29
|
+
import { pathToFileURL } from 'node:url';
|
|
30
|
+
|
|
31
|
+
import type { PluginApi } from './pluginApi.js';
|
|
32
|
+
|
|
33
|
+
const loaderCache = new Map<string, PluginApi | null>();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 内置插件静态注册表(仅绿色版 compile 入口 entry.compiled.tsx 会填充)。
|
|
37
|
+
*
|
|
38
|
+
* 背景:`bun --compile` 后,rich 插件的 plugin.ts 顺链 import 的框架 SDK
|
|
39
|
+
* (`../../../src/plugin-sdk.js`)已被焊进 exe、磁盘上不存在 → 动态 import
|
|
40
|
+
* 外部 plugin.ts 必抛错。绿色版改为在 compile 入口**静态 import** 内置插件
|
|
41
|
+
* (`src/plugins/builtinRegistry.ts`),让 bun 把执行器整树打进 exe,再经
|
|
42
|
+
* setBuiltinPluginApi 注册到这里;loadPluginApi 在磁盘加载缺失/失败时回退到此表。
|
|
43
|
+
*
|
|
44
|
+
* dev / npm 模式:entry.compiled 永不被 import → 此表恒为空 → 回退分支不触发
|
|
45
|
+
* → loadPluginApi 行为与改动前逐字节一致。
|
|
46
|
+
*
|
|
47
|
+
* key = 插件目录名(`plugins/<id>/` 的 `<id>`,等于 `basename(pluginRoot)`)。
|
|
48
|
+
*/
|
|
49
|
+
const builtinApis = new Map<string, PluginApi>();
|
|
50
|
+
|
|
51
|
+
/** 由绿色版 compile 入口调用,注册内置插件执行器。dev/npm 不会调用。 */
|
|
52
|
+
export function setBuiltinPluginApi(key: string, api: PluginApi): void {
|
|
53
|
+
builtinApis.set(key, api);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isPluginApi(value: unknown): value is PluginApi {
|
|
57
|
+
if (!value || typeof value !== 'object') return false;
|
|
58
|
+
const { runCommand } = value as { runCommand?: unknown };
|
|
59
|
+
if (runCommand !== undefined && typeof runCommand !== 'function') return false;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 加载插件目录下的 plugin.ts;不存在或失败返回 null。
|
|
65
|
+
* 同一 pluginRoot 多次调用走缓存,避免重复 import。
|
|
66
|
+
*/
|
|
67
|
+
export async function loadPluginApi(pluginRoot: string): Promise<PluginApi | null> {
|
|
68
|
+
if (loaderCache.has(pluginRoot)) {
|
|
69
|
+
return loaderCache.get(pluginRoot) ?? null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// prefer plugin.js(install / Node 兼容),fallback plugin.ts(dev / 用户 Bun 插件)
|
|
73
|
+
const jsPath = join(pluginRoot, 'plugin.js');
|
|
74
|
+
const tsPath = join(pluginRoot, 'plugin.ts');
|
|
75
|
+
const jsExists = existsSync(jsPath);
|
|
76
|
+
const tsExists = existsSync(tsPath);
|
|
77
|
+
|
|
78
|
+
// dev 模式 stale 检测:两文件都存在 + .ts 比 .js 新 → 用户改了源码但没清编译产物
|
|
79
|
+
if (jsExists && tsExists) {
|
|
80
|
+
try {
|
|
81
|
+
const jsMtime = statSync(jsPath).mtimeMs;
|
|
82
|
+
const tsMtime = statSync(tsPath).mtimeMs;
|
|
83
|
+
if (tsMtime > jsMtime) {
|
|
84
|
+
console.warn(
|
|
85
|
+
`[minimal-agent] 检测到 stale plugin.js(${pluginRoot}):plugin.ts 比 plugin.js 新,` +
|
|
86
|
+
`加载器仍优先吃 plugin.js。如果你刚改过源码却没生效,请跑 \`bun run clean\` 删掉原地编译产物。`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
// stat 失败静默,不影响加载
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 绿色版回退:磁盘加载缺失/失败时,按目录名查 exe 内静态注册表。
|
|
95
|
+
// dev/npm 注册表恒空 → 永远返回 null → 不改变下方任何分支的最终结果。
|
|
96
|
+
const builtinKey = basename(pluginRoot);
|
|
97
|
+
const builtinFallback = (): PluginApi | null => {
|
|
98
|
+
const api = builtinApis.get(builtinKey);
|
|
99
|
+
if (api) {
|
|
100
|
+
loaderCache.set(pluginRoot, api);
|
|
101
|
+
return api;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const pluginEntry = jsExists ? jsPath : tsExists ? tsPath : undefined;
|
|
107
|
+
if (!pluginEntry) {
|
|
108
|
+
// 磁盘无 plugin.js/.ts:绿色目录只铺声明式部分(plugin.json + commands/),
|
|
109
|
+
// 执行器在 exe 内注册表 → 这里命中。dev/npm 注册表空 → 维持原 null。
|
|
110
|
+
const fb = builtinFallback();
|
|
111
|
+
if (fb) return fb;
|
|
112
|
+
loaderCache.set(pluginRoot, null);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const mod = await import(pathToFileURL(pluginEntry).href);
|
|
118
|
+
const candidate = (mod as { default?: unknown }).default;
|
|
119
|
+
if (!isPluginApi(candidate)) {
|
|
120
|
+
console.warn(
|
|
121
|
+
`[minimal-agent] plugin.ts default export is not a valid PluginApi: ${pluginRoot}`,
|
|
122
|
+
);
|
|
123
|
+
loaderCache.set(pluginRoot, null);
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
loaderCache.set(pluginRoot, candidate);
|
|
127
|
+
return candidate;
|
|
128
|
+
} catch (err) {
|
|
129
|
+
// 绿色版:磁盘铺了 plugin.js 但它顺链 import 的框架 SDK 在 exe 内、磁盘没有
|
|
130
|
+
// → import 抛错 → 回退到 exe 内注册表。dev/npm 注册表空 → 维持原 warn + null。
|
|
131
|
+
const fb = builtinFallback();
|
|
132
|
+
if (fb) return fb;
|
|
133
|
+
console.warn(
|
|
134
|
+
`[minimal-agent] failed to load plugin entry at ${pluginRoot}: ${
|
|
135
|
+
err instanceof Error ? err.message : String(err)
|
|
136
|
+
}`,
|
|
137
|
+
);
|
|
138
|
+
loaderCache.set(pluginRoot, null);
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** 测试用:重置加载器缓存 + 绿色版内置注册表。 */
|
|
144
|
+
export function _resetPluginLoader(): void {
|
|
145
|
+
loaderCache.clear();
|
|
146
|
+
builtinApis.clear();
|
|
147
|
+
}
|