@office-ai/aioncli-core 0.18.7 → 0.24.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/dist/docs/CONTRIBUTING.md +546 -0
- package/dist/docs/architecture.md +80 -0
- package/dist/docs/assets/connected_devtools.png +0 -0
- package/dist/docs/assets/gemini-screenshot.png +0 -0
- package/dist/docs/assets/release_patch.png +0 -0
- package/dist/docs/assets/theme-ansi-light.png +0 -0
- package/dist/docs/assets/theme-ansi.png +0 -0
- package/dist/docs/assets/theme-atom-one.png +0 -0
- package/dist/docs/assets/theme-ayu-light.png +0 -0
- package/dist/docs/assets/theme-ayu.png +0 -0
- package/dist/docs/assets/theme-custom.png +0 -0
- package/dist/docs/assets/theme-default-light.png +0 -0
- package/dist/docs/assets/theme-default.png +0 -0
- package/dist/docs/assets/theme-dracula.png +0 -0
- package/dist/docs/assets/theme-github-light.png +0 -0
- package/dist/docs/assets/theme-github.png +0 -0
- package/dist/docs/assets/theme-google-light.png +0 -0
- package/dist/docs/assets/theme-xcode-light.png +0 -0
- package/dist/docs/changelogs/index.md +592 -0
- package/dist/docs/changelogs/latest.md +225 -0
- package/dist/docs/changelogs/preview.md +129 -0
- package/dist/docs/changelogs/releases.md +896 -0
- package/dist/docs/cli/authentication.md +3 -0
- package/dist/docs/cli/checkpointing.md +94 -0
- package/dist/docs/cli/commands.md +354 -0
- package/dist/docs/cli/configuration.md +780 -0
- package/dist/docs/cli/custom-commands.md +315 -0
- package/dist/docs/cli/enterprise.md +565 -0
- package/dist/docs/cli/gemini-ignore.md +71 -0
- package/dist/docs/cli/gemini-md.md +108 -0
- package/dist/docs/cli/generation-settings.md +210 -0
- package/dist/docs/cli/headless.md +388 -0
- package/dist/docs/cli/index.md +63 -0
- package/dist/docs/cli/keyboard-shortcuts.md +143 -0
- package/dist/docs/cli/model-routing.md +37 -0
- package/dist/docs/cli/model.md +62 -0
- package/dist/docs/cli/sandbox.md +171 -0
- package/dist/docs/cli/session-management.md +158 -0
- package/dist/docs/cli/settings.md +114 -0
- package/dist/docs/cli/skills.md +156 -0
- package/dist/docs/cli/system-prompt.md +93 -0
- package/dist/docs/cli/telemetry.md +791 -0
- package/dist/docs/cli/themes.md +237 -0
- package/dist/docs/cli/token-caching.md +20 -0
- package/dist/docs/cli/trusted-folders.md +95 -0
- package/dist/docs/cli/tutorials/skills-getting-started.md +124 -0
- package/dist/docs/cli/tutorials.md +87 -0
- package/dist/docs/cli/uninstall.md +47 -0
- package/dist/docs/core/index.md +101 -0
- package/dist/docs/core/memport.md +244 -0
- package/dist/docs/core/policy-engine.md +267 -0
- package/dist/docs/core/tools-api.md +131 -0
- package/dist/docs/examples/proxy-script.md +83 -0
- package/dist/docs/extension.md +160 -0
- package/dist/docs/extensions/extension-releasing.md +183 -0
- package/dist/docs/extensions/getting-started-extensions.md +245 -0
- package/dist/docs/extensions/index.md +293 -0
- package/dist/docs/faq.md +154 -0
- package/dist/docs/get-started/authentication.md +321 -0
- package/dist/docs/get-started/configuration-v1.md +888 -0
- package/dist/docs/get-started/configuration.md +1536 -0
- package/dist/docs/get-started/deployment.md +143 -0
- package/dist/docs/get-started/examples.md +219 -0
- package/dist/docs/get-started/gemini-3.md +116 -0
- package/dist/docs/get-started/index.md +71 -0
- package/dist/docs/get-started/installation.md +141 -0
- package/dist/docs/hooks/best-practices.md +856 -0
- package/dist/docs/hooks/index.md +687 -0
- package/dist/docs/hooks/reference.md +168 -0
- package/dist/docs/hooks/writing-hooks.md +1026 -0
- package/dist/docs/ide-integration/ide-companion-spec.md +267 -0
- package/dist/docs/ide-integration/index.md +202 -0
- package/dist/docs/index.md +147 -0
- package/dist/docs/integration-tests.md +211 -0
- package/dist/docs/issue-and-pr-automation.md +134 -0
- package/dist/docs/local-development.md +128 -0
- package/dist/docs/mermaid/context.mmd +103 -0
- package/dist/docs/mermaid/render-path.mmd +64 -0
- package/dist/docs/npm.md +62 -0
- package/dist/docs/quota-and-pricing.md +158 -0
- package/dist/docs/release-confidence.md +164 -0
- package/dist/docs/releases.md +540 -0
- package/dist/docs/sidebar.json +301 -0
- package/dist/docs/tools/file-system.md +217 -0
- package/dist/docs/tools/index.md +95 -0
- package/dist/docs/tools/mcp-server.md +1044 -0
- package/dist/docs/tools/memory.md +54 -0
- package/dist/docs/tools/shell.md +260 -0
- package/dist/docs/tools/todos.md +57 -0
- package/dist/docs/tools/web-fetch.md +59 -0
- package/dist/docs/tools/web-search.md +42 -0
- package/dist/docs/tos-privacy.md +96 -0
- package/dist/docs/troubleshooting.md +158 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/src/agents/a2a-client-manager.d.ts +78 -0
- package/dist/src/agents/a2a-client-manager.js +295 -0
- package/dist/src/agents/a2a-client-manager.js.map +1 -0
- package/dist/src/agents/a2a-client-manager.test.d.ts +6 -0
- package/dist/src/agents/a2a-client-manager.test.js +237 -0
- package/dist/src/agents/a2a-client-manager.test.js.map +1 -0
- package/dist/src/agents/a2aUtils.d.ts +28 -0
- package/dist/src/agents/a2aUtils.js +111 -0
- package/dist/src/agents/a2aUtils.js.map +1 -0
- package/dist/src/agents/a2aUtils.test.d.ts +6 -0
- package/dist/src/agents/a2aUtils.test.js +147 -0
- package/dist/src/agents/a2aUtils.test.js.map +1 -0
- package/dist/src/agents/codebase-investigator.d.ts +2 -2
- package/dist/src/agents/codebase-investigator.js +5 -4
- package/dist/src/agents/codebase-investigator.js.map +1 -1
- package/dist/src/agents/delegate-to-agent-tool.d.ts +19 -0
- package/dist/src/agents/delegate-to-agent-tool.js +115 -0
- package/dist/src/agents/delegate-to-agent-tool.js.map +1 -0
- package/dist/src/agents/delegate-to-agent-tool.test.d.ts +6 -0
- package/dist/src/agents/delegate-to-agent-tool.test.js +165 -0
- package/dist/src/agents/delegate-to-agent-tool.test.js.map +1 -0
- package/dist/src/agents/introspection-agent.d.ts +23 -0
- package/dist/src/agents/introspection-agent.js +72 -0
- package/dist/src/agents/introspection-agent.js.map +1 -0
- package/dist/src/agents/introspection-agent.test.d.ts +6 -0
- package/dist/src/agents/introspection-agent.test.js +47 -0
- package/dist/src/agents/introspection-agent.test.js.map +1 -0
- package/dist/src/agents/local-executor.d.ts +108 -0
- package/dist/src/agents/local-executor.js +801 -0
- package/dist/src/agents/local-executor.js.map +1 -0
- package/dist/src/agents/local-executor.test.d.ts +6 -0
- package/dist/src/agents/local-executor.test.js +1380 -0
- package/dist/src/agents/local-executor.test.js.map +1 -0
- package/dist/src/agents/local-invocation.d.ts +45 -0
- package/dist/src/agents/local-invocation.js +101 -0
- package/dist/src/agents/local-invocation.js.map +1 -0
- package/dist/src/agents/local-invocation.test.d.ts +6 -0
- package/dist/src/agents/local-invocation.test.js +218 -0
- package/dist/src/agents/local-invocation.test.js.map +1 -0
- package/dist/src/agents/registry.d.ts +25 -1
- package/dist/src/agents/registry.js +149 -5
- package/dist/src/agents/registry.js.map +1 -1
- package/dist/src/agents/registry.test.js +247 -25
- package/dist/src/agents/registry.test.js.map +1 -1
- package/dist/src/agents/remote-invocation.d.ts +35 -0
- package/dist/src/agents/remote-invocation.js +129 -0
- package/dist/src/agents/remote-invocation.js.map +1 -0
- package/dist/src/agents/remote-invocation.test.d.ts +6 -0
- package/dist/src/agents/remote-invocation.test.js +201 -0
- package/dist/src/agents/remote-invocation.test.js.map +1 -0
- package/dist/src/agents/subagent-tool-wrapper.d.ts +2 -2
- package/dist/src/agents/subagent-tool-wrapper.js +11 -6
- package/dist/src/agents/subagent-tool-wrapper.js.map +1 -1
- package/dist/src/agents/subagent-tool-wrapper.test.js +25 -17
- package/dist/src/agents/subagent-tool-wrapper.test.js.map +1 -1
- package/dist/src/agents/toml-loader.d.ts +74 -0
- package/dist/src/agents/toml-loader.js +248 -0
- package/dist/src/agents/toml-loader.js.map +1 -0
- package/dist/src/agents/toml-loader.test.d.ts +6 -0
- package/dist/src/agents/toml-loader.test.js +309 -0
- package/dist/src/agents/toml-loader.test.js.map +1 -0
- package/dist/src/agents/types.d.ts +18 -4
- package/dist/src/availability/errorClassification.d.ts +7 -0
- package/dist/src/availability/errorClassification.js +20 -0
- package/dist/src/availability/errorClassification.js.map +1 -0
- package/dist/src/availability/modelAvailabilityService.d.ts +36 -0
- package/dist/src/availability/modelAvailabilityService.js +87 -0
- package/dist/src/availability/modelAvailabilityService.js.map +1 -0
- package/dist/src/availability/modelAvailabilityService.test.d.ts +6 -0
- package/dist/src/availability/modelAvailabilityService.test.js +140 -0
- package/dist/src/availability/modelAvailabilityService.test.js.map +1 -0
- package/dist/src/availability/modelPolicy.d.ts +49 -0
- package/dist/src/availability/modelPolicy.js +7 -0
- package/dist/src/availability/modelPolicy.js.map +1 -0
- package/dist/src/availability/policyCatalog.d.ts +23 -0
- package/dist/src/availability/policyCatalog.js +82 -0
- package/dist/src/availability/policyCatalog.js.map +1 -0
- package/dist/src/availability/policyCatalog.test.d.ts +6 -0
- package/dist/src/availability/policyCatalog.test.js +70 -0
- package/dist/src/availability/policyCatalog.test.js.map +1 -0
- package/dist/src/availability/policyHelpers.d.ts +52 -0
- package/dist/src/availability/policyHelpers.js +136 -0
- package/dist/src/availability/policyHelpers.js.map +1 -0
- package/dist/src/availability/policyHelpers.test.d.ts +6 -0
- package/dist/src/availability/policyHelpers.test.js +182 -0
- package/dist/src/availability/policyHelpers.test.js.map +1 -0
- package/dist/src/availability/testUtils.d.ts +10 -0
- package/dist/src/availability/testUtils.js +22 -0
- package/dist/src/availability/testUtils.js.map +1 -0
- package/dist/src/code_assist/experiments/client_metadata.js +3 -2
- package/dist/src/code_assist/experiments/client_metadata.js.map +1 -1
- package/dist/src/code_assist/experiments/client_metadata.test.js +7 -10
- package/dist/src/code_assist/experiments/client_metadata.test.js.map +1 -1
- package/dist/src/code_assist/experiments/experiments.js +2 -2
- package/dist/src/code_assist/experiments/experiments.js.map +1 -1
- package/dist/src/code_assist/oauth2.d.ts +2 -0
- package/dist/src/code_assist/oauth2.js +73 -17
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/code_assist/oauth2.test.js +195 -18
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/code_assist/server.d.ts +10 -1
- package/dist/src/code_assist/server.js +81 -15
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +221 -25
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/code_assist/setup.js +6 -4
- package/dist/src/code_assist/setup.js.map +1 -1
- package/dist/src/code_assist/setup.test.js +63 -0
- package/dist/src/code_assist/setup.test.js.map +1 -1
- package/dist/src/code_assist/telemetry.d.ts +14 -0
- package/dist/src/code_assist/telemetry.js +156 -0
- package/dist/src/code_assist/telemetry.js.map +1 -0
- package/dist/src/code_assist/telemetry.test.d.ts +6 -0
- package/dist/src/code_assist/telemetry.test.js +300 -0
- package/dist/src/code_assist/telemetry.test.js.map +1 -0
- package/dist/src/code_assist/types.d.ts +84 -1
- package/dist/src/code_assist/types.js +21 -0
- package/dist/src/code_assist/types.js.map +1 -1
- package/dist/src/commands/init.d.ts +7 -0
- package/dist/src/commands/init.js +53 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/init.test.d.ts +6 -0
- package/dist/src/commands/init.test.js +25 -0
- package/dist/src/commands/init.test.js.map +1 -0
- package/dist/src/commands/restore.d.ts +9 -0
- package/dist/src/commands/restore.js +46 -0
- package/dist/src/commands/restore.js.map +1 -0
- package/dist/src/commands/restore.test.d.ts +6 -0
- package/dist/src/commands/restore.test.js +137 -0
- package/dist/src/commands/restore.test.js.map +1 -0
- package/dist/src/commands/types.d.ts +41 -0
- package/dist/src/commands/types.js +7 -0
- package/dist/src/commands/types.js.map +1 -0
- package/dist/src/config/config.d.ts +120 -23
- package/dist/src/config/config.js +336 -116
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +373 -95
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/defaultModelConfigs.js +46 -0
- package/dist/src/config/defaultModelConfigs.js.map +1 -1
- package/dist/src/config/flashFallback.test.js +11 -35
- package/dist/src/config/flashFallback.test.js.map +1 -1
- package/dist/src/config/models.d.ts +29 -15
- package/dist/src/config/models.js +78 -28
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/config/models.test.js +91 -77
- package/dist/src/config/models.test.js.map +1 -1
- package/dist/src/config/storage.d.ts +4 -0
- package/dist/src/config/storage.js +12 -0
- package/dist/src/config/storage.js.map +1 -1
- package/dist/src/config/storage.test.js +16 -0
- package/dist/src/config/storage.test.js.map +1 -1
- package/dist/src/confirmation-bus/message-bus.d.ts +6 -0
- package/dist/src/confirmation-bus/message-bus.js +66 -3
- package/dist/src/confirmation-bus/message-bus.js.map +1 -1
- package/dist/src/confirmation-bus/types.d.ts +29 -2
- package/dist/src/confirmation-bus/types.js +3 -0
- package/dist/src/confirmation-bus/types.js.map +1 -1
- package/dist/src/core/baseLlmClient.d.ts +30 -2
- package/dist/src/core/baseLlmClient.js +107 -49
- package/dist/src/core/baseLlmClient.js.map +1 -1
- package/dist/src/core/baseLlmClient.test.js +271 -13
- package/dist/src/core/baseLlmClient.test.js.map +1 -1
- package/dist/src/core/client.d.ts +5 -1
- package/dist/src/core/client.js +241 -63
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +462 -72
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/clientHookTriggers.d.ts +36 -0
- package/dist/src/core/clientHookTriggers.js +76 -0
- package/dist/src/core/clientHookTriggers.js.map +1 -0
- package/dist/src/core/contentGenerator.js +17 -4
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +132 -3
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolHookTriggers.d.ts +55 -0
- package/dist/src/core/coreToolHookTriggers.js +304 -0
- package/dist/src/core/coreToolHookTriggers.js.map +1 -0
- package/dist/src/core/coreToolHookTriggers.test.d.ts +6 -0
- package/dist/src/core/coreToolHookTriggers.test.js +191 -0
- package/dist/src/core/coreToolHookTriggers.test.js.map +1 -0
- package/dist/src/core/coreToolScheduler.d.ts +6 -85
- package/dist/src/core/coreToolScheduler.js +91 -274
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +161 -346
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.js +132 -76
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +240 -257
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/geminiChatHookTriggers.d.ts +64 -0
- package/dist/src/core/geminiChatHookTriggers.js +136 -0
- package/dist/src/core/geminiChatHookTriggers.js.map +1 -0
- package/dist/src/core/geminiChat_network_retry.test.d.ts +6 -0
- package/dist/src/core/geminiChat_network_retry.test.js +196 -0
- package/dist/src/core/geminiChat_network_retry.test.js.map +1 -0
- package/dist/src/core/logger.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.js +23 -6
- package/dist/src/core/loggingContentGenerator.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +13 -8
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/prompts.js +82 -26
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/prompts.test.js +102 -3
- package/dist/src/core/prompts.test.js.map +1 -1
- package/dist/src/core/sessionHookTriggers.d.ts +29 -0
- package/dist/src/core/sessionHookTriggers.js +75 -0
- package/dist/src/core/sessionHookTriggers.js.map +1 -0
- package/dist/src/core/turn.d.ts +34 -21
- package/dist/src/core/turn.js +33 -13
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/core/turn.test.js +0 -5
- package/dist/src/core/turn.test.js.map +1 -1
- package/dist/src/fallback/handler.js +101 -93
- package/dist/src/fallback/handler.js.map +1 -1
- package/dist/src/fallback/handler.test.js +186 -173
- package/dist/src/fallback/handler.test.js.map +1 -1
- package/dist/src/fallback/types.d.ts +8 -0
- package/dist/src/generated/git-commit.d.ts +3 -3
- package/dist/src/generated/git-commit.js +3 -3
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/hooks/hookAggregator.js +7 -0
- package/dist/src/hooks/hookAggregator.js.map +1 -1
- package/dist/src/hooks/hookEventHandler.d.ts +113 -0
- package/dist/src/hooks/hookEventHandler.js +571 -0
- package/dist/src/hooks/hookEventHandler.js.map +1 -0
- package/dist/src/hooks/hookEventHandler.test.d.ts +6 -0
- package/dist/src/hooks/hookEventHandler.test.js +461 -0
- package/dist/src/hooks/hookEventHandler.test.js.map +1 -0
- package/dist/src/hooks/hookPlanner.d.ts +1 -5
- package/dist/src/hooks/hookPlanner.js +2 -7
- package/dist/src/hooks/hookPlanner.js.map +1 -1
- package/dist/src/hooks/hookPlanner.test.js +62 -2
- package/dist/src/hooks/hookPlanner.test.js.map +1 -1
- package/dist/src/hooks/hookRegistry.d.ts +6 -18
- package/dist/src/hooks/hookRegistry.js +49 -35
- package/dist/src/hooks/hookRegistry.js.map +1 -1
- package/dist/src/hooks/hookRegistry.test.js +167 -8
- package/dist/src/hooks/hookRegistry.test.js.map +1 -1
- package/dist/src/hooks/hookRunner.d.ts +5 -3
- package/dist/src/hooks/hookRunner.js +74 -18
- package/dist/src/hooks/hookRunner.js.map +1 -1
- package/dist/src/hooks/hookRunner.test.js +174 -36
- package/dist/src/hooks/hookRunner.test.js.map +1 -1
- package/dist/src/hooks/hookSystem.d.ts +40 -0
- package/dist/src/hooks/hookSystem.js +65 -0
- package/dist/src/hooks/hookSystem.js.map +1 -0
- package/dist/src/hooks/hookSystem.test.d.ts +6 -0
- package/dist/src/hooks/hookSystem.test.js +319 -0
- package/dist/src/hooks/hookSystem.test.js.map +1 -0
- package/dist/src/hooks/index.d.ts +17 -0
- package/dist/src/hooks/index.js +18 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/hooks/trustedHooks.d.ts +28 -0
- package/dist/src/hooks/trustedHooks.js +90 -0
- package/dist/src/hooks/trustedHooks.js.map +1 -0
- package/dist/src/hooks/trustedHooks.test.d.ts +6 -0
- package/dist/src/hooks/trustedHooks.test.js +154 -0
- package/dist/src/hooks/trustedHooks.test.js.map +1 -0
- package/dist/src/hooks/types.d.ts +21 -11
- package/dist/src/hooks/types.js +31 -27
- package/dist/src/hooks/types.js.map +1 -1
- package/dist/src/hooks/types.test.js +5 -24
- package/dist/src/hooks/types.test.js.map +1 -1
- package/dist/src/ide/detect-ide.test.js +32 -1
- package/dist/src/ide/detect-ide.test.js.map +1 -1
- package/dist/src/ide/ide-client.js +9 -4
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ide-client.test.js +17 -0
- package/dist/src/ide/ide-client.test.js.map +1 -1
- package/dist/src/ide/ide-installer.test.js +1 -1
- package/dist/src/ide/ide-installer.test.js.map +1 -1
- package/dist/src/ide/types.d.ts +4 -4
- package/dist/src/index.d.ts +17 -1
- package/dist/src/index.js +18 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/auth-provider.d.ts +16 -0
- package/dist/src/mcp/auth-provider.js +7 -0
- package/dist/src/mcp/auth-provider.js.map +1 -0
- package/dist/src/mcp/google-auth-provider.d.ts +10 -2
- package/dist/src/mcp/google-auth-provider.js +28 -0
- package/dist/src/mcp/google-auth-provider.js.map +1 -1
- package/dist/src/mcp/google-auth-provider.test.js +45 -0
- package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
- package/dist/src/mcp/oauth-provider.js +6 -2
- package/dist/src/mcp/oauth-provider.js.map +1 -1
- package/dist/src/mcp/oauth-provider.test.js +4 -1
- package/dist/src/mcp/oauth-provider.test.js.map +1 -1
- package/dist/src/mcp/oauth-utils.d.ts +8 -1
- package/dist/src/mcp/oauth-utils.js +31 -2
- package/dist/src/mcp/oauth-utils.js.map +1 -1
- package/dist/src/mcp/oauth-utils.test.js +42 -0
- package/dist/src/mcp/oauth-utils.test.js.map +1 -1
- package/dist/src/mcp/sa-impersonation-provider.d.ts +2 -2
- package/dist/src/mcp/sa-impersonation-provider.js.map +1 -1
- package/dist/src/mcp/token-storage/hybrid-token-storage.js +1 -1
- package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -1
- package/dist/src/mcp/token-storage/keychain-token-storage.js +1 -1
- package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -1
- package/dist/src/output/json-formatter.d.ts +2 -2
- package/dist/src/output/json-formatter.js +6 -3
- package/dist/src/output/json-formatter.js.map +1 -1
- package/dist/src/output/json-formatter.test.js +37 -9
- package/dist/src/output/json-formatter.test.js.map +1 -1
- package/dist/src/output/stream-json-formatter.js +6 -0
- package/dist/src/output/stream-json-formatter.js.map +1 -1
- package/dist/src/output/stream-json-formatter.test.js +98 -100
- package/dist/src/output/stream-json-formatter.test.js.map +1 -1
- package/dist/src/output/types.d.ts +3 -0
- package/dist/src/output/types.js.map +1 -1
- package/dist/src/policy/config.js +140 -15
- package/dist/src/policy/config.js.map +1 -1
- package/dist/src/policy/config.test.js +21 -0
- package/dist/src/policy/config.test.js.map +1 -1
- package/dist/src/policy/persistence.test.d.ts +6 -0
- package/dist/src/policy/persistence.test.js +154 -0
- package/dist/src/policy/persistence.test.js.map +1 -0
- package/dist/src/policy/policies/agent.toml +31 -0
- package/dist/src/policy/policies/read-only.toml +5 -0
- package/dist/src/policy/policies/write.toml +5 -0
- package/dist/src/policy/policies/yolo.toml +1 -0
- package/dist/src/policy/policy-engine.d.ts +30 -1
- package/dist/src/policy/policy-engine.js +192 -5
- package/dist/src/policy/policy-engine.js.map +1 -1
- package/dist/src/policy/policy-engine.test.js +520 -3
- package/dist/src/policy/policy-engine.test.js.map +1 -1
- package/dist/src/policy/policy-updater.test.d.ts +6 -0
- package/dist/src/policy/policy-updater.test.js +116 -0
- package/dist/src/policy/policy-updater.test.js.map +1 -0
- package/dist/src/policy/shell-safety.test.d.ts +6 -0
- package/dist/src/policy/shell-safety.test.js +75 -0
- package/dist/src/policy/shell-safety.test.js.map +1 -0
- package/dist/src/policy/toml-loader.d.ts +3 -5
- package/dist/src/policy/toml-loader.js +12 -60
- package/dist/src/policy/toml-loader.js.map +1 -1
- package/dist/src/policy/toml-loader.test.js +38 -7
- package/dist/src/policy/toml-loader.test.js.map +1 -1
- package/dist/src/policy/types.d.ts +72 -1
- package/dist/src/policy/types.js +21 -0
- package/dist/src/policy/types.js.map +1 -1
- package/dist/src/policy/utils.d.ts +21 -0
- package/dist/src/policy/utils.js +42 -0
- package/dist/src/policy/utils.js.map +1 -0
- package/dist/src/policy/utils.test.d.ts +6 -0
- package/dist/src/policy/utils.test.js +64 -0
- package/dist/src/policy/utils.test.js.map +1 -0
- package/dist/src/resources/resource-registry.d.ts +30 -0
- package/dist/src/resources/resource-registry.js +57 -0
- package/dist/src/resources/resource-registry.js.map +1 -0
- package/dist/src/resources/resource-registry.test.d.ts +6 -0
- package/dist/src/resources/resource-registry.test.js +54 -0
- package/dist/src/resources/resource-registry.test.js.map +1 -0
- package/dist/src/routing/modelRouterService.js +0 -15
- package/dist/src/routing/modelRouterService.js.map +1 -1
- package/dist/src/routing/modelRouterService.test.js +0 -62
- package/dist/src/routing/modelRouterService.test.js.map +1 -1
- package/dist/src/routing/strategies/classifierStrategy.js +10 -21
- package/dist/src/routing/strategies/classifierStrategy.js.map +1 -1
- package/dist/src/routing/strategies/classifierStrategy.test.js +2 -1
- package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -1
- package/dist/src/routing/strategies/compositeStrategy.js +4 -2
- package/dist/src/routing/strategies/compositeStrategy.js.map +1 -1
- package/dist/src/routing/strategies/compositeStrategy.test.js +11 -10
- package/dist/src/routing/strategies/compositeStrategy.test.js.map +1 -1
- package/dist/src/routing/strategies/fallbackStrategy.js +20 -12
- package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -1
- package/dist/src/routing/strategies/fallbackStrategy.test.js +63 -39
- package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -1
- package/dist/src/routing/strategies/overrideStrategy.js +3 -2
- package/dist/src/routing/strategies/overrideStrategy.js.map +1 -1
- package/dist/src/safety/checker-runner.js +17 -6
- package/dist/src/safety/checker-runner.js.map +1 -1
- package/dist/src/scheduler/tool-executor.d.ts +22 -0
- package/dist/src/scheduler/tool-executor.js +198 -0
- package/dist/src/scheduler/tool-executor.js.map +1 -0
- package/dist/src/scheduler/tool-executor.test.d.ts +6 -0
- package/dist/src/scheduler/tool-executor.test.js +231 -0
- package/dist/src/scheduler/tool-executor.test.js.map +1 -0
- package/dist/src/scheduler/types.d.ts +95 -0
- package/dist/src/scheduler/types.js +7 -0
- package/dist/src/scheduler/types.js.map +1 -0
- package/dist/src/services/chatCompressionService.d.ts +1 -0
- package/dist/src/services/chatCompressionService.js +38 -8
- package/dist/src/services/chatCompressionService.js.map +1 -1
- package/dist/src/services/chatCompressionService.test.js +35 -31
- package/dist/src/services/chatCompressionService.test.js.map +1 -1
- package/dist/src/services/chatRecordingService.d.ts +14 -0
- package/dist/src/services/chatRecordingService.js +37 -0
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/contextManager.d.ts +29 -0
- package/dist/src/services/contextManager.js +71 -0
- package/dist/src/services/contextManager.js.map +1 -0
- package/dist/src/services/contextManager.test.d.ts +6 -0
- package/dist/src/services/contextManager.test.js +104 -0
- package/dist/src/services/contextManager.test.js.map +1 -0
- package/dist/src/services/environmentSanitization.d.ts +15 -0
- package/dist/src/services/environmentSanitization.js +141 -0
- package/dist/src/services/environmentSanitization.js.map +1 -0
- package/dist/src/services/environmentSanitization.test.d.ts +6 -0
- package/dist/src/services/environmentSanitization.test.js +284 -0
- package/dist/src/services/environmentSanitization.test.js.map +1 -0
- package/dist/src/services/fileSystemService.d.ts +0 -9
- package/dist/src/services/fileSystemService.js +0 -11
- package/dist/src/services/fileSystemService.js.map +1 -1
- package/dist/src/services/gitService.js +18 -2
- package/dist/src/services/gitService.js.map +1 -1
- package/dist/src/services/gitService.test.js +56 -0
- package/dist/src/services/gitService.test.js.map +1 -1
- package/dist/src/services/loopDetectionService.js +5 -4
- package/dist/src/services/loopDetectionService.js.map +1 -1
- package/dist/src/services/loopDetectionService.test.js +14 -8
- package/dist/src/services/loopDetectionService.test.js.map +1 -1
- package/dist/src/services/modelConfig.golden.test.js +32 -0
- package/dist/src/services/modelConfig.golden.test.js.map +1 -1
- package/dist/src/services/modelConfig.integration.test.js +1 -1
- package/dist/src/services/modelConfig.integration.test.js.map +1 -1
- package/dist/src/services/modelConfigService.d.ts +4 -0
- package/dist/src/services/modelConfigService.js +8 -3
- package/dist/src/services/modelConfigService.js.map +1 -1
- package/dist/src/services/modelConfigService.test.js +221 -0
- package/dist/src/services/modelConfigService.test.js.map +1 -1
- package/dist/src/services/modelConfigServiceTestUtils.d.ts +10 -0
- package/dist/src/services/modelConfigServiceTestUtils.js +17 -0
- package/dist/src/services/modelConfigServiceTestUtils.js.map +1 -0
- package/dist/src/services/sessionSummaryService.d.ts +28 -0
- package/dist/src/services/sessionSummaryService.js +131 -0
- package/dist/src/services/sessionSummaryService.js.map +1 -0
- package/dist/src/services/sessionSummaryService.test.d.ts +6 -0
- package/dist/src/services/sessionSummaryService.test.js +785 -0
- package/dist/src/services/sessionSummaryService.test.js.map +1 -0
- package/dist/src/services/sessionSummaryUtils.d.ts +16 -0
- package/dist/src/services/sessionSummaryUtils.js +129 -0
- package/dist/src/services/sessionSummaryUtils.js.map +1 -0
- package/dist/src/services/sessionSummaryUtils.test.d.ts +6 -0
- package/dist/src/services/sessionSummaryUtils.test.js +137 -0
- package/dist/src/services/sessionSummaryUtils.test.js.map +1 -0
- package/dist/src/services/shellExecutionService.d.ts +4 -0
- package/dist/src/services/shellExecutionService.js +45 -27
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +240 -8
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/services/test-data/resolved-aliases-retry.golden.json +238 -0
- package/dist/src/services/test-data/resolved-aliases.golden.json +36 -0
- package/dist/src/skills/skillLoader.d.ts +28 -0
- package/dist/src/skills/skillLoader.js +77 -0
- package/dist/src/skills/skillLoader.js.map +1 -0
- package/dist/src/skills/skillLoader.test.d.ts +1 -0
- package/dist/src/skills/skillLoader.test.js +2 -0
- package/dist/src/skills/skillLoader.test.js.map +1 -0
- package/dist/src/skills/skillManager.d.ts +51 -0
- package/dist/src/skills/skillManager.js +89 -0
- package/dist/src/skills/skillManager.js.map +1 -0
- package/dist/src/skills/skillManager.test.d.ts +6 -0
- package/dist/src/skills/skillManager.test.js +128 -0
- package/dist/src/skills/skillManager.test.js.map +1 -0
- package/dist/src/telemetry/activity-detector.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +11 -7
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +127 -47
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +105 -18
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +9 -3
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +20 -5
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/config.js +2 -0
- package/dist/src/telemetry/config.js.map +1 -1
- package/dist/src/telemetry/config.test.js +25 -0
- package/dist/src/telemetry/config.test.js.map +1 -1
- package/dist/src/telemetry/gcp-exporters.d.ts +4 -3
- package/dist/src/telemetry/gcp-exporters.js +8 -4
- package/dist/src/telemetry/gcp-exporters.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +2 -1
- package/dist/src/telemetry/index.js +2 -1
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +5 -3
- package/dist/src/telemetry/loggers.js +353 -334
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.circular.js +1 -0
- package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +238 -31
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/metrics.d.ts +22 -0
- package/dist/src/telemetry/metrics.js +32 -0
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/metrics.test.js +64 -0
- package/dist/src/telemetry/metrics.test.js.map +1 -1
- package/dist/src/telemetry/sanitize.d.ts +25 -0
- package/dist/src/telemetry/sanitize.js +48 -0
- package/dist/src/telemetry/sanitize.js.map +1 -0
- package/dist/src/telemetry/sanitize.test.d.ts +6 -0
- package/dist/src/telemetry/sanitize.test.js +279 -0
- package/dist/src/telemetry/sanitize.test.js.map +1 -0
- package/dist/src/telemetry/sdk.d.ts +9 -2
- package/dist/src/telemetry/sdk.js +142 -17
- package/dist/src/telemetry/sdk.js.map +1 -1
- package/dist/src/telemetry/sdk.test.js +130 -28
- package/dist/src/telemetry/sdk.test.js.map +1 -1
- package/dist/src/telemetry/semantic.js +1 -1
- package/dist/src/telemetry/semantic.js.map +1 -1
- package/dist/src/telemetry/startupProfiler.d.ts +51 -0
- package/dist/src/telemetry/startupProfiler.js +170 -0
- package/dist/src/telemetry/startupProfiler.js.map +1 -0
- package/dist/src/telemetry/startupProfiler.test.d.ts +6 -0
- package/dist/src/telemetry/startupProfiler.test.js +285 -0
- package/dist/src/telemetry/startupProfiler.test.js.map +1 -0
- package/dist/src/telemetry/telemetry.test.js +10 -3
- package/dist/src/telemetry/telemetry.test.js.map +1 -1
- package/dist/src/telemetry/trace.js +2 -2
- package/dist/src/telemetry/trace.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +62 -16
- package/dist/src/telemetry/types.js +157 -27
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.d.ts +1 -0
- package/dist/src/telemetry/uiTelemetry.js +2 -0
- package/dist/src/telemetry/uiTelemetry.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +4 -0
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
- package/dist/src/test-utils/mock-message-bus.d.ts +61 -0
- package/dist/src/test-utils/mock-message-bus.js +160 -0
- package/dist/src/test-utils/mock-message-bus.js.map +1 -0
- package/dist/src/test-utils/mock-tool.d.ts +5 -3
- package/dist/src/test-utils/mock-tool.js +12 -11
- package/dist/src/test-utils/mock-tool.js.map +1 -1
- package/dist/src/tools/activate-skill.d.ts +27 -0
- package/dist/src/tools/activate-skill.js +120 -0
- package/dist/src/tools/activate-skill.js.map +1 -0
- package/dist/src/tools/activate-skill.test.d.ts +6 -0
- package/dist/src/tools/activate-skill.test.js +95 -0
- package/dist/src/tools/activate-skill.test.js.map +1 -0
- package/dist/src/tools/confirmation-policy.test.d.ts +6 -0
- package/dist/src/tools/confirmation-policy.test.js +142 -0
- package/dist/src/tools/confirmation-policy.test.js.map +1 -0
- package/dist/src/tools/edit.d.ts +27 -5
- package/dist/src/tools/edit.js +449 -137
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +258 -526
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/get-internal-docs.d.ts +27 -0
- package/dist/src/tools/get-internal-docs.js +129 -0
- package/dist/src/tools/get-internal-docs.js.map +1 -0
- package/dist/src/tools/get-internal-docs.test.d.ts +6 -0
- package/dist/src/tools/get-internal-docs.test.js +57 -0
- package/dist/src/tools/get-internal-docs.test.js.map +1 -0
- package/dist/src/tools/glob.d.ts +2 -2
- package/dist/src/tools/glob.js +1 -1
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/glob.test.js +2 -1
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/grep.d.ts +2 -2
- package/dist/src/tools/grep.js +1 -1
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/grep.test.js +5 -4
- package/dist/src/tools/grep.test.js.map +1 -1
- package/dist/src/tools/ls.d.ts +2 -2
- package/dist/src/tools/ls.js +2 -2
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/ls.test.js +2 -1
- package/dist/src/tools/ls.test.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.d.ts +3 -1
- package/dist/src/tools/mcp-client-manager.js +42 -9
- package/dist/src/tools/mcp-client-manager.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.test.js +66 -10
- package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +44 -6
- package/dist/src/tools/mcp-client.js +476 -176
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +633 -36
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/mcp-tool.d.ts +2 -2
- package/dist/src/tools/mcp-tool.js +20 -7
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/mcp-tool.test.js +35 -5
- package/dist/src/tools/mcp-tool.test.js.map +1 -1
- package/dist/src/tools/memoryTool.d.ts +3 -3
- package/dist/src/tools/memoryTool.js +3 -4
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/memoryTool.test.js +5 -2
- package/dist/src/tools/memoryTool.test.js.map +1 -1
- package/dist/src/tools/message-bus-integration.test.js +10 -37
- package/dist/src/tools/message-bus-integration.test.js.map +1 -1
- package/dist/src/tools/modifiable-tool.js.map +1 -1
- package/dist/src/tools/modifiable-tool.test.js +22 -13
- package/dist/src/tools/modifiable-tool.test.js.map +1 -1
- package/dist/src/tools/read-file.d.ts +2 -2
- package/dist/src/tools/read-file.js +2 -2
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-file.test.js +3 -2
- package/dist/src/tools/read-file.test.js.map +1 -1
- package/dist/src/tools/read-many-files.d.ts +2 -2
- package/dist/src/tools/read-many-files.js +7 -6
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/read-many-files.test.js +4 -3
- package/dist/src/tools/read-many-files.test.js.map +1 -1
- package/dist/src/tools/ripGrep.d.ts +3 -2
- package/dist/src/tools/ripGrep.js +18 -7
- package/dist/src/tools/ripGrep.js.map +1 -1
- package/dist/src/tools/ripGrep.test.js +60 -4
- package/dist/src/tools/ripGrep.test.js.map +1 -1
- package/dist/src/tools/shell.d.ts +5 -7
- package/dist/src/tools/shell.js +77 -51
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +59 -63
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/tool-error.d.ts +2 -1
- package/dist/src/tools/tool-error.js +2 -0
- package/dist/src/tools/tool-error.js.map +1 -1
- package/dist/src/tools/tool-names.d.ts +17 -0
- package/dist/src/tools/tool-names.js +59 -0
- package/dist/src/tools/tool-names.js.map +1 -1
- package/dist/src/tools/tool-names.test.d.ts +6 -0
- package/dist/src/tools/tool-names.test.js +43 -0
- package/dist/src/tools/tool-names.test.js.map +1 -0
- package/dist/src/tools/tool-registry.d.ts +11 -7
- package/dist/src/tools/tool-registry.js +15 -10
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +16 -11
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +25 -6
- package/dist/src/tools/tools.js +44 -25
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/tools.test.js +3 -1
- package/dist/src/tools/tools.test.js.map +1 -1
- package/dist/src/tools/web-fetch.d.ts +2 -2
- package/dist/src/tools/web-fetch.js +22 -9
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/web-fetch.test.js +18 -19
- package/dist/src/tools/web-fetch.test.js.map +1 -1
- package/dist/src/tools/web-search.d.ts +2 -2
- package/dist/src/tools/web-search.js +5 -5
- package/dist/src/tools/web-search.js.map +1 -1
- package/dist/src/tools/web-search.test.js +2 -1
- package/dist/src/tools/web-search.test.js.map +1 -1
- package/dist/src/tools/write-file.d.ts +2 -2
- package/dist/src/tools/write-file.js +10 -4
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js +4 -1
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/tools/write-todos.d.ts +2 -2
- package/dist/src/tools/write-todos.js +5 -4
- package/dist/src/tools/write-todos.js.map +1 -1
- package/dist/src/tools/write-todos.test.js +2 -1
- package/dist/src/tools/write-todos.test.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.d.ts +8 -0
- package/dist/src/utils/bfsFileSearch.js +63 -23
- package/dist/src/utils/bfsFileSearch.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.test.js +65 -1
- package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
- package/dist/src/utils/checkpointUtils.d.ts +82 -0
- package/dist/src/utils/checkpointUtils.js +117 -0
- package/dist/src/utils/checkpointUtils.js.map +1 -0
- package/dist/src/utils/checkpointUtils.test.d.ts +6 -0
- package/dist/src/utils/checkpointUtils.test.js +229 -0
- package/dist/src/utils/checkpointUtils.test.js.map +1 -0
- package/dist/src/utils/customHeaderUtils.d.ts +9 -0
- package/dist/src/utils/customHeaderUtils.js +34 -0
- package/dist/src/utils/customHeaderUtils.js.map +1 -0
- package/dist/src/utils/customHeaderUtils.test.d.ts +6 -0
- package/dist/src/utils/customHeaderUtils.test.js +77 -0
- package/dist/src/utils/customHeaderUtils.test.js.map +1 -0
- package/dist/src/utils/debugLogger.d.ts +3 -0
- package/dist/src/utils/debugLogger.js +28 -0
- package/dist/src/utils/debugLogger.js.map +1 -1
- package/dist/src/utils/editCorrector.js +6 -5
- package/dist/src/utils/editCorrector.js.map +1 -1
- package/dist/src/utils/editCorrector.test.js +7 -3
- package/dist/src/utils/editCorrector.test.js.map +1 -1
- package/dist/src/utils/editor.d.ts +9 -1
- package/dist/src/utils/editor.js +23 -14
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/environmentContext.d.ts +1 -0
- package/dist/src/utils/environmentContext.js +4 -0
- package/dist/src/utils/environmentContext.js.map +1 -1
- package/dist/src/utils/environmentContext.test.js +2 -0
- package/dist/src/utils/environmentContext.test.js.map +1 -1
- package/dist/src/utils/errorReporting.d.ts +1 -1
- package/dist/src/utils/errorReporting.js +13 -12
- package/dist/src/utils/errorReporting.js.map +1 -1
- package/dist/src/utils/errorReporting.test.js +17 -14
- package/dist/src/utils/errorReporting.test.js.map +1 -1
- package/dist/src/utils/errors.d.ts +8 -0
- package/dist/src/utils/errors.js +39 -2
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/errors.test.d.ts +6 -0
- package/dist/src/utils/errors.test.js +155 -0
- package/dist/src/utils/errors.test.js.map +1 -0
- package/dist/src/utils/events.d.ts +49 -19
- package/dist/src/utils/events.js +21 -9
- package/dist/src/utils/events.js.map +1 -1
- package/dist/src/utils/events.test.js +25 -0
- package/dist/src/utils/events.test.js.map +1 -1
- package/dist/src/utils/exitCodes.d.ts +12 -0
- package/dist/src/utils/exitCodes.js +13 -0
- package/dist/src/utils/exitCodes.js.map +1 -0
- package/dist/src/utils/extensionLoader.d.ts +2 -2
- package/dist/src/utils/extensionLoader.js +5 -6
- package/dist/src/utils/extensionLoader.js.map +1 -1
- package/dist/src/utils/extensionLoader.test.js +11 -0
- package/dist/src/utils/extensionLoader.test.js.map +1 -1
- package/dist/src/utils/fetch.d.ts +1 -1
- package/dist/src/utils/fetch.js +3 -3
- package/dist/src/utils/fetch.js.map +1 -1
- package/dist/src/utils/fileUtils.d.ts +4 -0
- package/dist/src/utils/fileUtils.js +53 -0
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +127 -1
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/filesearch/crawlCache.js.map +1 -1
- package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
- package/dist/src/utils/flashFallback.test.js +1 -1
- package/dist/src/utils/flashFallback.test.js.map +1 -1
- package/dist/src/utils/geminiIgnoreParser.d.ts +11 -0
- package/dist/src/utils/geminiIgnoreParser.js +20 -0
- package/dist/src/utils/geminiIgnoreParser.js.map +1 -1
- package/dist/src/utils/geminiIgnoreParser.test.js +48 -0
- package/dist/src/utils/geminiIgnoreParser.test.js.map +1 -1
- package/dist/src/utils/generateContentResponseUtilities.d.ts +3 -1
- package/dist/src/utils/generateContentResponseUtilities.js +106 -0
- package/dist/src/utils/generateContentResponseUtilities.js.map +1 -1
- package/dist/src/utils/generateContentResponseUtilities.test.js +279 -2
- package/dist/src/utils/generateContentResponseUtilities.test.js.map +1 -1
- package/dist/src/utils/getFolderStructure.js +7 -2
- package/dist/src/utils/getFolderStructure.js.map +1 -1
- package/dist/src/utils/googleErrors.js +31 -18
- package/dist/src/utils/googleErrors.js.map +1 -1
- package/dist/src/utils/googleErrors.test.js +10 -2
- package/dist/src/utils/googleErrors.test.js.map +1 -1
- package/dist/src/utils/googleQuotaErrors.d.ts +3 -3
- package/dist/src/utils/googleQuotaErrors.js +32 -6
- package/dist/src/utils/googleQuotaErrors.js.map +1 -1
- package/dist/src/utils/googleQuotaErrors.test.js +94 -2
- package/dist/src/utils/googleQuotaErrors.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.d.ts +5 -0
- package/dist/src/utils/memoryDiscovery.js +9 -5
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +31 -1
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.test.js +4 -0
- package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
- package/dist/src/utils/package.d.ts +14 -0
- package/dist/src/utils/package.js +15 -2
- package/dist/src/utils/package.js.map +1 -1
- package/dist/src/utils/pathCorrector.js +12 -2
- package/dist/src/utils/pathCorrector.js.map +1 -1
- package/dist/src/utils/pathCorrector.test.js +6 -2
- package/dist/src/utils/pathCorrector.test.js.map +1 -1
- package/dist/src/utils/retry.d.ts +11 -0
- package/dist/src/utils/retry.js +62 -21
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/retry.test.js +170 -10
- package/dist/src/utils/retry.test.js.map +1 -1
- package/dist/src/utils/schemaValidator.d.ts +1 -1
- package/dist/src/utils/schemaValidator.js +1 -1
- package/dist/src/utils/shell-permissions.d.ts +52 -0
- package/dist/src/utils/shell-permissions.js +188 -0
- package/dist/src/utils/shell-permissions.js.map +1 -0
- package/dist/src/utils/shell-permissions.test.d.ts +6 -0
- package/dist/src/utils/shell-permissions.test.js +369 -0
- package/dist/src/utils/shell-permissions.test.js.map +1 -0
- package/dist/src/utils/shell-utils.d.ts +16 -47
- package/dist/src/utils/shell-utils.js +99 -195
- package/dist/src/utils/shell-utils.js.map +1 -1
- package/dist/src/utils/shell-utils.test.js +99 -288
- package/dist/src/utils/shell-utils.test.js.map +1 -1
- package/dist/src/utils/stdio.d.ts +2 -2
- package/dist/src/utils/stdio.js +2 -2
- package/dist/src/utils/stdio.js.map +1 -1
- package/dist/src/utils/stdio.test.js +5 -5
- package/dist/src/utils/stdio.test.js.map +1 -1
- package/dist/src/utils/summarizer.test.js +3 -2
- package/dist/src/utils/summarizer.test.js.map +1 -1
- package/dist/src/utils/terminal.d.ts +4 -0
- package/dist/src/utils/terminal.js +12 -0
- package/dist/src/utils/terminal.js.map +1 -1
- package/dist/src/utils/terminalSerializer.test.js +17 -0
- package/dist/src/utils/terminalSerializer.test.js.map +1 -1
- package/dist/src/utils/tokenCalculation.d.ts +19 -0
- package/dist/src/utils/tokenCalculation.js +85 -0
- package/dist/src/utils/tokenCalculation.js.map +1 -0
- package/dist/src/utils/tokenCalculation.test.d.ts +6 -0
- package/dist/src/utils/tokenCalculation.test.js +87 -0
- package/dist/src/utils/tokenCalculation.test.js.map +1 -0
- package/dist/src/utils/tool-utils.d.ts +9 -0
- package/dist/src/utils/tool-utils.js +29 -0
- package/dist/src/utils/tool-utils.js.map +1 -1
- package/dist/src/utils/tool-utils.test.js +17 -2
- package/dist/src/utils/tool-utils.test.js.map +1 -1
- package/dist/src/utils/version.d.ts +6 -0
- package/dist/src/utils/version.js +15 -0
- package/dist/src/utils/version.js.map +1 -0
- package/dist/src/utils/version.test.d.ts +6 -0
- package/dist/src/utils/version.test.js +39 -0
- package/dist/src/utils/version.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
|
@@ -0,0 +1,1380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach, } from 'vitest';
|
|
7
|
+
import { debugLogger } from '../utils/debugLogger.js';
|
|
8
|
+
import { LocalAgentExecutor } from './local-executor.js';
|
|
9
|
+
import { makeFakeConfig } from '../test-utils/config.js';
|
|
10
|
+
import { ToolRegistry } from '../tools/tool-registry.js';
|
|
11
|
+
import { LSTool } from '../tools/ls.js';
|
|
12
|
+
import { LS_TOOL_NAME, READ_FILE_TOOL_NAME } from '../tools/tool-names.js';
|
|
13
|
+
import { GeminiChat, StreamEventType, } from '../core/geminiChat.js';
|
|
14
|
+
import {} from '@google/genai';
|
|
15
|
+
import { MockTool } from '../test-utils/mock-tool.js';
|
|
16
|
+
import { getDirectoryContextString } from '../utils/environmentContext.js';
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
import { promptIdContext } from '../utils/promptIdContext.js';
|
|
19
|
+
import { logAgentStart, logAgentFinish, logRecoveryAttempt, } from '../telemetry/loggers.js';
|
|
20
|
+
import { AgentStartEvent, AgentFinishEvent, RecoveryAttemptEvent, } from '../telemetry/types.js';
|
|
21
|
+
import { AgentTerminateMode } from './types.js';
|
|
22
|
+
import { CompressionStatus } from '../core/turn.js';
|
|
23
|
+
import { ChatCompressionService } from '../services/chatCompressionService.js';
|
|
24
|
+
import { getModelConfigAlias } from './registry.js';
|
|
25
|
+
const { mockSendMessageStream, mockExecuteToolCall, mockSetSystemInstruction, mockCompress, } = vi.hoisted(() => ({
|
|
26
|
+
mockSendMessageStream: vi.fn(),
|
|
27
|
+
mockExecuteToolCall: vi.fn(),
|
|
28
|
+
mockSetSystemInstruction: vi.fn(),
|
|
29
|
+
mockCompress: vi.fn(),
|
|
30
|
+
}));
|
|
31
|
+
let mockChatHistory = [];
|
|
32
|
+
const mockSetHistory = vi.fn((newHistory) => {
|
|
33
|
+
mockChatHistory = newHistory;
|
|
34
|
+
});
|
|
35
|
+
vi.mock('../services/chatCompressionService.js', () => ({
|
|
36
|
+
ChatCompressionService: vi.fn().mockImplementation(() => ({
|
|
37
|
+
compress: mockCompress,
|
|
38
|
+
})),
|
|
39
|
+
}));
|
|
40
|
+
vi.mock('../core/geminiChat.js', async (importOriginal) => {
|
|
41
|
+
const actual = await importOriginal();
|
|
42
|
+
return {
|
|
43
|
+
...actual,
|
|
44
|
+
GeminiChat: vi.fn().mockImplementation(() => ({
|
|
45
|
+
sendMessageStream: mockSendMessageStream,
|
|
46
|
+
getHistory: vi.fn((_curated) => [...mockChatHistory]),
|
|
47
|
+
setHistory: mockSetHistory,
|
|
48
|
+
setSystemInstruction: mockSetSystemInstruction,
|
|
49
|
+
})),
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
vi.mock('../core/nonInteractiveToolExecutor.js', () => ({
|
|
53
|
+
executeToolCall: mockExecuteToolCall,
|
|
54
|
+
}));
|
|
55
|
+
vi.mock('../utils/version.js', () => ({
|
|
56
|
+
getVersion: vi.fn().mockResolvedValue('1.2.3'),
|
|
57
|
+
}));
|
|
58
|
+
vi.mock('../utils/environmentContext.js');
|
|
59
|
+
vi.mock('../telemetry/loggers.js', () => ({
|
|
60
|
+
logAgentStart: vi.fn(),
|
|
61
|
+
logAgentFinish: vi.fn(),
|
|
62
|
+
logRecoveryAttempt: vi.fn(),
|
|
63
|
+
}));
|
|
64
|
+
vi.mock('../utils/promptIdContext.js', async (importOriginal) => {
|
|
65
|
+
const actual = await importOriginal();
|
|
66
|
+
return {
|
|
67
|
+
...actual,
|
|
68
|
+
promptIdContext: {
|
|
69
|
+
...actual.promptIdContext,
|
|
70
|
+
getStore: vi.fn(),
|
|
71
|
+
run: vi.fn((_id, fn) => fn()),
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
const MockedGeminiChat = vi.mocked(GeminiChat);
|
|
76
|
+
const mockedGetDirectoryContextString = vi.mocked(getDirectoryContextString);
|
|
77
|
+
const mockedPromptIdContext = vi.mocked(promptIdContext);
|
|
78
|
+
const mockedLogAgentStart = vi.mocked(logAgentStart);
|
|
79
|
+
const mockedLogAgentFinish = vi.mocked(logAgentFinish);
|
|
80
|
+
const mockedLogRecoveryAttempt = vi.mocked(logRecoveryAttempt);
|
|
81
|
+
// Constants for testing
|
|
82
|
+
const TASK_COMPLETE_TOOL_NAME = 'complete_task';
|
|
83
|
+
const MOCK_TOOL_NOT_ALLOWED = new MockTool({ name: 'write_file_interactive' });
|
|
84
|
+
/**
|
|
85
|
+
* Helper to create a mock API response chunk.
|
|
86
|
+
* Uses conditional spread to handle readonly functionCalls property safely.
|
|
87
|
+
*/
|
|
88
|
+
const createMockResponseChunk = (parts, functionCalls) => ({
|
|
89
|
+
candidates: [{ index: 0, content: { role: 'model', parts } }],
|
|
90
|
+
...(functionCalls && functionCalls.length > 0 ? { functionCalls } : {}),
|
|
91
|
+
});
|
|
92
|
+
/**
|
|
93
|
+
* Helper to mock a single turn of model response in the stream.
|
|
94
|
+
*/
|
|
95
|
+
const mockModelResponse = (functionCalls, thought, text) => {
|
|
96
|
+
const parts = [];
|
|
97
|
+
if (thought) {
|
|
98
|
+
parts.push({
|
|
99
|
+
text: `**${thought}** This is the reasoning part.`,
|
|
100
|
+
thought: true,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (text)
|
|
104
|
+
parts.push({ text });
|
|
105
|
+
const responseChunk = createMockResponseChunk(parts, functionCalls);
|
|
106
|
+
mockSendMessageStream.mockImplementationOnce(async () => (async function* () {
|
|
107
|
+
yield {
|
|
108
|
+
type: StreamEventType.CHUNK,
|
|
109
|
+
value: responseChunk,
|
|
110
|
+
};
|
|
111
|
+
})());
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Helper to extract the message parameters sent to sendMessageStream.
|
|
115
|
+
* Provides type safety for inspecting mock calls.
|
|
116
|
+
*/
|
|
117
|
+
const getMockMessageParams = (callIndex) => {
|
|
118
|
+
const call = mockSendMessageStream.mock.calls[callIndex];
|
|
119
|
+
expect(call).toBeDefined();
|
|
120
|
+
return {
|
|
121
|
+
modelConfigKey: call[0],
|
|
122
|
+
message: call[1],
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
let mockConfig;
|
|
126
|
+
let parentToolRegistry;
|
|
127
|
+
/**
|
|
128
|
+
* Type-safe helper to create agent definitions for tests.
|
|
129
|
+
*/
|
|
130
|
+
const createTestDefinition = (tools = [LS_TOOL_NAME], runConfigOverrides = {}, outputConfigMode = 'default', schema = z.string()) => {
|
|
131
|
+
let outputConfig;
|
|
132
|
+
if (outputConfigMode === 'default') {
|
|
133
|
+
outputConfig = {
|
|
134
|
+
outputName: 'finalResult',
|
|
135
|
+
description: 'The final result.',
|
|
136
|
+
schema,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
kind: 'local',
|
|
141
|
+
name: 'TestAgent',
|
|
142
|
+
description: 'An agent for testing.',
|
|
143
|
+
inputConfig: {
|
|
144
|
+
inputs: { goal: { type: 'string', required: true, description: 'goal' } },
|
|
145
|
+
},
|
|
146
|
+
modelConfig: { model: 'gemini-test-model', temp: 0, top_p: 1 },
|
|
147
|
+
runConfig: { max_time_minutes: 5, max_turns: 5, ...runConfigOverrides },
|
|
148
|
+
promptConfig: { systemPrompt: 'Achieve the goal: ${goal}.' },
|
|
149
|
+
toolConfig: { tools },
|
|
150
|
+
outputConfig,
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
describe('LocalAgentExecutor', () => {
|
|
154
|
+
let activities;
|
|
155
|
+
let onActivity;
|
|
156
|
+
let abortController;
|
|
157
|
+
let signal;
|
|
158
|
+
beforeEach(async () => {
|
|
159
|
+
vi.resetAllMocks();
|
|
160
|
+
mockCompress.mockClear();
|
|
161
|
+
mockSetHistory.mockClear();
|
|
162
|
+
mockSendMessageStream.mockReset();
|
|
163
|
+
mockSetSystemInstruction.mockReset();
|
|
164
|
+
mockExecuteToolCall.mockReset();
|
|
165
|
+
mockedLogAgentStart.mockReset();
|
|
166
|
+
mockedLogAgentFinish.mockReset();
|
|
167
|
+
mockedPromptIdContext.getStore.mockReset();
|
|
168
|
+
mockedPromptIdContext.run.mockImplementation((_id, fn) => fn());
|
|
169
|
+
ChatCompressionService.mockImplementation(() => ({
|
|
170
|
+
compress: mockCompress,
|
|
171
|
+
}));
|
|
172
|
+
mockCompress.mockResolvedValue({
|
|
173
|
+
newHistory: null,
|
|
174
|
+
info: { compressionStatus: CompressionStatus.NOOP },
|
|
175
|
+
});
|
|
176
|
+
MockedGeminiChat.mockImplementation(() => ({
|
|
177
|
+
sendMessageStream: mockSendMessageStream,
|
|
178
|
+
setSystemInstruction: mockSetSystemInstruction,
|
|
179
|
+
getHistory: vi.fn((_curated) => [...mockChatHistory]),
|
|
180
|
+
getLastPromptTokenCount: vi.fn(() => 100),
|
|
181
|
+
setHistory: mockSetHistory,
|
|
182
|
+
}));
|
|
183
|
+
vi.useFakeTimers();
|
|
184
|
+
mockConfig = makeFakeConfig();
|
|
185
|
+
parentToolRegistry = new ToolRegistry(mockConfig, mockConfig.getMessageBus());
|
|
186
|
+
parentToolRegistry.registerTool(new LSTool(mockConfig, mockConfig.getMessageBus()));
|
|
187
|
+
parentToolRegistry.registerTool(new MockTool({ name: READ_FILE_TOOL_NAME }));
|
|
188
|
+
parentToolRegistry.registerTool(MOCK_TOOL_NOT_ALLOWED);
|
|
189
|
+
vi.spyOn(mockConfig, 'getToolRegistry').mockReturnValue(parentToolRegistry);
|
|
190
|
+
mockedGetDirectoryContextString.mockResolvedValue('Mocked Environment Context');
|
|
191
|
+
activities = [];
|
|
192
|
+
onActivity = (activity) => activities.push(activity);
|
|
193
|
+
abortController = new AbortController();
|
|
194
|
+
signal = abortController.signal;
|
|
195
|
+
});
|
|
196
|
+
afterEach(() => {
|
|
197
|
+
vi.useRealTimers();
|
|
198
|
+
});
|
|
199
|
+
describe('create (Initialization and Validation)', () => {
|
|
200
|
+
it('should create successfully with allowed tools', async () => {
|
|
201
|
+
const definition = createTestDefinition([LS_TOOL_NAME]);
|
|
202
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
203
|
+
expect(executor).toBeInstanceOf(LocalAgentExecutor);
|
|
204
|
+
});
|
|
205
|
+
it('should allow any tool for experimentation (formerly SECURITY check)', async () => {
|
|
206
|
+
const definition = createTestDefinition([MOCK_TOOL_NOT_ALLOWED.name]);
|
|
207
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
208
|
+
expect(executor).toBeInstanceOf(LocalAgentExecutor);
|
|
209
|
+
});
|
|
210
|
+
it('should create an isolated ToolRegistry for the agent', async () => {
|
|
211
|
+
const definition = createTestDefinition([
|
|
212
|
+
LS_TOOL_NAME,
|
|
213
|
+
READ_FILE_TOOL_NAME,
|
|
214
|
+
]);
|
|
215
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
216
|
+
const agentRegistry = executor['toolRegistry'];
|
|
217
|
+
expect(agentRegistry).not.toBe(parentToolRegistry);
|
|
218
|
+
expect(agentRegistry.getAllToolNames()).toEqual(expect.arrayContaining([LS_TOOL_NAME, READ_FILE_TOOL_NAME]));
|
|
219
|
+
expect(agentRegistry.getAllToolNames()).toHaveLength(2);
|
|
220
|
+
expect(agentRegistry.getTool(MOCK_TOOL_NOT_ALLOWED.name)).toBeUndefined();
|
|
221
|
+
});
|
|
222
|
+
it('should use parentPromptId from context to create agentId', async () => {
|
|
223
|
+
const parentId = 'parent-id';
|
|
224
|
+
mockedPromptIdContext.getStore.mockReturnValue(parentId);
|
|
225
|
+
const definition = createTestDefinition();
|
|
226
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
227
|
+
expect(executor['agentId']).toMatch(new RegExp(`^${parentId}-${definition.name}-`));
|
|
228
|
+
});
|
|
229
|
+
it('should correctly apply templates to initialMessages', async () => {
|
|
230
|
+
const definition = createTestDefinition();
|
|
231
|
+
// Override promptConfig to use initialMessages instead of systemPrompt
|
|
232
|
+
definition.promptConfig = {
|
|
233
|
+
initialMessages: [
|
|
234
|
+
{ role: 'user', parts: [{ text: 'Goal: ${goal}' }] },
|
|
235
|
+
{ role: 'model', parts: [{ text: 'OK, starting on ${goal}.' }] },
|
|
236
|
+
],
|
|
237
|
+
};
|
|
238
|
+
const inputs = { goal: 'TestGoal' };
|
|
239
|
+
// Mock a response to prevent the loop from running forever
|
|
240
|
+
mockModelResponse([
|
|
241
|
+
{
|
|
242
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
243
|
+
args: { finalResult: 'done' },
|
|
244
|
+
id: 'call1',
|
|
245
|
+
},
|
|
246
|
+
]);
|
|
247
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
248
|
+
await executor.run(inputs, signal);
|
|
249
|
+
const chatConstructorArgs = MockedGeminiChat.mock.calls[0];
|
|
250
|
+
const startHistory = chatConstructorArgs[3]; // history is the 4th arg
|
|
251
|
+
expect(startHistory).toBeDefined();
|
|
252
|
+
expect(startHistory).toHaveLength(2);
|
|
253
|
+
// Perform checks on defined objects to satisfy TS
|
|
254
|
+
const firstPart = startHistory?.[0]?.parts?.[0];
|
|
255
|
+
expect(firstPart?.text).toBe('Goal: TestGoal');
|
|
256
|
+
const secondPart = startHistory?.[1]?.parts?.[0];
|
|
257
|
+
expect(secondPart?.text).toBe('OK, starting on TestGoal.');
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
describe('run (Execution Loop and Logic)', () => {
|
|
261
|
+
it('should log AgentFinish with error if run throws', async () => {
|
|
262
|
+
const definition = createTestDefinition();
|
|
263
|
+
// Make the definition invalid to cause an error during run
|
|
264
|
+
definition.inputConfig.inputs = {
|
|
265
|
+
goal: { type: 'string', required: true, description: 'goal' },
|
|
266
|
+
};
|
|
267
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
268
|
+
// Run without inputs to trigger validation error
|
|
269
|
+
await expect(executor.run({}, signal)).rejects.toThrow(/Missing required input parameters/);
|
|
270
|
+
expect(mockedLogAgentStart).toHaveBeenCalledTimes(1);
|
|
271
|
+
expect(mockedLogAgentFinish).toHaveBeenCalledTimes(1);
|
|
272
|
+
expect(mockedLogAgentFinish).toHaveBeenCalledWith(mockConfig, expect.objectContaining({
|
|
273
|
+
terminate_reason: AgentTerminateMode.ERROR,
|
|
274
|
+
}));
|
|
275
|
+
});
|
|
276
|
+
it('should execute successfully when model calls complete_task with output (Happy Path with Output)', async () => {
|
|
277
|
+
const definition = createTestDefinition();
|
|
278
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
279
|
+
const inputs = { goal: 'Find files' };
|
|
280
|
+
// Turn 1: Model calls ls
|
|
281
|
+
mockModelResponse([{ name: LS_TOOL_NAME, args: { path: '.' }, id: 'call1' }], 'T1: Listing');
|
|
282
|
+
mockExecuteToolCall.mockResolvedValueOnce({
|
|
283
|
+
status: 'success',
|
|
284
|
+
request: {
|
|
285
|
+
callId: 'call1',
|
|
286
|
+
name: LS_TOOL_NAME,
|
|
287
|
+
args: { path: '.' },
|
|
288
|
+
isClientInitiated: false,
|
|
289
|
+
prompt_id: 'test-prompt',
|
|
290
|
+
},
|
|
291
|
+
tool: {},
|
|
292
|
+
invocation: {},
|
|
293
|
+
response: {
|
|
294
|
+
callId: 'call1',
|
|
295
|
+
resultDisplay: 'file1.txt',
|
|
296
|
+
responseParts: [
|
|
297
|
+
{
|
|
298
|
+
functionResponse: {
|
|
299
|
+
name: LS_TOOL_NAME,
|
|
300
|
+
response: { result: 'file1.txt' },
|
|
301
|
+
id: 'call1',
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
error: undefined,
|
|
306
|
+
errorType: undefined,
|
|
307
|
+
contentLength: undefined,
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
// Turn 2: Model calls complete_task with required output
|
|
311
|
+
mockModelResponse([
|
|
312
|
+
{
|
|
313
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
314
|
+
args: { finalResult: 'Found file1.txt' },
|
|
315
|
+
id: 'call2',
|
|
316
|
+
},
|
|
317
|
+
], 'T2: Done');
|
|
318
|
+
const output = await executor.run(inputs, signal);
|
|
319
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(2);
|
|
320
|
+
const systemInstruction = MockedGeminiChat.mock.calls[0][1];
|
|
321
|
+
expect(systemInstruction).toContain(`MUST call the \`${TASK_COMPLETE_TOOL_NAME}\` tool`);
|
|
322
|
+
expect(systemInstruction).toContain('Mocked Environment Context');
|
|
323
|
+
expect(systemInstruction).toContain('You are running in a non-interactive mode');
|
|
324
|
+
expect(systemInstruction).toContain('Always use absolute paths');
|
|
325
|
+
const { modelConfigKey } = getMockMessageParams(0);
|
|
326
|
+
expect(modelConfigKey.model).toBe(getModelConfigAlias(definition));
|
|
327
|
+
const chatConstructorArgs = MockedGeminiChat.mock.calls[0];
|
|
328
|
+
// tools are the 3rd argument (index 2), passed as [{ functionDeclarations: [...] }]
|
|
329
|
+
const passedToolsArg = chatConstructorArgs[2];
|
|
330
|
+
const sentTools = passedToolsArg[0].functionDeclarations;
|
|
331
|
+
expect(sentTools).toBeDefined();
|
|
332
|
+
expect(sentTools).toEqual(expect.arrayContaining([
|
|
333
|
+
expect.objectContaining({ name: LS_TOOL_NAME }),
|
|
334
|
+
expect.objectContaining({ name: TASK_COMPLETE_TOOL_NAME }),
|
|
335
|
+
]));
|
|
336
|
+
const completeToolDef = sentTools.find((t) => t.name === TASK_COMPLETE_TOOL_NAME);
|
|
337
|
+
expect(completeToolDef?.parameters?.required).toContain('finalResult');
|
|
338
|
+
expect(output.result).toBe('Found file1.txt');
|
|
339
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
340
|
+
// Telemetry checks
|
|
341
|
+
expect(mockedLogAgentStart).toHaveBeenCalledTimes(1);
|
|
342
|
+
expect(mockedLogAgentStart).toHaveBeenCalledWith(mockConfig, expect.any(AgentStartEvent));
|
|
343
|
+
expect(mockedLogAgentFinish).toHaveBeenCalledTimes(1);
|
|
344
|
+
expect(mockedLogAgentFinish).toHaveBeenCalledWith(mockConfig, expect.any(AgentFinishEvent));
|
|
345
|
+
const finishEvent = mockedLogAgentFinish.mock.calls[0][1];
|
|
346
|
+
expect(finishEvent.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
347
|
+
// Context checks
|
|
348
|
+
expect(mockedPromptIdContext.run).toHaveBeenCalledTimes(2); // Two turns
|
|
349
|
+
const agentId = executor['agentId'];
|
|
350
|
+
expect(mockedPromptIdContext.run).toHaveBeenNthCalledWith(1, `${agentId}#0`, expect.any(Function));
|
|
351
|
+
expect(mockedPromptIdContext.run).toHaveBeenNthCalledWith(2, `${agentId}#1`, expect.any(Function));
|
|
352
|
+
expect(activities).toEqual(expect.arrayContaining([
|
|
353
|
+
expect.objectContaining({
|
|
354
|
+
type: 'THOUGHT_CHUNK',
|
|
355
|
+
data: { text: 'T1: Listing' },
|
|
356
|
+
}),
|
|
357
|
+
expect.objectContaining({
|
|
358
|
+
type: 'TOOL_CALL_END',
|
|
359
|
+
data: { name: LS_TOOL_NAME, output: 'file1.txt' },
|
|
360
|
+
}),
|
|
361
|
+
expect.objectContaining({
|
|
362
|
+
type: 'TOOL_CALL_START',
|
|
363
|
+
data: {
|
|
364
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
365
|
+
args: { finalResult: 'Found file1.txt' },
|
|
366
|
+
},
|
|
367
|
+
}),
|
|
368
|
+
expect.objectContaining({
|
|
369
|
+
type: 'TOOL_CALL_END',
|
|
370
|
+
data: {
|
|
371
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
372
|
+
output: expect.stringContaining('Output submitted'),
|
|
373
|
+
},
|
|
374
|
+
}),
|
|
375
|
+
]));
|
|
376
|
+
});
|
|
377
|
+
it('should execute successfully when model calls complete_task without output (Happy Path No Output)', async () => {
|
|
378
|
+
const definition = createTestDefinition([LS_TOOL_NAME], {}, 'none');
|
|
379
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
380
|
+
mockModelResponse([
|
|
381
|
+
{ name: LS_TOOL_NAME, args: { path: '.' }, id: 'call1' },
|
|
382
|
+
]);
|
|
383
|
+
mockExecuteToolCall.mockResolvedValueOnce({
|
|
384
|
+
status: 'success',
|
|
385
|
+
request: {
|
|
386
|
+
callId: 'call1',
|
|
387
|
+
name: LS_TOOL_NAME,
|
|
388
|
+
args: { path: '.' },
|
|
389
|
+
isClientInitiated: false,
|
|
390
|
+
prompt_id: 'test-prompt',
|
|
391
|
+
},
|
|
392
|
+
tool: {},
|
|
393
|
+
invocation: {},
|
|
394
|
+
response: {
|
|
395
|
+
callId: 'call1',
|
|
396
|
+
resultDisplay: 'ok',
|
|
397
|
+
responseParts: [
|
|
398
|
+
{
|
|
399
|
+
functionResponse: {
|
|
400
|
+
name: LS_TOOL_NAME,
|
|
401
|
+
response: {},
|
|
402
|
+
id: 'call1',
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
error: undefined,
|
|
407
|
+
errorType: undefined,
|
|
408
|
+
contentLength: undefined,
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
mockModelResponse([
|
|
412
|
+
{
|
|
413
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
414
|
+
args: { result: 'All work done' },
|
|
415
|
+
id: 'call2',
|
|
416
|
+
},
|
|
417
|
+
], 'Task finished.');
|
|
418
|
+
const output = await executor.run({ goal: 'Do work' }, signal);
|
|
419
|
+
const { modelConfigKey } = getMockMessageParams(0);
|
|
420
|
+
expect(modelConfigKey.model).toBe(getModelConfigAlias(definition));
|
|
421
|
+
const chatConstructorArgs = MockedGeminiChat.mock.calls[0];
|
|
422
|
+
const passedToolsArg = chatConstructorArgs[2];
|
|
423
|
+
const sentTools = passedToolsArg[0].functionDeclarations;
|
|
424
|
+
expect(sentTools).toBeDefined();
|
|
425
|
+
const completeToolDef = sentTools.find((t) => t.name === TASK_COMPLETE_TOOL_NAME);
|
|
426
|
+
expect(completeToolDef?.parameters?.required).toEqual(['result']);
|
|
427
|
+
expect(completeToolDef?.description).toContain('submit your final findings');
|
|
428
|
+
expect(output.result).toBe('All work done');
|
|
429
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
430
|
+
});
|
|
431
|
+
it('should error immediately if the model stops tools without calling complete_task (Protocol Violation)', async () => {
|
|
432
|
+
const definition = createTestDefinition();
|
|
433
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
434
|
+
mockModelResponse([
|
|
435
|
+
{ name: LS_TOOL_NAME, args: { path: '.' }, id: 'call1' },
|
|
436
|
+
]);
|
|
437
|
+
mockExecuteToolCall.mockResolvedValueOnce({
|
|
438
|
+
status: 'success',
|
|
439
|
+
request: {
|
|
440
|
+
callId: 'call1',
|
|
441
|
+
name: LS_TOOL_NAME,
|
|
442
|
+
args: { path: '.' },
|
|
443
|
+
isClientInitiated: false,
|
|
444
|
+
prompt_id: 'test-prompt',
|
|
445
|
+
},
|
|
446
|
+
tool: {},
|
|
447
|
+
invocation: {},
|
|
448
|
+
response: {
|
|
449
|
+
callId: 'call1',
|
|
450
|
+
resultDisplay: 'ok',
|
|
451
|
+
responseParts: [
|
|
452
|
+
{
|
|
453
|
+
functionResponse: {
|
|
454
|
+
name: LS_TOOL_NAME,
|
|
455
|
+
response: {},
|
|
456
|
+
id: 'call1',
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
],
|
|
460
|
+
error: undefined,
|
|
461
|
+
errorType: undefined,
|
|
462
|
+
contentLength: undefined,
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
// Turn 2 (protocol violation)
|
|
466
|
+
mockModelResponse([], 'I think I am done.');
|
|
467
|
+
// Turn 3 (recovery turn - also fails)
|
|
468
|
+
mockModelResponse([], 'I still give up.');
|
|
469
|
+
const output = await executor.run({ goal: 'Strict test' }, signal);
|
|
470
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(3);
|
|
471
|
+
const expectedError = `Agent stopped calling tools but did not call '${TASK_COMPLETE_TOOL_NAME}'.`;
|
|
472
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.ERROR_NO_COMPLETE_TASK_CALL);
|
|
473
|
+
expect(output.result).toBe(expectedError);
|
|
474
|
+
// Telemetry check for error
|
|
475
|
+
expect(mockedLogAgentFinish).toHaveBeenCalledWith(mockConfig, expect.objectContaining({
|
|
476
|
+
terminate_reason: AgentTerminateMode.ERROR_NO_COMPLETE_TASK_CALL,
|
|
477
|
+
}));
|
|
478
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
479
|
+
type: 'ERROR',
|
|
480
|
+
data: expect.objectContaining({
|
|
481
|
+
context: 'protocol_violation',
|
|
482
|
+
error: expectedError,
|
|
483
|
+
}),
|
|
484
|
+
}));
|
|
485
|
+
});
|
|
486
|
+
it('should report an error if complete_task is called with missing required arguments', async () => {
|
|
487
|
+
const definition = createTestDefinition();
|
|
488
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
489
|
+
// Turn 1: Missing arg
|
|
490
|
+
mockModelResponse([
|
|
491
|
+
{
|
|
492
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
493
|
+
args: { wrongArg: 'oops' },
|
|
494
|
+
id: 'call1',
|
|
495
|
+
},
|
|
496
|
+
]);
|
|
497
|
+
// Turn 2: Corrected
|
|
498
|
+
mockModelResponse([
|
|
499
|
+
{
|
|
500
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
501
|
+
args: { finalResult: 'Corrected result' },
|
|
502
|
+
id: 'call2',
|
|
503
|
+
},
|
|
504
|
+
]);
|
|
505
|
+
const output = await executor.run({ goal: 'Error test' }, signal);
|
|
506
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(2);
|
|
507
|
+
const expectedError = "Missing required argument 'finalResult' for completion.";
|
|
508
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
509
|
+
type: 'ERROR',
|
|
510
|
+
data: {
|
|
511
|
+
context: 'tool_call',
|
|
512
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
513
|
+
error: expectedError,
|
|
514
|
+
},
|
|
515
|
+
}));
|
|
516
|
+
const turn2Params = getMockMessageParams(1);
|
|
517
|
+
const turn2Parts = turn2Params.message;
|
|
518
|
+
expect(turn2Parts).toBeDefined();
|
|
519
|
+
expect(turn2Parts).toHaveLength(1);
|
|
520
|
+
expect(turn2Parts[0]).toEqual(expect.objectContaining({
|
|
521
|
+
functionResponse: expect.objectContaining({
|
|
522
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
523
|
+
response: { error: expectedError },
|
|
524
|
+
id: 'call1',
|
|
525
|
+
}),
|
|
526
|
+
}));
|
|
527
|
+
expect(output.result).toBe('Corrected result');
|
|
528
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
529
|
+
});
|
|
530
|
+
it('should handle multiple calls to complete_task in the same turn (accept first, block rest)', async () => {
|
|
531
|
+
const definition = createTestDefinition([], {}, 'none');
|
|
532
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
533
|
+
// Turn 1: Duplicate calls
|
|
534
|
+
mockModelResponse([
|
|
535
|
+
{
|
|
536
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
537
|
+
args: { result: 'done' },
|
|
538
|
+
id: 'call1',
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
542
|
+
args: { result: 'ignored' },
|
|
543
|
+
id: 'call2',
|
|
544
|
+
},
|
|
545
|
+
]);
|
|
546
|
+
const output = await executor.run({ goal: 'Dup test' }, signal);
|
|
547
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(1);
|
|
548
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
549
|
+
const completions = activities.filter((a) => a.type === 'TOOL_CALL_END' &&
|
|
550
|
+
a.data['name'] === TASK_COMPLETE_TOOL_NAME);
|
|
551
|
+
const errors = activities.filter((a) => a.type === 'ERROR' && a.data['name'] === TASK_COMPLETE_TOOL_NAME);
|
|
552
|
+
expect(completions).toHaveLength(1);
|
|
553
|
+
expect(errors).toHaveLength(1);
|
|
554
|
+
expect(errors[0].data['error']).toContain('Task already marked complete in this turn');
|
|
555
|
+
});
|
|
556
|
+
it('should execute parallel tool calls and then complete', async () => {
|
|
557
|
+
const definition = createTestDefinition([LS_TOOL_NAME]);
|
|
558
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
559
|
+
const call1 = {
|
|
560
|
+
name: LS_TOOL_NAME,
|
|
561
|
+
args: { path: '/a' },
|
|
562
|
+
id: 'c1',
|
|
563
|
+
};
|
|
564
|
+
const call2 = {
|
|
565
|
+
name: LS_TOOL_NAME,
|
|
566
|
+
args: { path: '/b' },
|
|
567
|
+
id: 'c2',
|
|
568
|
+
};
|
|
569
|
+
// Turn 1: Parallel calls
|
|
570
|
+
mockModelResponse([call1, call2]);
|
|
571
|
+
// Concurrency mock
|
|
572
|
+
let callsStarted = 0;
|
|
573
|
+
let resolveCalls;
|
|
574
|
+
const bothStarted = new Promise((r) => {
|
|
575
|
+
resolveCalls = r;
|
|
576
|
+
});
|
|
577
|
+
mockExecuteToolCall.mockImplementation(async (_ctx, reqInfo) => {
|
|
578
|
+
callsStarted++;
|
|
579
|
+
if (callsStarted === 2)
|
|
580
|
+
resolveCalls();
|
|
581
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
582
|
+
return {
|
|
583
|
+
status: 'success',
|
|
584
|
+
request: reqInfo,
|
|
585
|
+
tool: {},
|
|
586
|
+
invocation: {},
|
|
587
|
+
response: {
|
|
588
|
+
callId: reqInfo.callId,
|
|
589
|
+
resultDisplay: 'ok',
|
|
590
|
+
responseParts: [
|
|
591
|
+
{
|
|
592
|
+
functionResponse: {
|
|
593
|
+
name: reqInfo.name,
|
|
594
|
+
response: {},
|
|
595
|
+
id: reqInfo.callId,
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
],
|
|
599
|
+
error: undefined,
|
|
600
|
+
errorType: undefined,
|
|
601
|
+
contentLength: undefined,
|
|
602
|
+
},
|
|
603
|
+
};
|
|
604
|
+
});
|
|
605
|
+
// Turn 2: Completion
|
|
606
|
+
mockModelResponse([
|
|
607
|
+
{
|
|
608
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
609
|
+
args: { finalResult: 'done' },
|
|
610
|
+
id: 'c3',
|
|
611
|
+
},
|
|
612
|
+
]);
|
|
613
|
+
const runPromise = executor.run({ goal: 'Parallel' }, signal);
|
|
614
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
615
|
+
await bothStarted;
|
|
616
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
617
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
618
|
+
const output = await runPromise;
|
|
619
|
+
expect(mockExecuteToolCall).toHaveBeenCalledTimes(2);
|
|
620
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
621
|
+
// Safe access to message parts
|
|
622
|
+
const turn2Params = getMockMessageParams(1);
|
|
623
|
+
const parts = turn2Params.message;
|
|
624
|
+
expect(parts).toBeDefined();
|
|
625
|
+
expect(parts).toHaveLength(2);
|
|
626
|
+
expect(parts).toEqual(expect.arrayContaining([
|
|
627
|
+
expect.objectContaining({
|
|
628
|
+
functionResponse: expect.objectContaining({ id: 'c1' }),
|
|
629
|
+
}),
|
|
630
|
+
expect.objectContaining({
|
|
631
|
+
functionResponse: expect.objectContaining({ id: 'c2' }),
|
|
632
|
+
}),
|
|
633
|
+
]));
|
|
634
|
+
});
|
|
635
|
+
it('SECURITY: should block unauthorized tools and provide explicit failure to model', async () => {
|
|
636
|
+
const definition = createTestDefinition([LS_TOOL_NAME]);
|
|
637
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
638
|
+
// Turn 1: Model tries to use a tool not in its config
|
|
639
|
+
const badCallId = 'bad_call_1';
|
|
640
|
+
mockModelResponse([
|
|
641
|
+
{
|
|
642
|
+
name: READ_FILE_TOOL_NAME,
|
|
643
|
+
args: { path: 'secret.txt' },
|
|
644
|
+
id: badCallId,
|
|
645
|
+
},
|
|
646
|
+
]);
|
|
647
|
+
// Turn 2: Model gives up and completes
|
|
648
|
+
mockModelResponse([
|
|
649
|
+
{
|
|
650
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
651
|
+
args: { finalResult: 'Could not read file.' },
|
|
652
|
+
id: 'c2',
|
|
653
|
+
},
|
|
654
|
+
]);
|
|
655
|
+
const consoleWarnSpy = vi
|
|
656
|
+
.spyOn(debugLogger, 'warn')
|
|
657
|
+
.mockImplementation(() => { });
|
|
658
|
+
await executor.run({ goal: 'Sec test' }, signal);
|
|
659
|
+
// Verify external executor was not called (Security held)
|
|
660
|
+
expect(mockExecuteToolCall).not.toHaveBeenCalled();
|
|
661
|
+
// 2. Verify console warning
|
|
662
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining(`[LocalAgentExecutor] Blocked call:`));
|
|
663
|
+
consoleWarnSpy.mockRestore();
|
|
664
|
+
// Verify specific error was sent back to model
|
|
665
|
+
const turn2Params = getMockMessageParams(1);
|
|
666
|
+
const parts = turn2Params.message;
|
|
667
|
+
expect(parts).toBeDefined();
|
|
668
|
+
expect(parts[0]).toEqual(expect.objectContaining({
|
|
669
|
+
functionResponse: expect.objectContaining({
|
|
670
|
+
id: badCallId,
|
|
671
|
+
name: READ_FILE_TOOL_NAME,
|
|
672
|
+
response: {
|
|
673
|
+
error: expect.stringContaining('Unauthorized tool call'),
|
|
674
|
+
},
|
|
675
|
+
}),
|
|
676
|
+
}));
|
|
677
|
+
// Verify Activity Stream reported the error
|
|
678
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
679
|
+
type: 'ERROR',
|
|
680
|
+
data: expect.objectContaining({
|
|
681
|
+
context: 'tool_call_unauthorized',
|
|
682
|
+
name: READ_FILE_TOOL_NAME,
|
|
683
|
+
}),
|
|
684
|
+
}));
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
describe('Edge Cases and Error Handling', () => {
|
|
688
|
+
it('should report an error if complete_task output fails schema validation', async () => {
|
|
689
|
+
const definition = createTestDefinition([], {}, 'default', z.string().min(10));
|
|
690
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
691
|
+
// Turn 1: Invalid arg (too short)
|
|
692
|
+
mockModelResponse([
|
|
693
|
+
{
|
|
694
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
695
|
+
args: { finalResult: 'short' },
|
|
696
|
+
id: 'call1',
|
|
697
|
+
},
|
|
698
|
+
]);
|
|
699
|
+
// Turn 2: Corrected
|
|
700
|
+
mockModelResponse([
|
|
701
|
+
{
|
|
702
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
703
|
+
args: { finalResult: 'This is a much longer and valid result' },
|
|
704
|
+
id: 'call2',
|
|
705
|
+
},
|
|
706
|
+
]);
|
|
707
|
+
const output = await executor.run({ goal: 'Validation test' }, signal);
|
|
708
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(2);
|
|
709
|
+
const expectedError = 'Output validation failed: {"formErrors":["String must contain at least 10 character(s)"],"fieldErrors":{}}';
|
|
710
|
+
// Check that the error was reported in the activity stream
|
|
711
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
712
|
+
type: 'ERROR',
|
|
713
|
+
data: {
|
|
714
|
+
context: 'tool_call',
|
|
715
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
716
|
+
error: expect.stringContaining('Output validation failed'),
|
|
717
|
+
},
|
|
718
|
+
}));
|
|
719
|
+
// Check that the error was sent back to the model for the next turn
|
|
720
|
+
const turn2Params = getMockMessageParams(1);
|
|
721
|
+
const turn2Parts = turn2Params.message;
|
|
722
|
+
expect(turn2Parts).toEqual([
|
|
723
|
+
expect.objectContaining({
|
|
724
|
+
functionResponse: expect.objectContaining({
|
|
725
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
726
|
+
response: { error: expectedError },
|
|
727
|
+
id: 'call1',
|
|
728
|
+
}),
|
|
729
|
+
}),
|
|
730
|
+
]);
|
|
731
|
+
// Check that the agent eventually succeeded
|
|
732
|
+
expect(output.result).toContain('This is a much longer and valid result');
|
|
733
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
734
|
+
});
|
|
735
|
+
it('should throw and log if GeminiChat creation fails', async () => {
|
|
736
|
+
const definition = createTestDefinition();
|
|
737
|
+
const initError = new Error('Chat creation failed');
|
|
738
|
+
MockedGeminiChat.mockImplementationOnce(() => {
|
|
739
|
+
throw initError;
|
|
740
|
+
});
|
|
741
|
+
// We expect the error to be thrown during the run, not creation
|
|
742
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
743
|
+
await expect(executor.run({ goal: 'test' }, signal)).rejects.toThrow(`Failed to create chat object: ${initError}`);
|
|
744
|
+
// Ensure the error was reported via the activity callback
|
|
745
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
746
|
+
type: 'ERROR',
|
|
747
|
+
data: expect.objectContaining({
|
|
748
|
+
error: `Error: Failed to create chat object: ${initError}`,
|
|
749
|
+
}),
|
|
750
|
+
}));
|
|
751
|
+
// Ensure the agent run was logged as a failure
|
|
752
|
+
expect(mockedLogAgentFinish).toHaveBeenCalledWith(mockConfig, expect.objectContaining({
|
|
753
|
+
terminate_reason: AgentTerminateMode.ERROR,
|
|
754
|
+
}));
|
|
755
|
+
});
|
|
756
|
+
it('should handle a failed tool call and feed the error to the model', async () => {
|
|
757
|
+
const definition = createTestDefinition([LS_TOOL_NAME]);
|
|
758
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
759
|
+
const toolErrorMessage = 'Tool failed spectacularly';
|
|
760
|
+
// Turn 1: Model calls a tool that will fail
|
|
761
|
+
mockModelResponse([
|
|
762
|
+
{ name: LS_TOOL_NAME, args: { path: '/fake' }, id: 'call1' },
|
|
763
|
+
]);
|
|
764
|
+
mockExecuteToolCall.mockResolvedValueOnce({
|
|
765
|
+
status: 'error',
|
|
766
|
+
request: {
|
|
767
|
+
callId: 'call1',
|
|
768
|
+
name: LS_TOOL_NAME,
|
|
769
|
+
args: { path: '/fake' },
|
|
770
|
+
isClientInitiated: false,
|
|
771
|
+
prompt_id: 'test-prompt',
|
|
772
|
+
},
|
|
773
|
+
tool: {},
|
|
774
|
+
invocation: {},
|
|
775
|
+
response: {
|
|
776
|
+
callId: 'call1',
|
|
777
|
+
resultDisplay: '',
|
|
778
|
+
responseParts: [
|
|
779
|
+
{
|
|
780
|
+
functionResponse: {
|
|
781
|
+
name: LS_TOOL_NAME,
|
|
782
|
+
response: { error: toolErrorMessage },
|
|
783
|
+
id: 'call1',
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
],
|
|
787
|
+
error: {
|
|
788
|
+
type: 'ToolError',
|
|
789
|
+
message: toolErrorMessage,
|
|
790
|
+
},
|
|
791
|
+
errorType: 'ToolError',
|
|
792
|
+
contentLength: 0,
|
|
793
|
+
},
|
|
794
|
+
});
|
|
795
|
+
// Turn 2: Model sees the error and completes
|
|
796
|
+
mockModelResponse([
|
|
797
|
+
{
|
|
798
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
799
|
+
args: { finalResult: 'Aborted due to tool failure.' },
|
|
800
|
+
id: 'call2',
|
|
801
|
+
},
|
|
802
|
+
]);
|
|
803
|
+
const output = await executor.run({ goal: 'Tool failure test' }, signal);
|
|
804
|
+
expect(mockExecuteToolCall).toHaveBeenCalledTimes(1);
|
|
805
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(2);
|
|
806
|
+
// Verify the error was reported in the activity stream
|
|
807
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
808
|
+
type: 'ERROR',
|
|
809
|
+
data: {
|
|
810
|
+
context: 'tool_call',
|
|
811
|
+
name: LS_TOOL_NAME,
|
|
812
|
+
error: toolErrorMessage,
|
|
813
|
+
},
|
|
814
|
+
}));
|
|
815
|
+
// Verify the error was sent back to the model
|
|
816
|
+
const turn2Params = getMockMessageParams(1);
|
|
817
|
+
const parts = turn2Params.message;
|
|
818
|
+
expect(parts).toEqual([
|
|
819
|
+
expect.objectContaining({
|
|
820
|
+
functionResponse: expect.objectContaining({
|
|
821
|
+
name: LS_TOOL_NAME,
|
|
822
|
+
id: 'call1',
|
|
823
|
+
response: {
|
|
824
|
+
error: toolErrorMessage,
|
|
825
|
+
},
|
|
826
|
+
}),
|
|
827
|
+
}),
|
|
828
|
+
]);
|
|
829
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
830
|
+
expect(output.result).toBe('Aborted due to tool failure.');
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
describe('run (Termination Conditions)', () => {
|
|
834
|
+
const mockWorkResponse = (id) => {
|
|
835
|
+
mockModelResponse([{ name: LS_TOOL_NAME, args: { path: '.' }, id }]);
|
|
836
|
+
mockExecuteToolCall.mockResolvedValueOnce({
|
|
837
|
+
status: 'success',
|
|
838
|
+
request: {
|
|
839
|
+
callId: id,
|
|
840
|
+
name: LS_TOOL_NAME,
|
|
841
|
+
args: { path: '.' },
|
|
842
|
+
isClientInitiated: false,
|
|
843
|
+
prompt_id: 'test-prompt',
|
|
844
|
+
},
|
|
845
|
+
tool: {},
|
|
846
|
+
invocation: {},
|
|
847
|
+
response: {
|
|
848
|
+
callId: id,
|
|
849
|
+
resultDisplay: 'ok',
|
|
850
|
+
responseParts: [
|
|
851
|
+
{ functionResponse: { name: LS_TOOL_NAME, response: {}, id } },
|
|
852
|
+
],
|
|
853
|
+
error: undefined,
|
|
854
|
+
errorType: undefined,
|
|
855
|
+
contentLength: undefined,
|
|
856
|
+
},
|
|
857
|
+
});
|
|
858
|
+
};
|
|
859
|
+
it('should terminate when max_turns is reached', async () => {
|
|
860
|
+
const MAX = 2;
|
|
861
|
+
const definition = createTestDefinition([LS_TOOL_NAME], {
|
|
862
|
+
max_turns: MAX,
|
|
863
|
+
});
|
|
864
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig);
|
|
865
|
+
mockWorkResponse('t1');
|
|
866
|
+
mockWorkResponse('t2');
|
|
867
|
+
// Recovery turn
|
|
868
|
+
mockModelResponse([], 'I give up');
|
|
869
|
+
const output = await executor.run({ goal: 'Turns test' }, signal);
|
|
870
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.MAX_TURNS);
|
|
871
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(MAX + 1);
|
|
872
|
+
});
|
|
873
|
+
it('should terminate with TIMEOUT if a model call takes too long', async () => {
|
|
874
|
+
const definition = createTestDefinition([LS_TOOL_NAME], {
|
|
875
|
+
max_time_minutes: 0.5, // 30 seconds
|
|
876
|
+
});
|
|
877
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
878
|
+
// Mock a model call that is interruptible by an abort signal.
|
|
879
|
+
mockSendMessageStream.mockImplementationOnce(async (_key, _message, _promptId, signal) =>
|
|
880
|
+
// eslint-disable-next-line require-yield
|
|
881
|
+
(async function* () {
|
|
882
|
+
await new Promise((resolve) => {
|
|
883
|
+
// This promise resolves when aborted, ending the generator.
|
|
884
|
+
signal?.addEventListener('abort', () => {
|
|
885
|
+
resolve();
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
})());
|
|
889
|
+
// Recovery turn
|
|
890
|
+
mockModelResponse([], 'I give up');
|
|
891
|
+
const runPromise = executor.run({ goal: 'Timeout test' }, signal);
|
|
892
|
+
// Advance time past the timeout to trigger the abort.
|
|
893
|
+
await vi.advanceTimersByTimeAsync(31 * 1000);
|
|
894
|
+
const output = await runPromise;
|
|
895
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.TIMEOUT);
|
|
896
|
+
expect(output.result).toContain('Agent timed out after 0.5 minutes.');
|
|
897
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(2);
|
|
898
|
+
// Verify activity stream reported the timeout
|
|
899
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
900
|
+
type: 'ERROR',
|
|
901
|
+
data: expect.objectContaining({
|
|
902
|
+
context: 'timeout',
|
|
903
|
+
error: 'Agent timed out after 0.5 minutes.',
|
|
904
|
+
}),
|
|
905
|
+
}));
|
|
906
|
+
// Verify telemetry
|
|
907
|
+
expect(mockedLogAgentFinish).toHaveBeenCalledWith(mockConfig, expect.objectContaining({
|
|
908
|
+
terminate_reason: AgentTerminateMode.TIMEOUT,
|
|
909
|
+
}));
|
|
910
|
+
});
|
|
911
|
+
it('should terminate with TIMEOUT if a tool call takes too long', async () => {
|
|
912
|
+
const definition = createTestDefinition([LS_TOOL_NAME], {
|
|
913
|
+
max_time_minutes: 1,
|
|
914
|
+
});
|
|
915
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig);
|
|
916
|
+
mockModelResponse([
|
|
917
|
+
{ name: LS_TOOL_NAME, args: { path: '.' }, id: 't1' },
|
|
918
|
+
]);
|
|
919
|
+
// Long running tool
|
|
920
|
+
mockExecuteToolCall.mockImplementationOnce(async (_ctx, reqInfo) => {
|
|
921
|
+
await vi.advanceTimersByTimeAsync(61 * 1000);
|
|
922
|
+
return {
|
|
923
|
+
status: 'success',
|
|
924
|
+
request: reqInfo,
|
|
925
|
+
tool: {},
|
|
926
|
+
invocation: {},
|
|
927
|
+
response: {
|
|
928
|
+
callId: 't1',
|
|
929
|
+
resultDisplay: 'ok',
|
|
930
|
+
responseParts: [],
|
|
931
|
+
error: undefined,
|
|
932
|
+
errorType: undefined,
|
|
933
|
+
contentLength: undefined,
|
|
934
|
+
},
|
|
935
|
+
};
|
|
936
|
+
});
|
|
937
|
+
// Recovery turn
|
|
938
|
+
mockModelResponse([], 'I give up');
|
|
939
|
+
const output = await executor.run({ goal: 'Timeout test' }, signal);
|
|
940
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.TIMEOUT);
|
|
941
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(2);
|
|
942
|
+
});
|
|
943
|
+
it('should terminate when AbortSignal is triggered', async () => {
|
|
944
|
+
const definition = createTestDefinition();
|
|
945
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig);
|
|
946
|
+
mockSendMessageStream.mockImplementationOnce(async () => (async function* () {
|
|
947
|
+
yield {
|
|
948
|
+
type: StreamEventType.CHUNK,
|
|
949
|
+
value: createMockResponseChunk([
|
|
950
|
+
{ text: 'Thinking...', thought: true },
|
|
951
|
+
]),
|
|
952
|
+
};
|
|
953
|
+
abortController.abort();
|
|
954
|
+
})());
|
|
955
|
+
const output = await executor.run({ goal: 'Abort test' }, signal);
|
|
956
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.ABORTED);
|
|
957
|
+
});
|
|
958
|
+
});
|
|
959
|
+
describe('run (Recovery Turns)', () => {
|
|
960
|
+
const mockWorkResponse = (id) => {
|
|
961
|
+
mockModelResponse([{ name: LS_TOOL_NAME, args: { path: '.' }, id }]);
|
|
962
|
+
mockExecuteToolCall.mockResolvedValueOnce({
|
|
963
|
+
status: 'success',
|
|
964
|
+
request: {
|
|
965
|
+
callId: id,
|
|
966
|
+
name: LS_TOOL_NAME,
|
|
967
|
+
args: { path: '.' },
|
|
968
|
+
isClientInitiated: false,
|
|
969
|
+
prompt_id: 'test-prompt',
|
|
970
|
+
},
|
|
971
|
+
tool: {},
|
|
972
|
+
invocation: {},
|
|
973
|
+
response: {
|
|
974
|
+
callId: id,
|
|
975
|
+
resultDisplay: 'ok',
|
|
976
|
+
responseParts: [
|
|
977
|
+
{ functionResponse: { name: LS_TOOL_NAME, response: {}, id } },
|
|
978
|
+
],
|
|
979
|
+
error: undefined,
|
|
980
|
+
errorType: undefined,
|
|
981
|
+
contentLength: undefined,
|
|
982
|
+
},
|
|
983
|
+
});
|
|
984
|
+
};
|
|
985
|
+
it('should recover successfully if complete_task is called during the grace turn after MAX_TURNS', async () => {
|
|
986
|
+
const MAX = 1;
|
|
987
|
+
const definition = createTestDefinition([LS_TOOL_NAME], {
|
|
988
|
+
max_turns: MAX,
|
|
989
|
+
});
|
|
990
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
991
|
+
// Turn 1 (hits max_turns)
|
|
992
|
+
mockWorkResponse('t1');
|
|
993
|
+
// Recovery Turn (succeeds)
|
|
994
|
+
mockModelResponse([
|
|
995
|
+
{
|
|
996
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
997
|
+
args: { finalResult: 'Recovered!' },
|
|
998
|
+
id: 't2',
|
|
999
|
+
},
|
|
1000
|
+
], 'Recovering from max turns');
|
|
1001
|
+
const output = await executor.run({ goal: 'Turns recovery' }, signal);
|
|
1002
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
1003
|
+
expect(output.result).toBe('Recovered!');
|
|
1004
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(MAX + 1); // 1 regular + 1 recovery
|
|
1005
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
1006
|
+
type: 'THOUGHT_CHUNK',
|
|
1007
|
+
data: {
|
|
1008
|
+
text: 'Execution limit reached (MAX_TURNS). Attempting one final recovery turn with a grace period.',
|
|
1009
|
+
},
|
|
1010
|
+
}));
|
|
1011
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
1012
|
+
type: 'THOUGHT_CHUNK',
|
|
1013
|
+
data: { text: 'Graceful recovery succeeded.' },
|
|
1014
|
+
}));
|
|
1015
|
+
});
|
|
1016
|
+
it('should fail if complete_task is NOT called during the grace turn after MAX_TURNS', async () => {
|
|
1017
|
+
const MAX = 1;
|
|
1018
|
+
const definition = createTestDefinition([LS_TOOL_NAME], {
|
|
1019
|
+
max_turns: MAX,
|
|
1020
|
+
});
|
|
1021
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
1022
|
+
// Turn 1 (hits max_turns)
|
|
1023
|
+
mockWorkResponse('t1');
|
|
1024
|
+
// Recovery Turn (fails by calling no tools)
|
|
1025
|
+
mockModelResponse([], 'I give up again.');
|
|
1026
|
+
const output = await executor.run({ goal: 'Turns recovery fail' }, signal);
|
|
1027
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.MAX_TURNS);
|
|
1028
|
+
expect(output.result).toContain('Agent reached max turns limit');
|
|
1029
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(MAX + 1);
|
|
1030
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
1031
|
+
type: 'ERROR',
|
|
1032
|
+
data: expect.objectContaining({
|
|
1033
|
+
context: 'recovery_turn',
|
|
1034
|
+
error: 'Graceful recovery attempt failed. Reason: stop',
|
|
1035
|
+
}),
|
|
1036
|
+
}));
|
|
1037
|
+
});
|
|
1038
|
+
it('should recover successfully from a protocol violation (no complete_task)', async () => {
|
|
1039
|
+
const definition = createTestDefinition();
|
|
1040
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
1041
|
+
// Turn 1: Normal work
|
|
1042
|
+
mockWorkResponse('t1');
|
|
1043
|
+
// Turn 2: Protocol violation (no tool calls)
|
|
1044
|
+
mockModelResponse([], 'I think I am done, but I forgot the right tool.');
|
|
1045
|
+
// Turn 3: Recovery turn (succeeds)
|
|
1046
|
+
mockModelResponse([
|
|
1047
|
+
{
|
|
1048
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
1049
|
+
args: { finalResult: 'Recovered from violation!' },
|
|
1050
|
+
id: 't3',
|
|
1051
|
+
},
|
|
1052
|
+
], 'My mistake, here is the completion.');
|
|
1053
|
+
const output = await executor.run({ goal: 'Violation recovery' }, signal);
|
|
1054
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(3);
|
|
1055
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
1056
|
+
expect(output.result).toBe('Recovered from violation!');
|
|
1057
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
1058
|
+
type: 'THOUGHT_CHUNK',
|
|
1059
|
+
data: {
|
|
1060
|
+
text: 'Execution limit reached (ERROR_NO_COMPLETE_TASK_CALL). Attempting one final recovery turn with a grace period.',
|
|
1061
|
+
},
|
|
1062
|
+
}));
|
|
1063
|
+
});
|
|
1064
|
+
it('should fail recovery from a protocol violation if it violates again', async () => {
|
|
1065
|
+
const definition = createTestDefinition();
|
|
1066
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
1067
|
+
// Turn 1: Normal work
|
|
1068
|
+
mockWorkResponse('t1');
|
|
1069
|
+
// Turn 2: Protocol violation (no tool calls)
|
|
1070
|
+
mockModelResponse([], 'I think I am done, but I forgot the right tool.');
|
|
1071
|
+
// Turn 3: Recovery turn (fails again)
|
|
1072
|
+
mockModelResponse([], 'I still dont know what to do.');
|
|
1073
|
+
const output = await executor.run({ goal: 'Violation recovery fail' }, signal);
|
|
1074
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(3);
|
|
1075
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.ERROR_NO_COMPLETE_TASK_CALL);
|
|
1076
|
+
expect(output.result).toContain(`Agent stopped calling tools but did not call '${TASK_COMPLETE_TOOL_NAME}'`);
|
|
1077
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
1078
|
+
type: 'ERROR',
|
|
1079
|
+
data: expect.objectContaining({
|
|
1080
|
+
context: 'recovery_turn',
|
|
1081
|
+
error: 'Graceful recovery attempt failed. Reason: stop',
|
|
1082
|
+
}),
|
|
1083
|
+
}));
|
|
1084
|
+
});
|
|
1085
|
+
it('should recover successfully from a TIMEOUT', async () => {
|
|
1086
|
+
const definition = createTestDefinition([LS_TOOL_NAME], {
|
|
1087
|
+
max_time_minutes: 0.5, // 30 seconds
|
|
1088
|
+
});
|
|
1089
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
1090
|
+
// Mock a model call that gets interrupted by the timeout.
|
|
1091
|
+
mockSendMessageStream.mockImplementationOnce(async (_key, _message, _promptId, signal) =>
|
|
1092
|
+
// eslint-disable-next-line require-yield
|
|
1093
|
+
(async function* () {
|
|
1094
|
+
// This promise never resolves, it waits for abort.
|
|
1095
|
+
await new Promise((resolve) => {
|
|
1096
|
+
signal?.addEventListener('abort', () => resolve());
|
|
1097
|
+
});
|
|
1098
|
+
})());
|
|
1099
|
+
// Recovery turn (succeeds)
|
|
1100
|
+
mockModelResponse([
|
|
1101
|
+
{
|
|
1102
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
1103
|
+
args: { finalResult: 'Recovered from timeout!' },
|
|
1104
|
+
id: 't2',
|
|
1105
|
+
},
|
|
1106
|
+
], 'Apologies for the delay, finishing up.');
|
|
1107
|
+
const runPromise = executor.run({ goal: 'Timeout recovery' }, signal);
|
|
1108
|
+
// Advance time past the timeout to trigger the abort and recovery.
|
|
1109
|
+
await vi.advanceTimersByTimeAsync(31 * 1000);
|
|
1110
|
+
const output = await runPromise;
|
|
1111
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(2); // 1 failed + 1 recovery
|
|
1112
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.GOAL);
|
|
1113
|
+
expect(output.result).toBe('Recovered from timeout!');
|
|
1114
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
1115
|
+
type: 'THOUGHT_CHUNK',
|
|
1116
|
+
data: {
|
|
1117
|
+
text: 'Execution limit reached (TIMEOUT). Attempting one final recovery turn with a grace period.',
|
|
1118
|
+
},
|
|
1119
|
+
}));
|
|
1120
|
+
});
|
|
1121
|
+
it('should fail recovery from a TIMEOUT if the grace period also times out', async () => {
|
|
1122
|
+
const definition = createTestDefinition([LS_TOOL_NAME], {
|
|
1123
|
+
max_time_minutes: 0.5, // 30 seconds
|
|
1124
|
+
});
|
|
1125
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
1126
|
+
mockSendMessageStream.mockImplementationOnce(async (_key, _message, _promptId, signal) =>
|
|
1127
|
+
// eslint-disable-next-line require-yield
|
|
1128
|
+
(async function* () {
|
|
1129
|
+
await new Promise((resolve) => signal?.addEventListener('abort', () => resolve()));
|
|
1130
|
+
})());
|
|
1131
|
+
// Mock the recovery call to also be long-running
|
|
1132
|
+
mockSendMessageStream.mockImplementationOnce(async (_key, _message, _promptId, signal) =>
|
|
1133
|
+
// eslint-disable-next-line require-yield
|
|
1134
|
+
(async function* () {
|
|
1135
|
+
await new Promise((resolve) => signal?.addEventListener('abort', () => resolve()));
|
|
1136
|
+
})());
|
|
1137
|
+
const runPromise = executor.run({ goal: 'Timeout recovery fail' }, signal);
|
|
1138
|
+
// 1. Trigger the main timeout
|
|
1139
|
+
await vi.advanceTimersByTimeAsync(31 * 1000);
|
|
1140
|
+
// 2. Let microtasks run (start recovery turn)
|
|
1141
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
1142
|
+
// 3. Trigger the grace period timeout (60s)
|
|
1143
|
+
await vi.advanceTimersByTimeAsync(61 * 1000);
|
|
1144
|
+
const output = await runPromise;
|
|
1145
|
+
expect(mockSendMessageStream).toHaveBeenCalledTimes(2);
|
|
1146
|
+
expect(output.terminate_reason).toBe(AgentTerminateMode.TIMEOUT);
|
|
1147
|
+
expect(output.result).toContain('Agent timed out after 0.5 minutes.');
|
|
1148
|
+
expect(activities).toContainEqual(expect.objectContaining({
|
|
1149
|
+
type: 'ERROR',
|
|
1150
|
+
data: expect.objectContaining({
|
|
1151
|
+
context: 'recovery_turn',
|
|
1152
|
+
error: 'Graceful recovery attempt failed. Reason: stop',
|
|
1153
|
+
}),
|
|
1154
|
+
}));
|
|
1155
|
+
});
|
|
1156
|
+
});
|
|
1157
|
+
describe('Telemetry and Logging', () => {
|
|
1158
|
+
const mockWorkResponse = (id) => {
|
|
1159
|
+
mockModelResponse([{ name: LS_TOOL_NAME, args: { path: '.' }, id }]);
|
|
1160
|
+
mockExecuteToolCall.mockResolvedValueOnce({
|
|
1161
|
+
status: 'success',
|
|
1162
|
+
request: {
|
|
1163
|
+
callId: id,
|
|
1164
|
+
name: LS_TOOL_NAME,
|
|
1165
|
+
args: { path: '.' },
|
|
1166
|
+
isClientInitiated: false,
|
|
1167
|
+
prompt_id: 'test-prompt',
|
|
1168
|
+
},
|
|
1169
|
+
tool: {},
|
|
1170
|
+
invocation: {},
|
|
1171
|
+
response: {
|
|
1172
|
+
callId: id,
|
|
1173
|
+
resultDisplay: 'ok',
|
|
1174
|
+
responseParts: [
|
|
1175
|
+
{ functionResponse: { name: LS_TOOL_NAME, response: {}, id } },
|
|
1176
|
+
],
|
|
1177
|
+
error: undefined,
|
|
1178
|
+
errorType: undefined,
|
|
1179
|
+
contentLength: undefined,
|
|
1180
|
+
},
|
|
1181
|
+
});
|
|
1182
|
+
};
|
|
1183
|
+
beforeEach(() => {
|
|
1184
|
+
mockedLogRecoveryAttempt.mockClear();
|
|
1185
|
+
});
|
|
1186
|
+
it('should log a RecoveryAttemptEvent when a recoverable error occurs and recovery fails', async () => {
|
|
1187
|
+
const MAX = 1;
|
|
1188
|
+
const definition = createTestDefinition([LS_TOOL_NAME], {
|
|
1189
|
+
max_turns: MAX,
|
|
1190
|
+
});
|
|
1191
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig);
|
|
1192
|
+
// Turn 1 (hits max_turns)
|
|
1193
|
+
mockWorkResponse('t1');
|
|
1194
|
+
// Recovery Turn (fails by calling no tools)
|
|
1195
|
+
mockModelResponse([], 'I give up again.');
|
|
1196
|
+
await executor.run({ goal: 'Turns recovery fail' }, signal);
|
|
1197
|
+
expect(mockedLogRecoveryAttempt).toHaveBeenCalledTimes(1);
|
|
1198
|
+
const recoveryEvent = mockedLogRecoveryAttempt.mock.calls[0][1];
|
|
1199
|
+
expect(recoveryEvent).toBeInstanceOf(RecoveryAttemptEvent);
|
|
1200
|
+
expect(recoveryEvent.agent_name).toBe(definition.name);
|
|
1201
|
+
expect(recoveryEvent.reason).toBe(AgentTerminateMode.MAX_TURNS);
|
|
1202
|
+
expect(recoveryEvent.success).toBe(false);
|
|
1203
|
+
expect(recoveryEvent.turn_count).toBe(1);
|
|
1204
|
+
expect(recoveryEvent.duration_ms).toBeGreaterThanOrEqual(0);
|
|
1205
|
+
});
|
|
1206
|
+
it('should log a successful RecoveryAttemptEvent when recovery succeeds', async () => {
|
|
1207
|
+
const MAX = 1;
|
|
1208
|
+
const definition = createTestDefinition([LS_TOOL_NAME], {
|
|
1209
|
+
max_turns: MAX,
|
|
1210
|
+
});
|
|
1211
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig);
|
|
1212
|
+
// Turn 1 (hits max_turns)
|
|
1213
|
+
mockWorkResponse('t1');
|
|
1214
|
+
// Recovery Turn (succeeds)
|
|
1215
|
+
mockModelResponse([
|
|
1216
|
+
{
|
|
1217
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
1218
|
+
args: { finalResult: 'Recovered!' },
|
|
1219
|
+
id: 't2',
|
|
1220
|
+
},
|
|
1221
|
+
], 'Recovering from max turns');
|
|
1222
|
+
await executor.run({ goal: 'Turns recovery success' }, signal);
|
|
1223
|
+
expect(mockedLogRecoveryAttempt).toHaveBeenCalledTimes(1);
|
|
1224
|
+
const recoveryEvent = mockedLogRecoveryAttempt.mock.calls[0][1];
|
|
1225
|
+
expect(recoveryEvent).toBeInstanceOf(RecoveryAttemptEvent);
|
|
1226
|
+
expect(recoveryEvent.success).toBe(true);
|
|
1227
|
+
expect(recoveryEvent.reason).toBe(AgentTerminateMode.MAX_TURNS);
|
|
1228
|
+
});
|
|
1229
|
+
});
|
|
1230
|
+
describe('Chat Compression', () => {
|
|
1231
|
+
const mockWorkResponse = (id) => {
|
|
1232
|
+
mockModelResponse([{ name: LS_TOOL_NAME, args: { path: '.' }, id }]);
|
|
1233
|
+
mockExecuteToolCall.mockResolvedValueOnce({
|
|
1234
|
+
status: 'success',
|
|
1235
|
+
request: {
|
|
1236
|
+
callId: id,
|
|
1237
|
+
name: LS_TOOL_NAME,
|
|
1238
|
+
args: { path: '.' },
|
|
1239
|
+
isClientInitiated: false,
|
|
1240
|
+
prompt_id: 'test-prompt',
|
|
1241
|
+
},
|
|
1242
|
+
tool: {},
|
|
1243
|
+
invocation: {},
|
|
1244
|
+
response: {
|
|
1245
|
+
callId: id,
|
|
1246
|
+
resultDisplay: 'ok',
|
|
1247
|
+
responseParts: [
|
|
1248
|
+
{ functionResponse: { name: LS_TOOL_NAME, response: {}, id } },
|
|
1249
|
+
],
|
|
1250
|
+
error: undefined,
|
|
1251
|
+
errorType: undefined,
|
|
1252
|
+
contentLength: undefined,
|
|
1253
|
+
},
|
|
1254
|
+
});
|
|
1255
|
+
};
|
|
1256
|
+
it('should attempt to compress chat history on each turn', async () => {
|
|
1257
|
+
const definition = createTestDefinition();
|
|
1258
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
1259
|
+
// Mock compression to do nothing
|
|
1260
|
+
mockCompress.mockResolvedValue({
|
|
1261
|
+
newHistory: null,
|
|
1262
|
+
info: { compressionStatus: CompressionStatus.NOOP },
|
|
1263
|
+
});
|
|
1264
|
+
// Turn 1
|
|
1265
|
+
mockWorkResponse('t1');
|
|
1266
|
+
// Turn 2: Complete
|
|
1267
|
+
mockModelResponse([
|
|
1268
|
+
{
|
|
1269
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
1270
|
+
args: { finalResult: 'Done' },
|
|
1271
|
+
id: 'call2',
|
|
1272
|
+
},
|
|
1273
|
+
], 'T2');
|
|
1274
|
+
await executor.run({ goal: 'Compress test' }, signal);
|
|
1275
|
+
expect(mockCompress).toHaveBeenCalledTimes(2);
|
|
1276
|
+
});
|
|
1277
|
+
it('should update chat history when compression is successful', async () => {
|
|
1278
|
+
const definition = createTestDefinition();
|
|
1279
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
1280
|
+
const compressedHistory = [
|
|
1281
|
+
{ role: 'user', parts: [{ text: 'compressed' }] },
|
|
1282
|
+
];
|
|
1283
|
+
mockCompress.mockResolvedValue({
|
|
1284
|
+
newHistory: compressedHistory,
|
|
1285
|
+
info: { compressionStatus: CompressionStatus.COMPRESSED },
|
|
1286
|
+
});
|
|
1287
|
+
// Turn 1: Complete
|
|
1288
|
+
mockModelResponse([
|
|
1289
|
+
{
|
|
1290
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
1291
|
+
args: { finalResult: 'Done' },
|
|
1292
|
+
id: 'call1',
|
|
1293
|
+
},
|
|
1294
|
+
], 'T1');
|
|
1295
|
+
await executor.run({ goal: 'Compress success' }, signal);
|
|
1296
|
+
expect(mockCompress).toHaveBeenCalledTimes(1);
|
|
1297
|
+
expect(mockSetHistory).toHaveBeenCalledTimes(1);
|
|
1298
|
+
expect(mockSetHistory).toHaveBeenCalledWith(compressedHistory);
|
|
1299
|
+
});
|
|
1300
|
+
it('should pass hasFailedCompressionAttempt=true to compression after a failure', async () => {
|
|
1301
|
+
const definition = createTestDefinition();
|
|
1302
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
1303
|
+
// First call fails
|
|
1304
|
+
mockCompress.mockResolvedValueOnce({
|
|
1305
|
+
newHistory: null,
|
|
1306
|
+
info: {
|
|
1307
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
1308
|
+
},
|
|
1309
|
+
});
|
|
1310
|
+
// Second call is neutral
|
|
1311
|
+
mockCompress.mockResolvedValueOnce({
|
|
1312
|
+
newHistory: null,
|
|
1313
|
+
info: { compressionStatus: CompressionStatus.NOOP },
|
|
1314
|
+
});
|
|
1315
|
+
// Turn 1
|
|
1316
|
+
mockWorkResponse('t1');
|
|
1317
|
+
// Turn 2: Complete
|
|
1318
|
+
mockModelResponse([
|
|
1319
|
+
{
|
|
1320
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
1321
|
+
args: { finalResult: 'Done' },
|
|
1322
|
+
id: 't2',
|
|
1323
|
+
},
|
|
1324
|
+
], 'T2');
|
|
1325
|
+
await executor.run({ goal: 'Compress fail' }, signal);
|
|
1326
|
+
expect(mockCompress).toHaveBeenCalledTimes(2);
|
|
1327
|
+
// First call, hasFailedCompressionAttempt is false
|
|
1328
|
+
expect(mockCompress.mock.calls[0][5]).toBe(false);
|
|
1329
|
+
// Second call, hasFailedCompressionAttempt is true
|
|
1330
|
+
expect(mockCompress.mock.calls[1][5]).toBe(true);
|
|
1331
|
+
});
|
|
1332
|
+
it('should reset hasFailedCompressionAttempt flag after a successful compression', async () => {
|
|
1333
|
+
const definition = createTestDefinition();
|
|
1334
|
+
const executor = await LocalAgentExecutor.create(definition, mockConfig, onActivity);
|
|
1335
|
+
const compressedHistory = [
|
|
1336
|
+
{ role: 'user', parts: [{ text: 'compressed' }] },
|
|
1337
|
+
];
|
|
1338
|
+
// Turn 1: Fails
|
|
1339
|
+
mockCompress.mockResolvedValueOnce({
|
|
1340
|
+
newHistory: null,
|
|
1341
|
+
info: {
|
|
1342
|
+
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
1343
|
+
},
|
|
1344
|
+
});
|
|
1345
|
+
// Turn 2: Succeeds
|
|
1346
|
+
mockCompress.mockResolvedValueOnce({
|
|
1347
|
+
newHistory: compressedHistory,
|
|
1348
|
+
info: { compressionStatus: CompressionStatus.COMPRESSED },
|
|
1349
|
+
});
|
|
1350
|
+
// Turn 3: Neutral
|
|
1351
|
+
mockCompress.mockResolvedValueOnce({
|
|
1352
|
+
newHistory: null,
|
|
1353
|
+
info: { compressionStatus: CompressionStatus.NOOP },
|
|
1354
|
+
});
|
|
1355
|
+
// Turn 1
|
|
1356
|
+
mockWorkResponse('t1');
|
|
1357
|
+
// Turn 2
|
|
1358
|
+
mockWorkResponse('t2');
|
|
1359
|
+
// Turn 3: Complete
|
|
1360
|
+
mockModelResponse([
|
|
1361
|
+
{
|
|
1362
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
1363
|
+
args: { finalResult: 'Done' },
|
|
1364
|
+
id: 't3',
|
|
1365
|
+
},
|
|
1366
|
+
], 'T3');
|
|
1367
|
+
await executor.run({ goal: 'Compress reset' }, signal);
|
|
1368
|
+
expect(mockCompress).toHaveBeenCalledTimes(3);
|
|
1369
|
+
// Call 1: hasFailed... is false
|
|
1370
|
+
expect(mockCompress.mock.calls[0][5]).toBe(false);
|
|
1371
|
+
// Call 2: hasFailed... is true
|
|
1372
|
+
expect(mockCompress.mock.calls[1][5]).toBe(true);
|
|
1373
|
+
// Call 3: hasFailed... is false again
|
|
1374
|
+
expect(mockCompress.mock.calls[2][5]).toBe(false);
|
|
1375
|
+
expect(mockSetHistory).toHaveBeenCalledTimes(1);
|
|
1376
|
+
expect(mockSetHistory).toHaveBeenCalledWith(compressedHistory);
|
|
1377
|
+
});
|
|
1378
|
+
});
|
|
1379
|
+
});
|
|
1380
|
+
//# sourceMappingURL=local-executor.test.js.map
|