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,119 @@
|
|
|
1
|
+
import { countTextTokens } from './tokenCounter.js';
|
|
2
|
+
export const DEFAULT_TRANSIENT_BUDGET_TOKENS = 3_000;
|
|
3
|
+
export function contextFragment(input) {
|
|
4
|
+
const content = input.content.trim();
|
|
5
|
+
return {
|
|
6
|
+
...input,
|
|
7
|
+
content,
|
|
8
|
+
estimatedTokens: input.estimatedTokens ?? countTextTokens(content),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const CLIP_MARKER = '\n[上下文片段因预算截断]';
|
|
12
|
+
/**
|
|
13
|
+
* 按**渲染后**的 token 数收敛,而不是裸内容。
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ 曾经的实现拿裸内容 token 判预算、外层却用 `renderedTokens()`(JSON 转义 + 信封)验收,
|
|
16
|
+
* 于是 `裸 token ≤ 预算 < 渲染 token` 这个窗口里 clip 会「认为放得下」原样返回,验收再不通过,
|
|
17
|
+
* 片段就被整段丢进 `omittedIds` —— `clippable` 形同虚设(`clippedIds` 恒空)。
|
|
18
|
+
* 窗口宽度 = 转义膨胀量,所以 **Windows 上尤其致命**:技能目录里每个 `\` 都要转义成 `\\`,
|
|
19
|
+
* 27 条技能的窗口从 POSIX 的 ~25 token 涨到 ~92 token,本仓库实测整份 `skills.catalog`
|
|
20
|
+
* (2643 token)在还剩 1900+ token 预算时被整个扔掉,模型完全看不到任何 Skill。
|
|
21
|
+
*
|
|
22
|
+
* 判据必须与验收同口径:这里直接对 `measure(候选内容)` 二分,measure 由调用方传入,
|
|
23
|
+
* 保证「算的就是最终要花的」。
|
|
24
|
+
*/
|
|
25
|
+
function clipToRenderedBudget(content, budgetTokens, measure) {
|
|
26
|
+
if (budgetTokens <= 0)
|
|
27
|
+
return '';
|
|
28
|
+
if (measure(content) <= budgetTokens)
|
|
29
|
+
return content;
|
|
30
|
+
// Token counter is deliberately dependency-free. Binary search preserves its
|
|
31
|
+
// exact mixed Chinese/ASCII estimate instead of falling back to chars/4.
|
|
32
|
+
let low = 0;
|
|
33
|
+
let high = content.length;
|
|
34
|
+
while (low < high) {
|
|
35
|
+
const mid = Math.ceil((low + high) / 2);
|
|
36
|
+
const candidate = `${content.slice(0, mid).trimEnd()}${CLIP_MARKER}`;
|
|
37
|
+
if (measure(candidate) <= budgetTokens)
|
|
38
|
+
low = mid;
|
|
39
|
+
else
|
|
40
|
+
high = mid - 1;
|
|
41
|
+
}
|
|
42
|
+
if (low <= 0)
|
|
43
|
+
return '';
|
|
44
|
+
return `${content.slice(0, low).trimEnd()}${CLIP_MARKER}`;
|
|
45
|
+
}
|
|
46
|
+
export function renderContextFragment(fragment) {
|
|
47
|
+
const encoded = JSON.stringify(fragment.content)
|
|
48
|
+
.replace(/</g, '\\u003c')
|
|
49
|
+
.replace(/>/g, '\\u003e')
|
|
50
|
+
.replace(/&/g, '\\u0026');
|
|
51
|
+
return `<context-fragment id="${fragment.id}" kind="${fragment.kind}" authority="${fragment.authority}" encoding="json">\n${encoded}\n</context-fragment>`;
|
|
52
|
+
}
|
|
53
|
+
function renderedTokens(fragment) {
|
|
54
|
+
return countTextTokens(renderContextFragment(fragment));
|
|
55
|
+
}
|
|
56
|
+
/** 片段之间的分隔符——记账时必须与最终 join 用的是同一个常量。 */
|
|
57
|
+
const FRAGMENT_SEPARATOR = '\n\n';
|
|
58
|
+
const SEPARATOR_TOKENS = countTextTokens(FRAGMENT_SEPARATOR);
|
|
59
|
+
/**
|
|
60
|
+
* Deterministic context budgeting. Required host facts are kept first; optional
|
|
61
|
+
* fragments compete by priority, then original order. No LLM call is involved.
|
|
62
|
+
*/
|
|
63
|
+
export function assembleContextBundle(input, budgetTokens = DEFAULT_TRANSIENT_BUDGET_TOKENS) {
|
|
64
|
+
const fragments = input.filter((fragment) => fragment.content.length > 0);
|
|
65
|
+
const indexed = fragments.map((fragment, index) => ({ fragment, index }));
|
|
66
|
+
const ordered = indexed.sort((a, b) => Number(Boolean(b.fragment.required)) - Number(Boolean(a.fragment.required))
|
|
67
|
+
|| b.fragment.priority - a.fragment.priority
|
|
68
|
+
|| a.index - b.index);
|
|
69
|
+
const included = [];
|
|
70
|
+
const omittedIds = [];
|
|
71
|
+
const clippedIds = [];
|
|
72
|
+
let remaining = Math.max(0, budgetTokens);
|
|
73
|
+
for (const item of ordered) {
|
|
74
|
+
const fragment = item.fragment;
|
|
75
|
+
// 片段之间要用 SEPARATOR 拼接,那几个 token 也得记账,否则最终 text 会超出预算
|
|
76
|
+
// (usedTokens > budgetTokens 会让 loop.ts::transientFor 的余量判断失真)。
|
|
77
|
+
const separator = included.length > 0 ? SEPARATOR_TOKENS : 0;
|
|
78
|
+
const budgetForItem = remaining - separator;
|
|
79
|
+
const fullTokens = renderedTokens(fragment);
|
|
80
|
+
if (fullTokens <= budgetForItem) {
|
|
81
|
+
included.push(item);
|
|
82
|
+
remaining -= fullTokens + separator;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if ((fragment.required || fragment.clippable) && budgetForItem > 24) {
|
|
86
|
+
// 同口径:二分时就按「这段内容渲染进本片段信封后要花多少 token」来量。
|
|
87
|
+
const content = clipToRenderedBudget(fragment.content, budgetForItem, (candidate) => renderedTokens({ ...fragment, content: candidate }));
|
|
88
|
+
const clipped = contextFragment({ ...fragment, content });
|
|
89
|
+
const clippedTokens = renderedTokens(clipped);
|
|
90
|
+
if (clipped.content && clippedTokens <= budgetForItem) {
|
|
91
|
+
included.push({ fragment: clipped, index: item.index });
|
|
92
|
+
if (clipped.content !== fragment.content)
|
|
93
|
+
clippedIds.push(fragment.id);
|
|
94
|
+
remaining -= clippedTokens + separator;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
omittedIds.push(fragment.id);
|
|
99
|
+
}
|
|
100
|
+
// Rendering follows semantic input order, not selection priority.
|
|
101
|
+
included.sort((a, b) => a.index - b.index);
|
|
102
|
+
const selected = included.map((item) => item.fragment);
|
|
103
|
+
const text = selected.map(renderContextFragment).join(FRAGMENT_SEPARATOR);
|
|
104
|
+
const usedTokens = countTextTokens(text);
|
|
105
|
+
return {
|
|
106
|
+
fragments: selected,
|
|
107
|
+
text,
|
|
108
|
+
diagnostics: {
|
|
109
|
+
budgetTokens,
|
|
110
|
+
usedTokens,
|
|
111
|
+
includedIds: selected.map((fragment) => fragment.id),
|
|
112
|
+
omittedIds,
|
|
113
|
+
clippedIds,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function resizeContextBundle(bundle, budgetTokens) {
|
|
118
|
+
return assembleContextBundle(bundle.fragments, budgetTokens);
|
|
119
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { countTextTokens } from './tokenCounter.js';
|
|
2
|
+
|
|
3
|
+
export type ContextFragmentKind =
|
|
4
|
+
| 'runtime'
|
|
5
|
+
| 'persona'
|
|
6
|
+
| 'memory'
|
|
7
|
+
| 'episode'
|
|
8
|
+
| 'project'
|
|
9
|
+
| 'skill'
|
|
10
|
+
| 'plugin'
|
|
11
|
+
| 'task'
|
|
12
|
+
| 'diagnostic';
|
|
13
|
+
|
|
14
|
+
export type ContextAuthority = 'host' | 'constraint' | 'user-evidence' | 'untrusted-data';
|
|
15
|
+
|
|
16
|
+
export interface ContextFragment {
|
|
17
|
+
id: string;
|
|
18
|
+
kind: ContextFragmentKind;
|
|
19
|
+
authority: ContextAuthority;
|
|
20
|
+
priority: number;
|
|
21
|
+
estimatedTokens: number;
|
|
22
|
+
content: string;
|
|
23
|
+
required?: boolean;
|
|
24
|
+
clippable?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ContextBudgetDiagnostics {
|
|
28
|
+
budgetTokens: number;
|
|
29
|
+
usedTokens: number;
|
|
30
|
+
includedIds: string[];
|
|
31
|
+
omittedIds: string[];
|
|
32
|
+
clippedIds: string[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ContextBundle {
|
|
36
|
+
fragments: ContextFragment[];
|
|
37
|
+
text: string;
|
|
38
|
+
diagnostics: ContextBudgetDiagnostics;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const DEFAULT_TRANSIENT_BUDGET_TOKENS = 3_000;
|
|
42
|
+
|
|
43
|
+
export function contextFragment(
|
|
44
|
+
input: Omit<ContextFragment, 'estimatedTokens'> & { estimatedTokens?: number },
|
|
45
|
+
): ContextFragment {
|
|
46
|
+
const content = input.content.trim();
|
|
47
|
+
return {
|
|
48
|
+
...input,
|
|
49
|
+
content,
|
|
50
|
+
estimatedTokens: input.estimatedTokens ?? countTextTokens(content),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const CLIP_MARKER = '\n[上下文片段因预算截断]';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 按**渲染后**的 token 数收敛,而不是裸内容。
|
|
58
|
+
*
|
|
59
|
+
* ⚠️ 曾经的实现拿裸内容 token 判预算、外层却用 `renderedTokens()`(JSON 转义 + 信封)验收,
|
|
60
|
+
* 于是 `裸 token ≤ 预算 < 渲染 token` 这个窗口里 clip 会「认为放得下」原样返回,验收再不通过,
|
|
61
|
+
* 片段就被整段丢进 `omittedIds` —— `clippable` 形同虚设(`clippedIds` 恒空)。
|
|
62
|
+
* 窗口宽度 = 转义膨胀量,所以 **Windows 上尤其致命**:技能目录里每个 `\` 都要转义成 `\\`,
|
|
63
|
+
* 27 条技能的窗口从 POSIX 的 ~25 token 涨到 ~92 token,本仓库实测整份 `skills.catalog`
|
|
64
|
+
* (2643 token)在还剩 1900+ token 预算时被整个扔掉,模型完全看不到任何 Skill。
|
|
65
|
+
*
|
|
66
|
+
* 判据必须与验收同口径:这里直接对 `measure(候选内容)` 二分,measure 由调用方传入,
|
|
67
|
+
* 保证「算的就是最终要花的」。
|
|
68
|
+
*/
|
|
69
|
+
function clipToRenderedBudget(
|
|
70
|
+
content: string,
|
|
71
|
+
budgetTokens: number,
|
|
72
|
+
measure: (candidate: string) => number,
|
|
73
|
+
): string {
|
|
74
|
+
if (budgetTokens <= 0) return '';
|
|
75
|
+
if (measure(content) <= budgetTokens) return content;
|
|
76
|
+
// Token counter is deliberately dependency-free. Binary search preserves its
|
|
77
|
+
// exact mixed Chinese/ASCII estimate instead of falling back to chars/4.
|
|
78
|
+
let low = 0;
|
|
79
|
+
let high = content.length;
|
|
80
|
+
while (low < high) {
|
|
81
|
+
const mid = Math.ceil((low + high) / 2);
|
|
82
|
+
const candidate = `${content.slice(0, mid).trimEnd()}${CLIP_MARKER}`;
|
|
83
|
+
if (measure(candidate) <= budgetTokens) low = mid;
|
|
84
|
+
else high = mid - 1;
|
|
85
|
+
}
|
|
86
|
+
if (low <= 0) return '';
|
|
87
|
+
return `${content.slice(0, low).trimEnd()}${CLIP_MARKER}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function renderContextFragment(fragment: ContextFragment): string {
|
|
91
|
+
const encoded = JSON.stringify(fragment.content)
|
|
92
|
+
.replace(/</g, '\\u003c')
|
|
93
|
+
.replace(/>/g, '\\u003e')
|
|
94
|
+
.replace(/&/g, '\\u0026');
|
|
95
|
+
return `<context-fragment id="${fragment.id}" kind="${fragment.kind}" authority="${fragment.authority}" encoding="json">\n${encoded}\n</context-fragment>`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function renderedTokens(fragment: ContextFragment): number {
|
|
99
|
+
return countTextTokens(renderContextFragment(fragment));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** 片段之间的分隔符——记账时必须与最终 join 用的是同一个常量。 */
|
|
103
|
+
const FRAGMENT_SEPARATOR = '\n\n';
|
|
104
|
+
const SEPARATOR_TOKENS = countTextTokens(FRAGMENT_SEPARATOR);
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Deterministic context budgeting. Required host facts are kept first; optional
|
|
108
|
+
* fragments compete by priority, then original order. No LLM call is involved.
|
|
109
|
+
*/
|
|
110
|
+
export function assembleContextBundle(
|
|
111
|
+
input: readonly ContextFragment[],
|
|
112
|
+
budgetTokens = DEFAULT_TRANSIENT_BUDGET_TOKENS,
|
|
113
|
+
): ContextBundle {
|
|
114
|
+
const fragments = input.filter((fragment) => fragment.content.length > 0);
|
|
115
|
+
const indexed = fragments.map((fragment, index) => ({ fragment, index }));
|
|
116
|
+
const ordered = indexed.sort((a, b) =>
|
|
117
|
+
Number(Boolean(b.fragment.required)) - Number(Boolean(a.fragment.required))
|
|
118
|
+
|| b.fragment.priority - a.fragment.priority
|
|
119
|
+
|| a.index - b.index);
|
|
120
|
+
const included: Array<{ fragment: ContextFragment; index: number }> = [];
|
|
121
|
+
const omittedIds: string[] = [];
|
|
122
|
+
const clippedIds: string[] = [];
|
|
123
|
+
let remaining = Math.max(0, budgetTokens);
|
|
124
|
+
|
|
125
|
+
for (const item of ordered) {
|
|
126
|
+
const fragment = item.fragment;
|
|
127
|
+
// 片段之间要用 SEPARATOR 拼接,那几个 token 也得记账,否则最终 text 会超出预算
|
|
128
|
+
// (usedTokens > budgetTokens 会让 loop.ts::transientFor 的余量判断失真)。
|
|
129
|
+
const separator = included.length > 0 ? SEPARATOR_TOKENS : 0;
|
|
130
|
+
const budgetForItem = remaining - separator;
|
|
131
|
+
const fullTokens = renderedTokens(fragment);
|
|
132
|
+
if (fullTokens <= budgetForItem) {
|
|
133
|
+
included.push(item);
|
|
134
|
+
remaining -= fullTokens + separator;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if ((fragment.required || fragment.clippable) && budgetForItem > 24) {
|
|
138
|
+
// 同口径:二分时就按「这段内容渲染进本片段信封后要花多少 token」来量。
|
|
139
|
+
const content = clipToRenderedBudget(
|
|
140
|
+
fragment.content,
|
|
141
|
+
budgetForItem,
|
|
142
|
+
(candidate) => renderedTokens({ ...fragment, content: candidate }),
|
|
143
|
+
);
|
|
144
|
+
const clipped = contextFragment({ ...fragment, content });
|
|
145
|
+
const clippedTokens = renderedTokens(clipped);
|
|
146
|
+
if (clipped.content && clippedTokens <= budgetForItem) {
|
|
147
|
+
included.push({ fragment: clipped, index: item.index });
|
|
148
|
+
if (clipped.content !== fragment.content) clippedIds.push(fragment.id);
|
|
149
|
+
remaining -= clippedTokens + separator;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
omittedIds.push(fragment.id);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Rendering follows semantic input order, not selection priority.
|
|
157
|
+
included.sort((a, b) => a.index - b.index);
|
|
158
|
+
const selected = included.map((item) => item.fragment);
|
|
159
|
+
const text = selected.map(renderContextFragment).join(FRAGMENT_SEPARATOR);
|
|
160
|
+
const usedTokens = countTextTokens(text);
|
|
161
|
+
return {
|
|
162
|
+
fragments: selected,
|
|
163
|
+
text,
|
|
164
|
+
diagnostics: {
|
|
165
|
+
budgetTokens,
|
|
166
|
+
usedTokens,
|
|
167
|
+
includedIds: selected.map((fragment) => fragment.id),
|
|
168
|
+
omittedIds,
|
|
169
|
+
clippedIds,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function resizeContextBundle(bundle: ContextBundle, budgetTokens: number): ContextBundle {
|
|
175
|
+
return assembleContextBundle(bundle.fragments, budgetTokens);
|
|
176
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { mkdir, readFile, rm, stat, utimes, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
|
|
4
|
+
const RETRY_MS = 200;
|
|
5
|
+
const STALE_MS = 2 * 60_000;
|
|
6
|
+
|
|
7
|
+
function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
// acquireContextLock 每 200ms 重试一次都会调用 sleep;listener 必须在正常
|
|
10
|
+
// 到点后摘掉,否则长等待会在同一个 AbortSignal 上线性堆积 listener。
|
|
11
|
+
const onAbort = () => {
|
|
12
|
+
clearTimeout(timer);
|
|
13
|
+
reject(new Error('aborted while waiting for workspace context lock'));
|
|
14
|
+
};
|
|
15
|
+
const timer = setTimeout(() => {
|
|
16
|
+
signal?.removeEventListener('abort', onAbort);
|
|
17
|
+
resolve();
|
|
18
|
+
}, ms);
|
|
19
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function pidAlive(pid: number): boolean {
|
|
24
|
+
try {
|
|
25
|
+
process.kill(pid, 0);
|
|
26
|
+
return true;
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// EPERM = 进程存在但不属于当前用户 → 算活(与 sessionRegistry/configFile 的
|
|
29
|
+
// processIsAlive 语义对齐);判死会在共享机器上误抢别的用户的活锁。
|
|
30
|
+
return (e as NodeJS.ErrnoException).code === 'EPERM';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function stale(lockDir: string): Promise<boolean> {
|
|
35
|
+
try {
|
|
36
|
+
const info = await stat(lockDir);
|
|
37
|
+
if (Date.now() - info.mtimeMs <= STALE_MS) return false;
|
|
38
|
+
const raw = JSON.parse(await readFile(`${lockDir}/owner.json`, 'utf8')) as { pid?: unknown };
|
|
39
|
+
return typeof raw.pid !== 'number' || !pidAlive(raw.pid);
|
|
40
|
+
} catch {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Cross-process exclusive lock for one canonical context file. A heartbeat keeps
|
|
47
|
+
* long agent turns from looking stale; dead processes are recovered by PID.
|
|
48
|
+
*/
|
|
49
|
+
export async function acquireContextLock(
|
|
50
|
+
sessionFile: string,
|
|
51
|
+
signal?: AbortSignal,
|
|
52
|
+
): Promise<() => Promise<void>> {
|
|
53
|
+
const lockDir = `${sessionFile}.lock`;
|
|
54
|
+
// 父目录(~/.minimal-agent/sessions/)通常早已存在(任何一个 cwd 聊过一轮就会由
|
|
55
|
+
// saveContext 递归建出来);只有全新 HOME 的第一次交互恰好落在这条锁路径上时才会
|
|
56
|
+
// 缺失。用普通 mkdir(lockDir)(不带 recursive)是刻意的——EEXIST 是这把互斥锁的
|
|
57
|
+
// 判活信号(下面 catch 靠它识别"别人正持锁"),recursive:true 会让已存在的锁目录
|
|
58
|
+
// 也悄悄"创建成功"、互斥失效。故只把父目录单独递归建好,叶子目录仍走非 recursive
|
|
59
|
+
// 的 mkdir 以保留 EEXIST 语义。
|
|
60
|
+
while (true) {
|
|
61
|
+
signal?.throwIfAborted();
|
|
62
|
+
try {
|
|
63
|
+
await mkdir(dirname(lockDir), { recursive: true });
|
|
64
|
+
await mkdir(lockDir);
|
|
65
|
+
await writeFile(`${lockDir}/owner.json`, JSON.stringify({ pid: process.pid, startedAt: Date.now() }), 'utf8');
|
|
66
|
+
const heartbeat = setInterval(() => {
|
|
67
|
+
const now = new Date();
|
|
68
|
+
void utimes(lockDir, now, now).catch(() => {});
|
|
69
|
+
}, 30_000);
|
|
70
|
+
return async () => {
|
|
71
|
+
clearInterval(heartbeat);
|
|
72
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
73
|
+
};
|
|
74
|
+
} catch (error) {
|
|
75
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
76
|
+
if (code !== 'EEXIST') throw error;
|
|
77
|
+
if (await stale(lockDir)) {
|
|
78
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
await sleep(RETRY_MS, signal);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/context/microCompactLite.ts —— 工具结果微压缩(v2)
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* 对齐 kakadeai 的 microCompact 设计,纯规则实现,零 LLM 调用。
|
|
6
|
+
*
|
|
7
|
+
* 核心思路:
|
|
8
|
+
* 工具执行结果在写入 history 前经过一道"单向检查门",
|
|
9
|
+
* 用规则压缩冗余内容,减少 token 消耗但不丢失关键信息。
|
|
10
|
+
*
|
|
11
|
+
* 对齐 kakadeai 的两条有效路径:
|
|
12
|
+
* Path A: Cached MC → 本方案用 SHA-1 进程缓存 + 计数淘汰实现
|
|
13
|
+
* Path B: Time-based MC → 本方案用轮次衰减实现
|
|
14
|
+
*
|
|
15
|
+
* kakadeai 已删除 Legacy LLM path → 本方案不实现 LLM 压缩
|
|
16
|
+
* ============================================================
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { createHash } from 'node:crypto';
|
|
20
|
+
|
|
21
|
+
// ==================== 配置常量 ====================
|
|
22
|
+
|
|
23
|
+
/** 同一内容最多原样保留多少次,之后用压缩引用替代 */
|
|
24
|
+
const MAX_REPEAT_COUNT = 3;
|
|
25
|
+
|
|
26
|
+
/** 触发截断的字符数阈值(仅白名单内的工具) */
|
|
27
|
+
const MAX_RESULT_SIZE = 4000;
|
|
28
|
+
|
|
29
|
+
/** 截断时保留的头部字符数 */
|
|
30
|
+
const HEAD_KEEP_CHARS = 2000;
|
|
31
|
+
|
|
32
|
+
/** 截断时保留的尾部字符数 */
|
|
33
|
+
const TAIL_KEEP_CHARS = 1000;
|
|
34
|
+
|
|
35
|
+
/** 时间衰减:条目首次出现后超过多少轮自动清理 */
|
|
36
|
+
const MAX_KEEP_ROUNDS = 10;
|
|
37
|
+
|
|
38
|
+
/** 短内容阈值:不超过此长度的内容直接放行(不做任何处理) */
|
|
39
|
+
const SHORT_CONTENT_THRESHOLD = 200;
|
|
40
|
+
|
|
41
|
+
// ==================== 类型定义 ====================
|
|
42
|
+
|
|
43
|
+
interface CacheEntry {
|
|
44
|
+
/** 出现次数 */
|
|
45
|
+
count: number;
|
|
46
|
+
/** 首次出现的工具名(用于生成压缩提示) */
|
|
47
|
+
firstToolName: string;
|
|
48
|
+
/** 首次出现的轮次号(用于时间衰减判断) */
|
|
49
|
+
firstSeenTurn: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ==================== Session 级状态 ====================
|
|
53
|
+
/**
|
|
54
|
+
* 微压缩的运行时状态。模块级 defaultState 用于单进程场景(TUI / print),
|
|
55
|
+
* 多并发场景(HTTP server per-request)调 createMicroCompactState() 拿独立一份。
|
|
56
|
+
*/
|
|
57
|
+
export interface MicroCompactState {
|
|
58
|
+
turn: number;
|
|
59
|
+
cache: Map<string, CacheEntry>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function createMicroCompactState(): MicroCompactState {
|
|
63
|
+
return { turn: 0, cache: new Map() };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const defaultState: MicroCompactState = createMicroCompactState();
|
|
67
|
+
|
|
68
|
+
export function incrementTurn(state: MicroCompactState = defaultState): void {
|
|
69
|
+
state.turn++;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function resetTurn(state: MicroCompactState = defaultState): void {
|
|
73
|
+
state.turn = 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** 缓存统计信息 */
|
|
77
|
+
export interface MicroCompactStats {
|
|
78
|
+
totalEntries: number;
|
|
79
|
+
repeatedCount: number;
|
|
80
|
+
compressedCount: number;
|
|
81
|
+
expiredCount: number;
|
|
82
|
+
truncatedCount: number;
|
|
83
|
+
totalCharsSaved: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** 获取当前缓存统计 */
|
|
87
|
+
export function getCacheStats(state: MicroCompactState = defaultState): MicroCompactStats {
|
|
88
|
+
let compressedCount = 0;
|
|
89
|
+
let expiredCount = 0;
|
|
90
|
+
let truncatedCount = 0;
|
|
91
|
+
let totalCharsSaved = 0;
|
|
92
|
+
let repeatedCount = 0;
|
|
93
|
+
|
|
94
|
+
for (const entry of state.cache.values()) {
|
|
95
|
+
if (entry.count > 1) repeatedCount++;
|
|
96
|
+
if (entry.count > MAX_REPEAT_COUNT) {
|
|
97
|
+
compressedCount++;
|
|
98
|
+
totalCharsSaved += (entry.count - MAX_REPEAT_COUNT) * 500;
|
|
99
|
+
}
|
|
100
|
+
if (state.turn - entry.firstSeenTurn > MAX_KEEP_ROUNDS) {
|
|
101
|
+
expiredCount++;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { totalEntries: state.cache.size, repeatedCount, compressedCount, expiredCount, truncatedCount, totalCharsSaved };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** 清空缓存(供 /new 命令或测试使用) */
|
|
109
|
+
export function clearCache(state: MicroCompactState = defaultState): void {
|
|
110
|
+
state.cache.clear();
|
|
111
|
+
state.turn = 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ==================== 工具白名单 ====================
|
|
115
|
+
/**
|
|
116
|
+
* 只有这些工具的返回值参与微压缩(长度截断 + SHA-1 去重)。
|
|
117
|
+
*
|
|
118
|
+
* 排除 Read/Edit/Write 的原因:输出本身就短,有独立的大小限制。
|
|
119
|
+
* 排除 WebSearch 的原因:搜索结果是"一次性知识注入",首次必须完整展示给模型;
|
|
120
|
+
* 后续重复时由 SHA-1 去重处理,但不做长度截断(避免丢失关键信息)。
|
|
121
|
+
*/
|
|
122
|
+
const COMPRESSIBLE_TOOLS = new Set([
|
|
123
|
+
'Grep',
|
|
124
|
+
'Bash',
|
|
125
|
+
'WebFetch',
|
|
126
|
+
'Glob',
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
// ==================== 核心函数 ====================
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 对一条工具执行结果进行微压缩处理。
|
|
133
|
+
*
|
|
134
|
+
* 这是整个模块唯一的对外接口。loop.ts 在把 tool_result 塞入 history 之前调用它。
|
|
135
|
+
*
|
|
136
|
+
* 处理流水线(按优先级):
|
|
137
|
+
* Rule 0: 错误/短内容 → 原样放行
|
|
138
|
+
* Rule 1: SHA-1 去重 → 相同内容第 4 次起压缩为引用标记
|
|
139
|
+
* Rule 2: 时间衰减 → 超过 N 轮未再出现的内容清理掉
|
|
140
|
+
* Rule 3: 长度截断 → 白名单工具超长内容保留头尾 + 省略标记
|
|
141
|
+
*
|
|
142
|
+
* @param toolName 工具名
|
|
143
|
+
* @param content 工具返回的原始内容
|
|
144
|
+
* @returns 处理后的内容(可能被压缩、截断、或原样返回)
|
|
145
|
+
*/
|
|
146
|
+
export function microCompact(
|
|
147
|
+
toolName: string,
|
|
148
|
+
content: string,
|
|
149
|
+
state: MicroCompactState = defaultState,
|
|
150
|
+
): string {
|
|
151
|
+
if (!content) return content;
|
|
152
|
+
|
|
153
|
+
// Rule 0: 错误信息原样保留
|
|
154
|
+
if (content.startsWith('Error:') || content.startsWith('错误')) {
|
|
155
|
+
return content;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Rule 0b: 短内容直接放行
|
|
159
|
+
if (content.length <= SHORT_CONTENT_THRESHOLD) {
|
|
160
|
+
return content;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Rule 1: SHA-1 去重
|
|
164
|
+
const hash = sha1(content);
|
|
165
|
+
const existing = state.cache.get(hash);
|
|
166
|
+
|
|
167
|
+
if (existing) {
|
|
168
|
+
existing.count++;
|
|
169
|
+
if (existing.count > MAX_REPEAT_COUNT) {
|
|
170
|
+
return `[↑ ${existing.firstToolName} 结果已重复出现 ${existing.count} 次(相同内容已省略)]`;
|
|
171
|
+
}
|
|
172
|
+
return content;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 首次看到此内容
|
|
176
|
+
state.cache.set(hash, { count: 1, firstToolName: toolName, firstSeenTurn: state.turn });
|
|
177
|
+
|
|
178
|
+
// Rule 2: 不需要——首次出现不存在时间衰减问题
|
|
179
|
+
|
|
180
|
+
// Rule 3: 长度截断(仅白名单工具)
|
|
181
|
+
if (content.length > MAX_RESULT_SIZE && COMPRESSIBLE_TOOLS.has(toolName)) {
|
|
182
|
+
return truncateContent(content);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return content;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 清理过期条目(每轮开始前调用)。
|
|
190
|
+
* 将超过 MAX_KEEP_ROUNDS 未被再次访问的条目标记为过期,
|
|
191
|
+
* 后续如果这些条目的 hash 再次命中,会作为"新内容"重新计数。
|
|
192
|
+
*/
|
|
193
|
+
export function expireOldEntries(state: MicroCompactState = defaultState): number {
|
|
194
|
+
let expired = 0;
|
|
195
|
+
for (const [hash, entry] of state.cache) {
|
|
196
|
+
if (state.turn - entry.firstSeenTurn > MAX_KEEP_ROUNDS) {
|
|
197
|
+
state.cache.delete(hash);
|
|
198
|
+
expired++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return expired;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ==================== 内部函数 ====================
|
|
205
|
+
|
|
206
|
+
function sha1(str: string): string {
|
|
207
|
+
return createHash('sha1').update(str).digest('hex');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function truncateContent(content: string): string {
|
|
211
|
+
const omitted = content.length - HEAD_KEEP_CHARS - TAIL_KEEP_CHARS;
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
content.slice(0, HEAD_KEEP_CHARS) +
|
|
215
|
+
`\n\n[... 省略了 ${omitted.toLocaleString()} 字符 ...]\n\n` +
|
|
216
|
+
content.slice(-TAIL_KEEP_CHARS)
|
|
217
|
+
);
|
|
218
|
+
}
|