From bee037fbdb4782157a79abe7ef99dacda99a949d Mon Sep 17 00:00:00 2001 From: Tyson Thomas Date: Sat, 20 Sep 2025 12:24:49 -0700 Subject: [PATCH 1/3] MCP OAuth Support and more updates --- config/gni/devtools_grd_files.gni | 52 +- config/gni/devtools_image_files.gni | 13 + .../schema-extractor/amazon-product-001.yaml | 2 +- .../evals/schema-extractor/bbc-news-001.yaml | 2 +- .../schema-extractor/bing-search-001.yaml | 2 +- .../schema-extractor/github-repo-001.yaml | 2 +- .../schema-extractor/google-flights-001.yaml | 2 +- .../schema-extractor/google-search-001.yaml | 2 +- .../evals/schema-extractor/homedepot-001.yaml | 2 +- .../evals/schema-extractor/macys-001.yaml | 2 +- .../wikipedia-search-001.yaml | 2 +- .../nodejs/evals/web-task-agent/jobs-001.yaml | 2 +- .../evals/web-task-agent/realestate-001.yaml | 2 +- .../web-task-agent-jobs-001.yaml | 2 +- .../web-task-agent-realestate-001.yaml | 2 +- eval-server/nodejs/schemas/client.schema.json | 2 +- .../nodejs/templates/default-client.yaml | 2 +- front_end/Images/src/asana-mcp.svg | 5 + front_end/Images/src/atlassian-mcp.svg | 4 + front_end/Images/src/github-mcp.svg | 4 + front_end/Images/src/google-drive-mcp.svg | 5 + front_end/Images/src/google-sheets-mcp.svg | 9 + front_end/Images/src/huggingface-mcp.svg | 11 + front_end/Images/src/intercom-mcp.svg | 1 + front_end/Images/src/invideo-mcp.svg | 4 + front_end/Images/src/linear-mcp.svg | 1 + front_end/Images/src/notion-mcp.svg | 1 + front_end/Images/src/sentry-mcp.svg | 1 + front_end/Images/src/slack-mcp.svg | 7 + front_end/Images/src/socket-mcp.svg | 5 + front_end/panels/ai_chat/BUILD.gn | 50 +- .../ai_chat/agent_framework/AgentRunner.ts | 39 +- .../agent_framework/AgentSessionTypes.ts | 4 +- .../agent_framework/ConfigurableAgentTool.ts | 33 +- .../implementation/ConfiguredAgents.ts | 1376 +- .../implementation/agents/ActionAgent.ts | 139 + .../agents/ActionVerificationAgent.ts | 110 + .../implementation/agents/AgentVersion.ts | 1 + .../implementation/agents/ClickActionAgent.ts | 84 + .../agents/ContentWriterAgent.ts | 72 + .../agents/DirectURLNavigatorAgent.ts | 72 + .../agents/EcommerceProductInfoAgent.ts | 138 + .../agents/FormFillActionAgent.ts | 86 + .../implementation/agents/HoverActionAgent.ts | 86 + .../agents/KeyboardInputActionAgent.ts | 87 + .../implementation/agents/ResearchAgent.ts | 207 + .../agents/ScrollActionAgent.ts | 88 + .../implementation/agents/SearchAgent.ts | 287 + .../implementation/agents/WebTaskAgent.ts | 241 + .../ai_chat/core/AgentDescriptorRegistry.ts | 138 + front_end/panels/ai_chat/core/AgentNodes.ts | 189 +- front_end/panels/ai_chat/core/AgentService.ts | 135 +- .../ai_chat/core/BaseOrchestratorAgent.ts | 107 +- .../panels/ai_chat/core/PageInfoManager.ts | 2 +- front_end/panels/ai_chat/core/State.ts | 6 + front_end/panels/ai_chat/core/StateGraph.ts | 8 +- front_end/panels/ai_chat/core/ToolNameMap.ts | 8 +- .../ai_chat/core/ToolSurfaceProvider.ts | 172 +- front_end/panels/ai_chat/core/Types.ts | 2 +- front_end/panels/ai_chat/core/Version.ts | 4 +- .../core/{ => __tests__}/AgentNodes.test.ts | 53 +- .../core/__tests__/ToolExecutorNode.test.ts | 278 + .../core/__tests__/ToolNameMap.test.ts | 142 + .../{ => __tests__}/ToolNameMapping.test.ts | 14 +- .../ToolSurfaceProvider.test.ts | 12 +- .../docs/MCP_OAuth_Implementation_Plan.md | 333 + .../evaluation/remote/EvaluationAgent.ts | 99 +- .../evaluation/runner/EvaluationRunner.ts | 30 +- .../test-cases/schema-extractor-tests.ts | 20 +- .../test-cases/web-task-agent-tests.ts | 4 +- front_end/panels/ai_chat/mcp/MCPConfig.ts | 442 +- front_end/panels/ai_chat/mcp/MCPRegistry.ts | 622 +- .../panels/ai_chat/mcp/MCPToolAdapter.ts | 2 +- .../mcp/{ => __tests__}/MCPClientSDK.test.ts | 14 +- .../ai_chat/mcp/__tests__/MCPConfig.test.ts | 173 + .../mcp/__tests__/MCPIntegration.test.ts | 318 + .../mcp/__tests__/MCPToolRegistration.test.ts | 195 + front_end/panels/ai_chat/tools/FetcherTool.ts | 42 +- .../ai_chat/tools/SchemaBasedExtractorTool.ts | 2 +- .../tools/StreamlinedSchemaExtractorTool.ts | 25 +- front_end/panels/ai_chat/tools/Tools.ts | 88 +- front_end/panels/ai_chat/ui/AIChatPanel.ts | 54 +- front_end/panels/ai_chat/ui/ChatView.ts | 205 +- front_end/panels/ai_chat/ui/SettingsDialog.ts | 3283 +- .../ui/{ => __tests__}/AIChatPanel.test.ts | 25 +- front_end/panels/ai_chat/ui/chatView.css | 44 + front_end/panels/ai_chat/ui/input/InputBar.ts | 15 + .../ai_chat/ui/mcp/MCPConnectionsDialog.ts | 712 + .../ui/mcp/MCPConnectorsCatalogDialog.ts | 1428 + .../third_party/mcp-sdk/.tonic_example.js | 20 + front_end/third_party/mcp-sdk/BUILD.gn | 62 +- front_end/third_party/mcp-sdk/LICENSE | 7 +- .../third_party/mcp-sdk/MCPOAuthProvider.ts | 261 + front_end/third_party/mcp-sdk/README.chromium | 19 +- front_end/third_party/mcp-sdk/README.md | 1497 + .../mcp-sdk/ajv/.runkit_example.js | 23 - .../third_party/mcp-sdk/ajv/.tonic_example.js | 20 + front_end/third_party/mcp-sdk/ajv/LICENSE | 2 +- front_end/third_party/mcp-sdk/ajv/README.md | 1498 +- .../third_party/mcp-sdk/ajv/dist/2019.d.ts | 19 - .../third_party/mcp-sdk/ajv/dist/2019.js | 61 - .../third_party/mcp-sdk/ajv/dist/2019.js.map | 1 - .../third_party/mcp-sdk/ajv/dist/2020.d.ts | 19 - .../third_party/mcp-sdk/ajv/dist/2020.js | 55 - .../third_party/mcp-sdk/ajv/dist/2020.js.map | 1 - .../third_party/mcp-sdk/ajv/dist/ajv-esm.js | 9 + .../mcp-sdk/ajv/dist/ajv.bundle.js | 7189 ++ .../third_party/mcp-sdk/ajv/dist/ajv.d.ts | 19 +- front_end/third_party/mcp-sdk/ajv/dist/ajv.js | 51 +- .../third_party/mcp-sdk/ajv/dist/ajv.js.map | 1 - .../third_party/mcp-sdk/ajv/dist/ajv.min.js | 3 + .../mcp-sdk/ajv/dist/ajv.min.js.map | 1 + .../mcp-sdk/ajv/dist/cjs/client/auth.d.ts | 139 - .../mcp-sdk/ajv/dist/cjs/client/auth.d.ts.map | 1 - .../mcp-sdk/ajv/dist/cjs/client/auth.js | 340 - .../mcp-sdk/ajv/dist/cjs/client/auth.js.map | 1 - .../mcp-sdk/ajv/dist/cjs/client/index.d.ts | 942 - .../mcp-sdk/ajv/dist/cjs/client/index.js.map | 1 - .../mcp-sdk/ajv/dist/cjs/client/sse.d.ts.map | 1 - .../mcp-sdk/ajv/dist/cjs/client/sse.js.map | 1 - .../mcp-sdk/ajv/dist/cjs/client/stdio.js.map | 1 - .../dist/cjs/client/streamableHttp.d.ts.map | 1 - .../ajv/dist/cjs/client/streamableHttp.js.map | 1 - .../client/simpleStreamableHttp.js.map | 1 - .../server/demoInMemoryOAuthProvider.d.ts.map | 1 - .../server/demoInMemoryOAuthProvider.js.map | 1 - .../server/jsonResponseStreamableHttp.js.map | 1 - .../examples/server/simpleSseServer.js.map | 1 - .../simpleStatelessStreamableHttp.js.map | 1 - .../examples/server/simpleStreamableHttp.js | 338 - .../server/simpleStreamableHttp.js.map | 1 - ...seAndStreamableHttpCompatibleServer.js.map | 1 - .../ajv/dist/cjs/server/auth/errors.d.ts.map | 1 - .../ajv/dist/cjs/server/auth/errors.js.map | 1 - .../cjs/server/auth/handlers/authorize.js.map | 1 - .../cjs/server/auth/handlers/register.js.map | 1 - .../cjs/server/auth/handlers/revoke.js.map | 1 - .../cjs/server/auth/handlers/token.js.map | 1 - .../server/auth/middleware/bearerAuth.js.map | 1 - .../dist/cjs/server/auth/provider.d.ts.map | 1 - .../auth/providers/proxyProvider.d.ts.map | 1 - .../auth/providers/proxyProvider.js.map | 1 - .../ajv/dist/cjs/server/completable.d.ts.map | 1 - .../ajv/dist/cjs/server/completable.js.map | 1 - .../ajv/dist/cjs/server/index.d.ts.map | 1 - .../mcp-sdk/ajv/dist/cjs/server/index.js.map | 1 - .../mcp-sdk/ajv/dist/cjs/server/mcp.d.ts.map | 1 - .../mcp-sdk/ajv/dist/cjs/server/mcp.js.map | 1 - .../mcp-sdk/ajv/dist/cjs/server/sse.d.ts.map | 1 - .../mcp-sdk/ajv/dist/cjs/server/sse.js.map | 1 - .../dist/cjs/server/streamableHttp.d.ts.map | 1 - .../ajv/dist/cjs/server/streamableHttp.js.map | 1 - .../mcp-sdk/ajv/dist/cjs/shared/auth.d.ts | 321 - .../mcp-sdk/ajv/dist/cjs/shared/auth.d.ts.map | 1 - .../mcp-sdk/ajv/dist/cjs/shared/auth.js.map | 1 - .../ajv/dist/cjs/shared/protocol.d.ts.map | 1 - .../ajv/dist/cjs/shared/protocol.js.map | 1 - .../ajv/dist/cjs/shared/transport.d.ts.map | 1 - .../mcp-sdk/ajv/dist/cjs/types.d.ts | 31751 --------- .../mcp-sdk/ajv/dist/cjs/types.d.ts.map | 1 - .../mcp-sdk/ajv/dist/cjs/types.js.map | 1 - .../ajv/dist/compile/codegen/code.d.ts | 40 - .../mcp-sdk/ajv/dist/compile/codegen/code.js | 156 - .../ajv/dist/compile/codegen/code.js.map | 1 - .../ajv/dist/compile/codegen/index.d.ts | 79 - .../mcp-sdk/ajv/dist/compile/codegen/index.js | 697 - .../ajv/dist/compile/codegen/index.js.map | 1 - .../ajv/dist/compile/codegen/scope.d.ts | 79 - .../mcp-sdk/ajv/dist/compile/codegen/scope.js | 143 - .../ajv/dist/compile/codegen/scope.js.map | 1 - .../mcp-sdk/ajv/dist/compile/errors.d.ts | 13 - .../mcp-sdk/ajv/dist/compile/errors.js | 123 - .../mcp-sdk/ajv/dist/compile/errors.js.map | 1 - .../mcp-sdk/ajv/dist/compile/index.d.ts | 80 - .../mcp-sdk/ajv/dist/compile/index.js | 242 - .../mcp-sdk/ajv/dist/compile/index.js.map | 1 - .../mcp-sdk/ajv/dist/compile/jtd/parse.d.ts | 4 - .../mcp-sdk/ajv/dist/compile/jtd/parse.js | 350 - .../mcp-sdk/ajv/dist/compile/jtd/parse.js.map | 1 - .../ajv/dist/compile/jtd/serialize.d.ts | 4 - .../mcp-sdk/ajv/dist/compile/jtd/serialize.js | 229 - .../ajv/dist/compile/jtd/serialize.js.map | 1 - .../mcp-sdk/ajv/dist/compile/jtd/types.d.ts | 6 - .../mcp-sdk/ajv/dist/compile/jtd/types.js | 14 - .../mcp-sdk/ajv/dist/compile/jtd/types.js.map | 1 - .../mcp-sdk/ajv/dist/compile/names.d.ts | 20 - .../mcp-sdk/ajv/dist/compile/names.js | 28 - .../mcp-sdk/ajv/dist/compile/names.js.map | 1 - .../mcp-sdk/ajv/dist/compile/ref_error.d.ts | 6 - .../mcp-sdk/ajv/dist/compile/ref_error.js | 12 - .../mcp-sdk/ajv/dist/compile/ref_error.js.map | 1 - .../mcp-sdk/ajv/dist/compile/resolve.d.ts | 12 - .../mcp-sdk/ajv/dist/compile/resolve.js | 155 - .../mcp-sdk/ajv/dist/compile/resolve.js.map | 1 - .../mcp-sdk/ajv/dist/compile/rules.d.ts | 28 - .../mcp-sdk/ajv/dist/compile/rules.js | 26 - .../mcp-sdk/ajv/dist/compile/rules.js.map | 1 - .../mcp-sdk/ajv/dist/compile/util.d.ts | 40 - .../mcp-sdk/ajv/dist/compile/util.js | 178 - .../mcp-sdk/ajv/dist/compile/util.js.map | 1 - .../dist/compile/validate/applicability.d.ts | 6 - .../dist/compile/validate/applicability.js | 19 - .../compile/validate/applicability.js.map | 1 - .../ajv/dist/compile/validate/boolSchema.d.ts | 4 - .../ajv/dist/compile/validate/boolSchema.js | 50 - .../dist/compile/validate/boolSchema.js.map | 1 - .../ajv/dist/compile/validate/dataType.d.ts | 17 - .../ajv/dist/compile/validate/dataType.js | 203 - .../ajv/dist/compile/validate/dataType.js.map | 1 - .../ajv/dist/compile/validate/defaults.d.ts | 2 - .../ajv/dist/compile/validate/defaults.js | 35 - .../ajv/dist/compile/validate/defaults.js.map | 1 - .../ajv/dist/compile/validate/index.d.ts | 42 - .../ajv/dist/compile/validate/index.js | 520 - .../ajv/dist/compile/validate/index.js.map | 1 - .../ajv/dist/compile/validate/keyword.d.ts | 8 - .../ajv/dist/compile/validate/keyword.js | 124 - .../ajv/dist/compile/validate/keyword.js.map | 1 - .../ajv/dist/compile/validate/subschema.d.ts | 47 - .../ajv/dist/compile/validate/subschema.js | 81 - .../dist/compile/validate/subschema.js.map | 1 - .../third_party/mcp-sdk/ajv/dist/core.d.ts | 173 - .../third_party/mcp-sdk/ajv/dist/core.js | 618 - .../third_party/mcp-sdk/ajv/dist/core.js.map | 1 - .../mcp-sdk/ajv/dist/esm/client/auth.d.ts | 139 - .../mcp-sdk/ajv/dist/esm/client/auth.d.ts.map | 1 - .../mcp-sdk/ajv/dist/esm/client/auth.js | 325 - .../mcp-sdk/ajv/dist/esm/client/auth.js.map | 1 - .../mcp-sdk/ajv/dist/esm/client/index.d.ts | 942 - .../mcp-sdk/ajv/dist/esm/client/index.js.map | 1 - .../mcp-sdk/ajv/dist/esm/client/sse.d.ts.map | 1 - .../mcp-sdk/ajv/dist/esm/client/sse.js.map | 1 - .../mcp-sdk/ajv/dist/esm/client/stdio.js.map | 1 - .../dist/esm/client/streamableHttp.d.ts.map | 1 - .../ajv/dist/esm/client/streamableHttp.js.map | 1 - .../client/simpleStreamableHttp.js.map | 1 - .../server/demoInMemoryOAuthProvider.d.ts.map | 1 - .../server/demoInMemoryOAuthProvider.js.map | 1 - .../server/jsonResponseStreamableHttp.js.map | 1 - .../examples/server/simpleSseServer.js.map | 1 - .../simpleStatelessStreamableHttp.js.map | 1 - .../examples/server/simpleStreamableHttp.js | 333 - .../server/simpleStreamableHttp.js.map | 1 - ...seAndStreamableHttpCompatibleServer.js.map | 1 - .../ajv/dist/esm/server/auth/errors.d.ts.map | 1 - .../ajv/dist/esm/server/auth/errors.js.map | 1 - .../esm/server/auth/handlers/authorize.js.map | 1 - .../esm/server/auth/handlers/register.js.map | 1 - .../esm/server/auth/handlers/revoke.js.map | 1 - .../esm/server/auth/handlers/token.js.map | 1 - .../server/auth/middleware/bearerAuth.js.map | 1 - .../dist/esm/server/auth/provider.d.ts.map | 1 - .../auth/providers/proxyProvider.d.ts.map | 1 - .../auth/providers/proxyProvider.js.map | 1 - .../ajv/dist/esm/server/completable.d.ts.map | 1 - .../ajv/dist/esm/server/completable.js.map | 1 - .../ajv/dist/esm/server/index.d.ts.map | 1 - .../mcp-sdk/ajv/dist/esm/server/index.js.map | 1 - .../mcp-sdk/ajv/dist/esm/server/mcp.d.ts.map | 1 - .../mcp-sdk/ajv/dist/esm/server/mcp.js.map | 1 - .../mcp-sdk/ajv/dist/esm/server/sse.d.ts.map | 1 - .../mcp-sdk/ajv/dist/esm/server/sse.js.map | 1 - .../dist/esm/server/streamableHttp.d.ts.map | 1 - .../ajv/dist/esm/server/streamableHttp.js.map | 1 - .../mcp-sdk/ajv/dist/esm/shared/auth.d.ts | 321 - .../mcp-sdk/ajv/dist/esm/shared/auth.d.ts.map | 1 - .../mcp-sdk/ajv/dist/esm/shared/auth.js.map | 1 - .../ajv/dist/esm/shared/protocol.d.ts.map | 1 - .../ajv/dist/esm/shared/protocol.js.map | 1 - .../ajv/dist/esm/shared/transport.d.ts.map | 1 - .../mcp-sdk/ajv/dist/esm/types.d.ts | 31751 --------- .../mcp-sdk/ajv/dist/esm/types.d.ts.map | 1 - .../mcp-sdk/ajv/dist/esm/types.js.map | 1 - .../third_party/mcp-sdk/ajv/dist/jtd.d.ts | 47 - front_end/third_party/mcp-sdk/ajv/dist/jtd.js | 72 - .../third_party/mcp-sdk/ajv/dist/jtd.js.map | 1 - .../mcp-sdk/ajv/dist/refs/data.json | 13 - .../dist/refs/json-schema-2019-09/index.d.ts | 2 - .../dist/refs/json-schema-2019-09/index.js | 28 - .../refs/json-schema-2019-09/index.js.map | 1 - .../json-schema-2019-09/meta/applicator.json | 53 - .../json-schema-2019-09/meta/content.json | 17 - .../refs/json-schema-2019-09/meta/core.json | 57 - .../refs/json-schema-2019-09/meta/format.json | 14 - .../json-schema-2019-09/meta/meta-data.json | 37 - .../json-schema-2019-09/meta/validation.json | 90 - .../dist/refs/json-schema-2019-09/schema.json | 39 - .../dist/refs/json-schema-2020-12/index.d.ts | 2 - .../dist/refs/json-schema-2020-12/index.js | 30 - .../refs/json-schema-2020-12/index.js.map | 1 - .../json-schema-2020-12/meta/applicator.json | 48 - .../json-schema-2020-12/meta/content.json | 17 - .../refs/json-schema-2020-12/meta/core.json | 51 - .../meta/format-annotation.json | 14 - .../json-schema-2020-12/meta/meta-data.json | 37 - .../json-schema-2020-12/meta/unevaluated.json | 15 - .../json-schema-2020-12/meta/validation.json | 90 - .../dist/refs/json-schema-2020-12/schema.json | 55 - .../ajv/dist/refs/json-schema-draft-06.json | 137 - .../ajv/dist/refs/json-schema-draft-07.json | 151 - .../mcp-sdk/ajv/dist/refs/jtd-schema.d.ts | 3 - .../mcp-sdk/ajv/dist/refs/jtd-schema.js | 118 - .../mcp-sdk/ajv/dist/refs/jtd-schema.js.map | 1 - .../mcp-sdk/ajv/dist/runtime/equal.d.ts | 6 - .../mcp-sdk/ajv/dist/runtime/equal.js | 7 - .../mcp-sdk/ajv/dist/runtime/equal.js.map | 1 - .../mcp-sdk/ajv/dist/runtime/parseJson.d.ts | 18 - .../mcp-sdk/ajv/dist/runtime/parseJson.js | 185 - .../mcp-sdk/ajv/dist/runtime/parseJson.js.map | 1 - .../mcp-sdk/ajv/dist/runtime/quote.d.ts | 5 - .../mcp-sdk/ajv/dist/runtime/quote.js | 30 - .../mcp-sdk/ajv/dist/runtime/quote.js.map | 1 - .../mcp-sdk/ajv/dist/runtime/re2.d.ts | 6 - .../mcp-sdk/ajv/dist/runtime/re2.js | 6 - .../mcp-sdk/ajv/dist/runtime/re2.js.map | 1 - .../mcp-sdk/ajv/dist/runtime/timestamp.d.ts | 5 - .../mcp-sdk/ajv/dist/runtime/timestamp.js | 42 - .../mcp-sdk/ajv/dist/runtime/timestamp.js.map | 1 - .../mcp-sdk/ajv/dist/runtime/ucs2length.d.ts | 5 - .../mcp-sdk/ajv/dist/runtime/ucs2length.js | 24 - .../ajv/dist/runtime/ucs2length.js.map | 1 - .../mcp-sdk/ajv/dist/runtime/uri.d.ts | 6 - .../mcp-sdk/ajv/dist/runtime/uri.js | 6 - .../mcp-sdk/ajv/dist/runtime/uri.js.map | 1 - .../ajv/dist/runtime/validation_error.d.ts | 7 - .../ajv/dist/runtime/validation_error.js | 11 - .../ajv/dist/runtime/validation_error.js.map | 1 - .../mcp-sdk/ajv/dist/standalone/index.d.ts | 6 - .../mcp-sdk/ajv/dist/standalone/index.js | 90 - .../mcp-sdk/ajv/dist/standalone/index.js.map | 1 - .../mcp-sdk/ajv/dist/standalone/instance.d.ts | 12 - .../mcp-sdk/ajv/dist/standalone/instance.js | 35 - .../ajv/dist/standalone/instance.js.map | 1 - .../mcp-sdk/ajv/dist/types/index.d.ts | 183 - .../mcp-sdk/ajv/dist/types/index.js | 3 - .../mcp-sdk/ajv/dist/types/index.js.map | 1 - .../mcp-sdk/ajv/dist/types/json-schema.d.ts | 125 - .../mcp-sdk/ajv/dist/types/json-schema.js | 3 - .../mcp-sdk/ajv/dist/types/json-schema.js.map | 1 - .../mcp-sdk/ajv/dist/types/jtd-schema.d.ts | 174 - .../mcp-sdk/ajv/dist/types/jtd-schema.js | 3 - .../mcp-sdk/ajv/dist/types/jtd-schema.js.map | 1 - .../applicator/additionalItems.d.ts | 8 - .../applicator/additionalItems.js | 49 - .../applicator/additionalItems.js.map | 1 - .../applicator/additionalProperties.d.ts | 6 - .../applicator/additionalProperties.js | 106 - .../applicator/additionalProperties.js.map | 1 - .../dist/vocabularies/applicator/allOf.d.ts | 3 - .../ajv/dist/vocabularies/applicator/allOf.js | 23 - .../dist/vocabularies/applicator/allOf.js.map | 1 - .../dist/vocabularies/applicator/anyOf.d.ts | 4 - .../ajv/dist/vocabularies/applicator/anyOf.js | 12 - .../dist/vocabularies/applicator/anyOf.js.map | 1 - .../vocabularies/applicator/contains.d.ts | 7 - .../dist/vocabularies/applicator/contains.js | 95 - .../vocabularies/applicator/contains.js.map | 1 - .../vocabularies/applicator/dependencies.d.ts | 21 - .../vocabularies/applicator/dependencies.js | 85 - .../applicator/dependencies.js.map | 1 - .../applicator/dependentSchemas.d.ts | 3 - .../applicator/dependentSchemas.js | 11 - .../applicator/dependentSchemas.js.map | 1 - .../ajv/dist/vocabularies/applicator/if.d.ts | 6 - .../ajv/dist/vocabularies/applicator/if.js | 66 - .../dist/vocabularies/applicator/if.js.map | 1 - .../dist/vocabularies/applicator/index.d.ts | 13 - .../ajv/dist/vocabularies/applicator/index.js | 44 - .../dist/vocabularies/applicator/index.js.map | 1 - .../dist/vocabularies/applicator/items.d.ts | 5 - .../ajv/dist/vocabularies/applicator/items.js | 52 - .../dist/vocabularies/applicator/items.js.map | 1 - .../vocabularies/applicator/items2020.d.ts | 6 - .../dist/vocabularies/applicator/items2020.js | 30 - .../vocabularies/applicator/items2020.js.map | 1 - .../ajv/dist/vocabularies/applicator/not.d.ts | 4 - .../ajv/dist/vocabularies/applicator/not.js | 26 - .../dist/vocabularies/applicator/not.js.map | 1 - .../dist/vocabularies/applicator/oneOf.d.ts | 6 - .../ajv/dist/vocabularies/applicator/oneOf.js | 60 - .../dist/vocabularies/applicator/oneOf.js.map | 1 - .../applicator/patternProperties.d.ts | 3 - .../applicator/patternProperties.js | 75 - .../applicator/patternProperties.js.map | 1 - .../vocabularies/applicator/prefixItems.d.ts | 3 - .../vocabularies/applicator/prefixItems.js | 12 - .../applicator/prefixItems.js.map | 1 - .../vocabularies/applicator/properties.d.ts | 3 - .../vocabularies/applicator/properties.js | 54 - .../vocabularies/applicator/properties.js.map | 1 - .../applicator/propertyNames.d.ts | 6 - .../vocabularies/applicator/propertyNames.js | 38 - .../applicator/propertyNames.js.map | 1 - .../vocabularies/applicator/thenElse.d.ts | 3 - .../dist/vocabularies/applicator/thenElse.js | 13 - .../vocabularies/applicator/thenElse.js.map | 1 - .../mcp-sdk/ajv/dist/vocabularies/code.d.ts | 17 - .../mcp-sdk/ajv/dist/vocabularies/code.js | 131 - .../mcp-sdk/ajv/dist/vocabularies/code.js.map | 1 - .../ajv/dist/vocabularies/core/id.d.ts | 3 - .../mcp-sdk/ajv/dist/vocabularies/core/id.js | 10 - .../ajv/dist/vocabularies/core/id.js.map | 1 - .../ajv/dist/vocabularies/core/index.d.ts | 3 - .../ajv/dist/vocabularies/core/index.js | 16 - .../ajv/dist/vocabularies/core/index.js.map | 1 - .../ajv/dist/vocabularies/core/ref.d.ts | 8 - .../mcp-sdk/ajv/dist/vocabularies/core/ref.js | 122 - .../ajv/dist/vocabularies/core/ref.js.map | 1 - .../vocabularies/discriminator/index.d.ts | 5 - .../dist/vocabularies/discriminator/index.js | 104 - .../vocabularies/discriminator/index.js.map | 1 - .../vocabularies/discriminator/types.d.ts | 10 - .../dist/vocabularies/discriminator/types.js | 9 - .../vocabularies/discriminator/types.js.map | 1 - .../ajv/dist/vocabularies/draft2020.d.ts | 3 - .../ajv/dist/vocabularies/draft2020.js | 23 - .../ajv/dist/vocabularies/draft2020.js.map | 1 - .../mcp-sdk/ajv/dist/vocabularies/draft7.d.ts | 3 - .../mcp-sdk/ajv/dist/vocabularies/draft7.js | 17 - .../ajv/dist/vocabularies/draft7.js.map | 1 - .../vocabularies/dynamic/dynamicAnchor.d.ts | 5 - .../vocabularies/dynamic/dynamicAnchor.js | 30 - .../vocabularies/dynamic/dynamicAnchor.js.map | 1 - .../dist/vocabularies/dynamic/dynamicRef.d.ts | 5 - .../dist/vocabularies/dynamic/dynamicRef.js | 51 - .../vocabularies/dynamic/dynamicRef.js.map | 1 - .../ajv/dist/vocabularies/dynamic/index.d.ts | 3 - .../ajv/dist/vocabularies/dynamic/index.js | 9 - .../dist/vocabularies/dynamic/index.js.map | 1 - .../vocabularies/dynamic/recursiveAnchor.d.ts | 3 - .../vocabularies/dynamic/recursiveAnchor.js | 16 - .../dynamic/recursiveAnchor.js.map | 1 - .../vocabularies/dynamic/recursiveRef.d.ts | 3 - .../dist/vocabularies/dynamic/recursiveRef.js | 10 - .../vocabularies/dynamic/recursiveRef.js.map | 1 - .../mcp-sdk/ajv/dist/vocabularies/errors.d.ts | 9 - .../mcp-sdk/ajv/dist/vocabularies/errors.js | 3 - .../ajv/dist/vocabularies/errors.js.map | 1 - .../ajv/dist/vocabularies/format/format.d.ts | 8 - .../ajv/dist/vocabularies/format/format.js | 92 - .../dist/vocabularies/format/format.js.map | 1 - .../ajv/dist/vocabularies/format/index.d.ts | 3 - .../ajv/dist/vocabularies/format/index.js | 6 - .../ajv/dist/vocabularies/format/index.js.map | 1 - .../dist/vocabularies/jtd/discriminator.d.ts | 6 - .../dist/vocabularies/jtd/discriminator.js | 71 - .../vocabularies/jtd/discriminator.js.map | 1 - .../ajv/dist/vocabularies/jtd/elements.d.ts | 5 - .../ajv/dist/vocabularies/jtd/elements.js | 24 - .../ajv/dist/vocabularies/jtd/elements.js.map | 1 - .../ajv/dist/vocabularies/jtd/enum.d.ts | 6 - .../mcp-sdk/ajv/dist/vocabularies/jtd/enum.js | 43 - .../ajv/dist/vocabularies/jtd/enum.js.map | 1 - .../ajv/dist/vocabularies/jtd/error.d.ts | 9 - .../ajv/dist/vocabularies/jtd/error.js | 20 - .../ajv/dist/vocabularies/jtd/error.js.map | 1 - .../ajv/dist/vocabularies/jtd/index.d.ts | 10 - .../ajv/dist/vocabularies/jtd/index.js | 29 - .../ajv/dist/vocabularies/jtd/index.js.map | 1 - .../ajv/dist/vocabularies/jtd/metadata.d.ts | 5 - .../ajv/dist/vocabularies/jtd/metadata.js | 25 - .../ajv/dist/vocabularies/jtd/metadata.js.map | 1 - .../ajv/dist/vocabularies/jtd/nullable.d.ts | 4 - .../ajv/dist/vocabularies/jtd/nullable.js | 22 - .../ajv/dist/vocabularies/jtd/nullable.js.map | 1 - .../vocabularies/jtd/optionalProperties.d.ts | 3 - .../vocabularies/jtd/optionalProperties.js | 15 - .../jtd/optionalProperties.js.map | 1 - .../ajv/dist/vocabularies/jtd/properties.d.ts | 22 - .../ajv/dist/vocabularies/jtd/properties.js | 149 - .../dist/vocabularies/jtd/properties.js.map | 1 - .../ajv/dist/vocabularies/jtd/ref.d.ts | 4 - .../mcp-sdk/ajv/dist/vocabularies/jtd/ref.js | 67 - .../ajv/dist/vocabularies/jtd/ref.js.map | 1 - .../ajv/dist/vocabularies/jtd/type.d.ts | 10 - .../mcp-sdk/ajv/dist/vocabularies/jtd/type.js | 69 - .../ajv/dist/vocabularies/jtd/type.js.map | 1 - .../ajv/dist/vocabularies/jtd/union.d.ts | 3 - .../ajv/dist/vocabularies/jtd/union.js | 12 - .../ajv/dist/vocabularies/jtd/union.js.map | 1 - .../ajv/dist/vocabularies/jtd/values.d.ts | 5 - .../ajv/dist/vocabularies/jtd/values.js | 51 - .../ajv/dist/vocabularies/jtd/values.js.map | 1 - .../ajv/dist/vocabularies/metadata.d.ts | 3 - .../mcp-sdk/ajv/dist/vocabularies/metadata.js | 18 - .../ajv/dist/vocabularies/metadata.js.map | 1 - .../mcp-sdk/ajv/dist/vocabularies/next.d.ts | 3 - .../mcp-sdk/ajv/dist/vocabularies/next.js | 8 - .../mcp-sdk/ajv/dist/vocabularies/next.js.map | 1 - .../dist/vocabularies/unevaluated/index.d.ts | 3 - .../dist/vocabularies/unevaluated/index.js | 7 - .../vocabularies/unevaluated/index.js.map | 1 - .../unevaluated/unevaluatedItems.d.ts | 6 - .../unevaluated/unevaluatedItems.js | 40 - .../unevaluated/unevaluatedItems.js.map | 1 - .../unevaluated/unevaluatedProperties.d.ts | 6 - .../unevaluated/unevaluatedProperties.js | 65 - .../unevaluated/unevaluatedProperties.js.map | 1 - .../dist/vocabularies/validation/const.d.ts | 6 - .../ajv/dist/vocabularies/validation/const.js | 25 - .../dist/vocabularies/validation/const.js.map | 1 - .../validation/dependentRequired.d.ts | 5 - .../validation/dependentRequired.js | 12 - .../validation/dependentRequired.js.map | 1 - .../dist/vocabularies/validation/enum.d.ts | 8 - .../ajv/dist/vocabularies/validation/enum.js | 48 - .../dist/vocabularies/validation/enum.js.map | 1 - .../dist/vocabularies/validation/index.d.ts | 16 - .../ajv/dist/vocabularies/validation/index.js | 33 - .../dist/vocabularies/validation/index.js.map | 1 - .../validation/limitContains.d.ts | 3 - .../vocabularies/validation/limitContains.js | 15 - .../validation/limitContains.js.map | 1 - .../vocabularies/validation/limitItems.d.ts | 3 - .../vocabularies/validation/limitItems.js | 24 - .../vocabularies/validation/limitItems.js.map | 1 - .../vocabularies/validation/limitLength.d.ts | 3 - .../vocabularies/validation/limitLength.js | 27 - .../validation/limitLength.js.map | 1 - .../vocabularies/validation/limitNumber.d.ts | 11 - .../vocabularies/validation/limitNumber.js | 27 - .../validation/limitNumber.js.map | 1 - .../validation/limitProperties.d.ts | 3 - .../validation/limitProperties.js | 24 - .../validation/limitProperties.js.map | 1 - .../vocabularies/validation/multipleOf.d.ts | 8 - .../vocabularies/validation/multipleOf.js | 26 - .../vocabularies/validation/multipleOf.js.map | 1 - .../dist/vocabularies/validation/pattern.d.ts | 8 - .../dist/vocabularies/validation/pattern.js | 24 - .../vocabularies/validation/pattern.js.map | 1 - .../vocabularies/validation/required.d.ts | 8 - .../dist/vocabularies/validation/required.js | 79 - .../vocabularies/validation/required.js.map | 1 - .../vocabularies/validation/uniqueItems.d.ts | 9 - .../vocabularies/validation/uniqueItems.js | 64 - .../validation/uniqueItems.js.map | 1 - front_end/third_party/mcp-sdk/ajv/lib/2019.ts | 81 - front_end/third_party/mcp-sdk/ajv/lib/2020.ts | 75 - .../third_party/mcp-sdk/ajv/lib/ajv.d.ts | 397 + front_end/third_party/mcp-sdk/ajv/lib/ajv.js | 506 + front_end/third_party/mcp-sdk/ajv/lib/ajv.ts | 70 - .../third_party/mcp-sdk/ajv/lib/cache.js | 26 + .../mcp-sdk/ajv/lib/compile/async.js | 90 + .../mcp-sdk/ajv/lib/compile/codegen/code.ts | 169 - .../mcp-sdk/ajv/lib/compile/codegen/index.ts | 852 - .../mcp-sdk/ajv/lib/compile/codegen/scope.ts | 215 - .../mcp-sdk/ajv/lib/compile/equal.js | 5 + .../mcp-sdk/ajv/lib/compile/error_classes.js | 34 + .../mcp-sdk/ajv/lib/compile/errors.ts | 184 - .../mcp-sdk/ajv/lib/compile/formats.js | 142 + .../mcp-sdk/ajv/lib/compile/index.js | 387 + .../mcp-sdk/ajv/lib/compile/index.ts | 324 - .../mcp-sdk/ajv/lib/compile/jtd/parse.ts | 411 - .../mcp-sdk/ajv/lib/compile/jtd/serialize.ts | 266 - .../mcp-sdk/ajv/lib/compile/jtd/types.ts | 16 - .../mcp-sdk/ajv/lib/compile/names.ts | 27 - .../mcp-sdk/ajv/lib/compile/ref_error.ts | 13 - .../mcp-sdk/ajv/lib/compile/resolve.js | 270 + .../mcp-sdk/ajv/lib/compile/resolve.ts | 149 - .../mcp-sdk/ajv/lib/compile/rules.js | 66 + .../mcp-sdk/ajv/lib/compile/rules.ts | 50 - .../mcp-sdk/ajv/lib/compile/schema_obj.js | 9 + .../mcp-sdk/ajv/lib/compile/ucs2length.js | 20 + .../mcp-sdk/ajv/lib/compile/util.js | 239 + .../mcp-sdk/ajv/lib/compile/util.ts | 213 - .../ajv/lib/compile/validate/applicability.ts | 22 - .../ajv/lib/compile/validate/boolSchema.ts | 47 - .../ajv/lib/compile/validate/dataType.ts | 230 - .../ajv/lib/compile/validate/defaults.ts | 32 - .../mcp-sdk/ajv/lib/compile/validate/index.ts | 582 - .../ajv/lib/compile/validate/keyword.ts | 171 - .../ajv/lib/compile/validate/subschema.ts | 135 - front_end/third_party/mcp-sdk/ajv/lib/core.ts | 891 - front_end/third_party/mcp-sdk/ajv/lib/data.js | 49 + .../mcp-sdk/ajv/lib/definition_schema.js | 37 + .../mcp-sdk/ajv/lib/dot/_limit.jst | 113 + .../mcp-sdk/ajv/lib/dot/_limitItems.jst | 12 + .../mcp-sdk/ajv/lib/dot/_limitLength.jst | 12 + .../mcp-sdk/ajv/lib/dot/_limitProperties.jst | 12 + .../third_party/mcp-sdk/ajv/lib/dot/allOf.jst | 32 + .../third_party/mcp-sdk/ajv/lib/dot/anyOf.jst | 46 + .../mcp-sdk/ajv/lib/dot/coerce.def | 51 + .../mcp-sdk/ajv/lib/dot/comment.jst | 9 + .../third_party/mcp-sdk/ajv/lib/dot/const.jst | 11 + .../mcp-sdk/ajv/lib/dot/contains.jst | 55 + .../mcp-sdk/ajv/lib/dot/custom.jst | 191 + .../mcp-sdk/ajv/lib/dot/defaults.def | 47 + .../mcp-sdk/ajv/lib/dot/definitions.def | 203 + .../mcp-sdk/ajv/lib/dot/dependencies.jst | 79 + .../third_party/mcp-sdk/ajv/lib/dot/enum.jst | 30 + .../mcp-sdk/ajv/lib/dot/errors.def | 194 + .../mcp-sdk/ajv/lib/dot/format.jst | 106 + .../third_party/mcp-sdk/ajv/lib/dot/if.jst | 73 + .../third_party/mcp-sdk/ajv/lib/dot/items.jst | 98 + .../mcp-sdk/ajv/lib/dot/missing.def | 39 + .../mcp-sdk/ajv/lib/dot/multipleOf.jst | 22 + .../third_party/mcp-sdk/ajv/lib/dot/not.jst | 43 + .../third_party/mcp-sdk/ajv/lib/dot/oneOf.jst | 54 + .../mcp-sdk/ajv/lib/dot/pattern.jst | 14 + .../mcp-sdk/ajv/lib/dot/properties.jst | 245 + .../mcp-sdk/ajv/lib/dot/propertyNames.jst | 52 + .../third_party/mcp-sdk/ajv/lib/dot/ref.jst | 85 + .../mcp-sdk/ajv/lib/dot/required.jst | 108 + .../mcp-sdk/ajv/lib/dot/uniqueItems.jst | 62 + .../mcp-sdk/ajv/lib/dot/validate.jst | 276 + .../mcp-sdk/ajv/lib/dotjs/README.md | 3 + .../mcp-sdk/ajv/lib/dotjs/_limit.js | 163 + .../mcp-sdk/ajv/lib/dotjs/_limitItems.js | 80 + .../mcp-sdk/ajv/lib/dotjs/_limitLength.js | 85 + .../mcp-sdk/ajv/lib/dotjs/_limitProperties.js | 80 + .../mcp-sdk/ajv/lib/dotjs/allOf.js | 42 + .../mcp-sdk/ajv/lib/dotjs/anyOf.js | 73 + .../mcp-sdk/ajv/lib/dotjs/comment.js | 14 + .../mcp-sdk/ajv/lib/dotjs/const.js | 56 + .../mcp-sdk/ajv/lib/dotjs/contains.js | 81 + .../mcp-sdk/ajv/lib/dotjs/custom.js | 228 + .../mcp-sdk/ajv/lib/dotjs/dependencies.js | 168 + .../third_party/mcp-sdk/ajv/lib/dotjs/enum.js | 66 + .../mcp-sdk/ajv/lib/dotjs/format.js | 150 + .../third_party/mcp-sdk/ajv/lib/dotjs/if.js | 103 + .../mcp-sdk/ajv/lib/dotjs/index.js | 33 + .../mcp-sdk/ajv/lib/dotjs/items.js | 140 + .../mcp-sdk/ajv/lib/dotjs/multipleOf.js | 80 + .../third_party/mcp-sdk/ajv/lib/dotjs/not.js | 84 + .../mcp-sdk/ajv/lib/dotjs/oneOf.js | 73 + .../mcp-sdk/ajv/lib/dotjs/pattern.js | 75 + .../mcp-sdk/ajv/lib/dotjs/properties.js | 335 + .../mcp-sdk/ajv/lib/dotjs/propertyNames.js | 81 + .../third_party/mcp-sdk/ajv/lib/dotjs/ref.js | 124 + .../mcp-sdk/ajv/lib/dotjs/required.js | 270 + .../mcp-sdk/ajv/lib/dotjs/uniqueItems.js | 86 + .../mcp-sdk/ajv/lib/dotjs/validate.js | 482 + front_end/third_party/mcp-sdk/ajv/lib/jtd.ts | 132 - .../third_party/mcp-sdk/ajv/lib/keyword.js | 146 + .../mcp-sdk/ajv/lib/refs/data.json | 26 +- .../ajv/lib/refs/json-schema-2019-09/index.ts | 28 - .../json-schema-2019-09/meta/applicator.json | 53 - .../json-schema-2019-09/meta/content.json | 17 - .../refs/json-schema-2019-09/meta/core.json | 57 - .../refs/json-schema-2019-09/meta/format.json | 14 - .../json-schema-2019-09/meta/meta-data.json | 37 - .../json-schema-2019-09/meta/validation.json | 90 - .../lib/refs/json-schema-2019-09/schema.json | 39 - .../ajv/lib/refs/json-schema-2020-12/index.ts | 30 - .../json-schema-2020-12/meta/applicator.json | 48 - .../json-schema-2020-12/meta/content.json | 17 - .../refs/json-schema-2020-12/meta/core.json | 51 - .../meta/format-annotation.json | 14 - .../json-schema-2020-12/meta/meta-data.json | 37 - .../json-schema-2020-12/meta/unevaluated.json | 15 - .../json-schema-2020-12/meta/validation.json | 90 - .../lib/refs/json-schema-2020-12/schema.json | 55 - .../ajv/lib/refs/json-schema-draft-04.json | 149 + .../ajv/lib/refs/json-schema-draft-06.json | 281 +- .../ajv/lib/refs/json-schema-draft-07.json | 309 +- .../ajv/lib/refs/json-schema-secure.json | 12 +- .../mcp-sdk/ajv/lib/refs/jtd-schema.ts | 130 - .../mcp-sdk/ajv/lib/runtime/equal.ts | 7 - .../mcp-sdk/ajv/lib/runtime/parseJson.ts | 177 - .../mcp-sdk/ajv/lib/runtime/quote.ts | 31 - .../mcp-sdk/ajv/lib/runtime/re2.ts | 6 - .../mcp-sdk/ajv/lib/runtime/timestamp.ts | 46 - .../mcp-sdk/ajv/lib/runtime/ucs2length.ts | 20 - .../mcp-sdk/ajv/lib/runtime/uri.ts | 6 - .../ajv/lib/runtime/validation_error.ts | 13 - .../mcp-sdk/ajv/lib/standalone/index.ts | 100 - .../mcp-sdk/ajv/lib/standalone/instance.ts | 36 - .../mcp-sdk/ajv/lib/types/index.ts | 244 - .../mcp-sdk/ajv/lib/types/json-schema.ts | 187 - .../mcp-sdk/ajv/lib/types/jtd-schema.ts | 273 - .../applicator/additionalItems.ts | 56 - .../applicator/additionalProperties.ts | 118 - .../ajv/lib/vocabularies/applicator/allOf.ts | 22 - .../ajv/lib/vocabularies/applicator/anyOf.ts | 14 - .../lib/vocabularies/applicator/contains.ts | 109 - .../vocabularies/applicator/dependencies.ts | 112 - .../applicator/dependentSchemas.ts | 11 - .../ajv/lib/vocabularies/applicator/if.ts | 80 - .../ajv/lib/vocabularies/applicator/index.ts | 53 - .../ajv/lib/vocabularies/applicator/items.ts | 59 - .../lib/vocabularies/applicator/items2020.ts | 36 - .../ajv/lib/vocabularies/applicator/not.ts | 38 - .../ajv/lib/vocabularies/applicator/oneOf.ts | 82 - .../applicator/patternProperties.ts | 91 - .../vocabularies/applicator/prefixItems.ts | 12 - .../lib/vocabularies/applicator/properties.ts | 57 - .../vocabularies/applicator/propertyNames.ts | 50 - .../lib/vocabularies/applicator/thenElse.ts | 13 - .../mcp-sdk/ajv/lib/vocabularies/code.ts | 168 - .../mcp-sdk/ajv/lib/vocabularies/core/id.ts | 10 - .../ajv/lib/vocabularies/core/index.ts | 16 - .../mcp-sdk/ajv/lib/vocabularies/core/ref.ts | 129 - .../lib/vocabularies/discriminator/index.ts | 113 - .../lib/vocabularies/discriminator/types.ts | 12 - .../mcp-sdk/ajv/lib/vocabularies/draft2020.ts | 23 - .../mcp-sdk/ajv/lib/vocabularies/draft7.ts | 17 - .../lib/vocabularies/dynamic/dynamicAnchor.ts | 31 - .../lib/vocabularies/dynamic/dynamicRef.ts | 51 - .../ajv/lib/vocabularies/dynamic/index.ts | 9 - .../vocabularies/dynamic/recursiveAnchor.ts | 14 - .../lib/vocabularies/dynamic/recursiveRef.ts | 10 - .../mcp-sdk/ajv/lib/vocabularies/errors.ts | 18 - .../ajv/lib/vocabularies/format/format.ts | 120 - .../ajv/lib/vocabularies/format/index.ts | 6 - .../ajv/lib/vocabularies/jtd/discriminator.ts | 89 - .../ajv/lib/vocabularies/jtd/elements.ts | 32 - .../mcp-sdk/ajv/lib/vocabularies/jtd/enum.ts | 45 - .../mcp-sdk/ajv/lib/vocabularies/jtd/error.ts | 23 - .../mcp-sdk/ajv/lib/vocabularies/jtd/index.ts | 37 - .../ajv/lib/vocabularies/jtd/metadata.ts | 24 - .../ajv/lib/vocabularies/jtd/nullable.ts | 21 - .../vocabularies/jtd/optionalProperties.ts | 15 - .../ajv/lib/vocabularies/jtd/properties.ts | 184 - .../mcp-sdk/ajv/lib/vocabularies/jtd/ref.ts | 76 - .../mcp-sdk/ajv/lib/vocabularies/jtd/type.ts | 75 - .../mcp-sdk/ajv/lib/vocabularies/jtd/union.ts | 12 - .../ajv/lib/vocabularies/jtd/values.ts | 58 - .../mcp-sdk/ajv/lib/vocabularies/metadata.ts | 17 - .../mcp-sdk/ajv/lib/vocabularies/next.ts | 8 - .../ajv/lib/vocabularies/unevaluated/index.ts | 7 - .../unevaluated/unevaluatedItems.ts | 47 - .../unevaluated/unevaluatedProperties.ts | 85 - .../ajv/lib/vocabularies/validation/const.ts | 28 - .../validation/dependentRequired.ts | 23 - .../ajv/lib/vocabularies/validation/enum.ts | 54 - .../ajv/lib/vocabularies/validation/index.ts | 49 - .../vocabularies/validation/limitContains.ts | 16 - .../lib/vocabularies/validation/limitItems.ts | 26 - .../vocabularies/validation/limitLength.ts | 30 - .../vocabularies/validation/limitNumber.ts | 42 - .../validation/limitProperties.ts | 26 - .../lib/vocabularies/validation/multipleOf.ts | 34 - .../lib/vocabularies/validation/pattern.ts | 28 - .../lib/vocabularies/validation/required.ts | 98 - .../vocabularies/validation/uniqueItems.ts | 79 - .../third_party/mcp-sdk/ajv/package.json | 136 +- .../mcp-sdk/ajv/scripts/.eslintrc.yml | 3 + .../third_party/mcp-sdk/ajv/scripts/bundle.js | 61 + .../mcp-sdk/ajv/scripts/compile-dots.js | 73 + .../third_party/mcp-sdk/ajv/scripts/info | 10 + .../mcp-sdk/ajv/scripts/prepare-tests | 12 + .../mcp-sdk/ajv/scripts/publish-built-version | 32 + .../mcp-sdk/ajv/scripts/travis-gh-pages | 23 + .../third_party/mcp-sdk/dist/ajv.bundle.js | 7189 ++ front_end/third_party/mcp-sdk/dist/ajv.min.js | 3 + .../third_party/mcp-sdk/dist/ajv.min.js.map | 1 + .../mcp-sdk/{ajv => }/dist/cjs/cli.d.ts | 0 .../mcp-sdk/{ajv => }/dist/cjs/cli.d.ts.map | 0 .../mcp-sdk/{ajv => }/dist/cjs/cli.js | 6 +- .../mcp-sdk/{ajv => }/dist/cjs/cli.js.map | 2 +- .../mcp-sdk/dist/cjs/client/auth.d.ts | 248 + .../mcp-sdk/dist/cjs/client/auth.d.ts.map | 1 + .../mcp-sdk/dist/cjs/client/auth.js | 718 + .../mcp-sdk/dist/cjs/client/auth.js.map | 1 + .../mcp-sdk/dist/cjs/client/index.d.ts | 1497 + .../{ajv => }/dist/cjs/client/index.d.ts.map | 2 +- .../{ajv => }/dist/cjs/client/index.js | 13 +- .../mcp-sdk/dist/cjs/client/index.js.map | 1 + .../mcp-sdk/dist/cjs/client/middleware.d.ts | 169 + .../dist/cjs/client/middleware.d.ts.map | 1 + .../mcp-sdk/dist/cjs/client/middleware.js | 256 + .../mcp-sdk/dist/cjs/client/middleware.js.map | 1 + .../{ajv => }/dist/cjs/client/sse.d.ts | 9 +- .../mcp-sdk/dist/cjs/client/sse.d.ts.map | 1 + .../mcp-sdk/{ajv => }/dist/cjs/client/sse.js | 47 +- .../mcp-sdk/dist/cjs/client/sse.js.map | 1 + .../dist/esm => dist/cjs}/client/stdio.d.ts | 6 + .../{ajv => }/dist/cjs/client/stdio.d.ts.map | 2 +- .../{ajv => }/dist/cjs/client/stdio.js | 26 +- .../mcp-sdk/dist/cjs/client/stdio.js.map | 1 + .../dist/cjs/client/streamableHttp.d.ts | 33 +- .../dist/cjs/client/streamableHttp.d.ts.map | 1 + .../dist/cjs/client/streamableHttp.js | 85 +- .../dist/cjs/client/streamableHttp.js.map | 1 + .../{ajv => }/dist/cjs/client/websocket.d.ts | 0 .../dist/cjs/client/websocket.d.ts.map | 0 .../{ajv => }/dist/cjs/client/websocket.js | 0 .../dist/cjs/client/websocket.js.map | 0 .../client/multipleClientsParallel.d.ts | 0 .../client/multipleClientsParallel.d.ts.map | 0 .../client/multipleClientsParallel.js | 0 .../client/multipleClientsParallel.js.map | 0 .../client/parallelToolCallsClient.d.ts | 0 .../client/parallelToolCallsClient.d.ts.map | 0 .../client/parallelToolCallsClient.js | 0 .../client/parallelToolCallsClient.js.map | 0 .../examples/client/simpleOAuthClient.d.ts | 0 .../client/simpleOAuthClient.d.ts.map | 0 .../cjs/examples/client/simpleOAuthClient.js | 4 +- .../examples/client/simpleOAuthClient.js.map | 2 +- .../examples/client/simpleStreamableHttp.d.ts | 0 .../client/simpleStreamableHttp.d.ts.map | 0 .../examples/client/simpleStreamableHttp.js | 266 +- .../client/simpleStreamableHttp.js.map | 1 + .../streamableHttpWithSseFallbackClient.d.ts | 0 ...reamableHttpWithSseFallbackClient.d.ts.map | 0 .../streamableHttpWithSseFallbackClient.js | 0 ...streamableHttpWithSseFallbackClient.js.map | 0 .../server/demoInMemoryOAuthProvider.d.ts | 16 +- .../server/demoInMemoryOAuthProvider.d.ts.map | 1 + .../server/demoInMemoryOAuthProvider.js | 34 +- .../server/demoInMemoryOAuthProvider.js.map | 1 + .../server/jsonResponseStreamableHttp.d.ts | 0 .../jsonResponseStreamableHttp.d.ts.map | 0 .../server/jsonResponseStreamableHttp.js | 38 +- .../server/jsonResponseStreamableHttp.js.map | 1 + .../server/mcpServerOutputSchema.d.ts | 0 .../server/mcpServerOutputSchema.d.ts.map | 0 .../examples/server/mcpServerOutputSchema.js | 0 .../server/mcpServerOutputSchema.js.map | 0 .../cjs/examples/server/simpleSseServer.d.ts | 0 .../examples/server/simpleSseServer.d.ts.map | 0 .../cjs/examples/server/simpleSseServer.js | 30 +- .../examples/server/simpleSseServer.js.map | 1 + .../server/simpleStatelessStreamableHttp.d.ts | 0 .../simpleStatelessStreamableHttp.d.ts.map | 0 .../server/simpleStatelessStreamableHttp.js | 25 +- .../simpleStatelessStreamableHttp.js.map | 1 + .../examples/server/simpleStreamableHttp.d.ts | 0 .../server/simpleStreamableHttp.d.ts.map | 0 .../examples/server/simpleStreamableHttp.js | 587 + .../server/simpleStreamableHttp.js.map | 1 + .../sseAndStreamableHttpCompatibleServer.d.ts | 0 ...AndStreamableHttpCompatibleServer.d.ts.map | 0 .../sseAndStreamableHttpCompatibleServer.js | 25 +- ...seAndStreamableHttpCompatibleServer.js.map | 1 + .../standaloneSseWithGetStreamableHttp.d.ts | 0 ...tandaloneSseWithGetStreamableHttp.d.ts.map | 0 .../standaloneSseWithGetStreamableHttp.js | 6 +- .../standaloneSseWithGetStreamableHttp.js.map | 2 +- .../examples/server/toolWithSampleServer.d.ts | 2 + .../server/toolWithSampleServer.d.ts.map | 1 + .../examples/server/toolWithSampleServer.js | 49 + .../server/toolWithSampleServer.js.map | 1 + .../examples/shared/inMemoryEventStore.d.ts | 0 .../shared/inMemoryEventStore.d.ts.map | 0 .../cjs/examples/shared/inMemoryEventStore.js | 0 .../examples/shared/inMemoryEventStore.js.map | 0 .../mcp-sdk/{ajv => }/dist/cjs/inMemory.d.ts | 0 .../{ajv => }/dist/cjs/inMemory.d.ts.map | 0 .../mcp-sdk/{ajv => }/dist/cjs/inMemory.js | 0 .../{ajv => }/dist/cjs/inMemory.js.map | 0 .../mcp-sdk/{ajv => }/dist/cjs/package.json | 0 .../esm => dist/cjs}/server/auth/clients.d.ts | 2 +- .../dist/cjs/server/auth/clients.d.ts.map | 2 +- .../{ajv => }/dist/cjs/server/auth/clients.js | 0 .../dist/cjs/server/auth/clients.js.map | 0 .../esm => dist/cjs}/server/auth/errors.d.ts | 51 +- .../dist/cjs/server/auth/errors.d.ts.map | 1 + .../{ajv => }/dist/cjs/server/auth/errors.js | 106 +- .../dist/cjs/server/auth/errors.js.map | 1 + .../cjs/server/auth/handlers/authorize.d.ts | 0 .../server/auth/handlers/authorize.d.ts.map | 2 +- .../cjs/server/auth/handlers/authorize.js | 6 +- .../cjs/server/auth/handlers/authorize.js.map | 1 + .../cjs/server/auth/handlers/metadata.d.ts | 0 .../server/auth/handlers/metadata.d.ts.map | 0 .../dist/cjs/server/auth/handlers/metadata.js | 0 .../cjs/server/auth/handlers/metadata.js.map | 0 .../cjs/server/auth/handlers/register.d.ts | 8 +- .../server/auth/handlers/register.d.ts.map | 2 +- .../dist/cjs/server/auth/handlers/register.js | 10 +- .../cjs/server/auth/handlers/register.js.map | 1 + .../dist/cjs/server/auth/handlers/revoke.d.ts | 2 +- .../cjs/server/auth/handlers/revoke.d.ts.map | 2 +- .../dist/cjs/server/auth/handlers/revoke.js | 10 +- .../cjs/server/auth/handlers/revoke.js.map | 1 + .../dist/cjs/server/auth/handlers/token.d.ts | 0 .../cjs/server/auth/handlers/token.d.ts.map | 2 +- .../dist/cjs/server/auth/handlers/token.js | 12 +- .../cjs/server/auth/handlers/token.js.map | 1 + .../auth/middleware/allowedMethods.d.ts | 0 .../auth/middleware/allowedMethods.d.ts.map | 0 .../server/auth/middleware/allowedMethods.js | 0 .../auth/middleware/allowedMethods.js.map | 0 .../server/auth/middleware/bearerAuth.d.ts | 0 .../auth/middleware/bearerAuth.d.ts.map | 2 +- .../cjs/server/auth/middleware/bearerAuth.js | 8 +- .../server/auth/middleware/bearerAuth.js.map | 1 + .../server/auth/middleware/clientAuth.d.ts | 0 .../auth/middleware/clientAuth.d.ts.map | 2 +- .../cjs/server/auth/middleware/clientAuth.js | 1 - .../server/auth/middleware/clientAuth.js.map | 2 +- .../cjs}/server/auth/provider.d.ts | 5 +- .../dist/cjs/server/auth/provider.d.ts.map | 1 + .../dist/cjs/server/auth/provider.js | 0 .../dist/cjs/server/auth/provider.js.map | 0 .../server/auth/providers/proxyProvider.d.ts | 10 +- .../auth/providers/proxyProvider.d.ts.map | 1 + .../server/auth/providers/proxyProvider.js | 25 +- .../auth/providers/proxyProvider.js.map | 1 + .../dist/cjs/server/auth/router.d.ts | 0 .../dist/cjs/server/auth/router.d.ts.map | 0 .../{ajv => }/dist/cjs/server/auth/router.js | 2 +- .../dist/cjs/server/auth/router.js.map | 2 +- .../{ajv => }/dist/cjs/server/auth/types.d.ts | 5 + .../dist/cjs/server/auth/types.d.ts.map | 2 +- .../{ajv => }/dist/cjs/server/auth/types.js | 0 .../dist/cjs/server/auth/types.js.map | 0 .../dist/cjs/server/completable.d.ts | 4 +- .../dist/cjs/server/completable.d.ts.map | 1 + .../{ajv => }/dist/cjs/server/completable.js | 0 .../dist/cjs/server/completable.js.map | 1 + .../{ajv => }/dist/cjs/server/index.d.ts | 39 +- .../mcp-sdk/dist/cjs/server/index.d.ts.map | 1 + .../{ajv => }/dist/cjs/server/index.js | 79 +- .../mcp-sdk/dist/cjs/server/index.js.map | 1 + .../dist/esm => dist/cjs}/server/mcp.d.ts | 43 +- .../mcp-sdk/dist/cjs/server/mcp.d.ts.map | 1 + .../mcp-sdk/{ajv => }/dist/cjs/server/mcp.js | 244 +- .../mcp-sdk/dist/cjs/server/mcp.js.map | 1 + .../dist/esm => dist/cjs}/server/sse.d.ts | 38 +- .../mcp-sdk/dist/cjs/server/sse.d.ts.map | 1 + .../mcp-sdk/{ajv => }/dist/cjs/server/sse.js | 50 +- .../mcp-sdk/dist/cjs/server/sse.js.map | 1 + .../{ajv => }/dist/cjs/server/stdio.d.ts | 0 .../{ajv => }/dist/cjs/server/stdio.d.ts.map | 0 .../{ajv => }/dist/cjs/server/stdio.js | 0 .../{ajv => }/dist/cjs/server/stdio.js.map | 0 .../cjs}/server/streamableHttp.d.ts | 46 +- .../dist/cjs/server/streamableHttp.d.ts.map | 1 + .../dist/cjs/server/streamableHttp.js | 96 +- .../dist/cjs/server/streamableHttp.js.map | 1 + .../mcp-sdk/dist/cjs/shared/auth-utils.d.ts | 23 + .../dist/cjs/shared/auth-utils.d.ts.map | 1 + .../mcp-sdk/dist/cjs/shared/auth-utils.js | 48 + .../mcp-sdk/dist/cjs/shared/auth-utils.js.map | 1 + .../mcp-sdk/dist/cjs/shared/auth.d.ts | 621 + .../mcp-sdk/dist/cjs/shared/auth.d.ts.map | 1 + .../mcp-sdk/{ajv => }/dist/cjs/shared/auth.js | 100 +- .../mcp-sdk/dist/cjs/shared/auth.js.map | 1 + .../dist/cjs/shared/metadataUtils.d.ts | 12 + .../dist/cjs/shared/metadataUtils.d.ts.map | 1 + .../mcp-sdk/dist/cjs/shared/metadataUtils.js | 29 + .../dist/cjs/shared/metadataUtils.js.map | 1 + .../{ajv => }/dist/cjs/shared/protocol.d.ts | 16 +- .../mcp-sdk/dist/cjs/shared/protocol.d.ts.map | 1 + .../{ajv => }/dist/cjs/shared/protocol.js | 64 +- .../mcp-sdk/dist/cjs/shared/protocol.js.map | 1 + .../{ajv => }/dist/cjs/shared/stdio.d.ts | 0 .../{ajv => }/dist/cjs/shared/stdio.d.ts.map | 0 .../{ajv => }/dist/cjs/shared/stdio.js | 0 .../{ajv => }/dist/cjs/shared/stdio.js.map | 0 .../{ajv => }/dist/cjs/shared/transport.d.ts | 15 +- .../dist/cjs/shared/transport.d.ts.map | 1 + .../{ajv => }/dist/cjs/shared/transport.js | 0 .../dist/cjs/shared/transport.js.map | 0 .../dist/cjs/shared/uriTemplate.d.ts | 0 .../dist/cjs/shared/uriTemplate.d.ts.map | 0 .../{ajv => }/dist/cjs/shared/uriTemplate.js | 0 .../dist/cjs/shared/uriTemplate.js.map | 0 .../third_party/mcp-sdk/dist/cjs/types.d.ts | 54852 ++++++++++++++++ .../mcp-sdk/dist/cjs/types.d.ts.map | 1 + .../mcp-sdk/{ajv => }/dist/cjs/types.js | 345 +- .../third_party/mcp-sdk/dist/cjs/types.js.map | 1 + .../mcp-sdk/{ajv => }/dist/esm/cli.d.ts | 0 .../mcp-sdk/{ajv => }/dist/esm/cli.d.ts.map | 0 .../mcp-sdk/{ajv => }/dist/esm/cli.js | 6 +- .../mcp-sdk/{ajv => }/dist/esm/cli.js.map | 2 +- .../mcp-sdk/dist/esm/client/auth.d.ts | 248 + .../mcp-sdk/dist/esm/client/auth.d.ts.map | 1 + .../mcp-sdk/dist/esm/client/auth.js | 784 + .../mcp-sdk/dist/esm/client/auth.js.map | 1 + .../mcp-sdk/dist/esm/client/index.d.ts | 1497 + .../{ajv => }/dist/esm/client/index.d.ts.map | 2 +- .../{ajv => }/dist/esm/client/index.js | 15 +- .../mcp-sdk/dist/esm/client/index.js.map | 1 + .../mcp-sdk/dist/esm/client/middleware.d.ts | 169 + .../dist/esm/client/middleware.d.ts.map | 1 + .../mcp-sdk/dist/esm/client/middleware.js | 249 + .../mcp-sdk/dist/esm/client/middleware.js.map | 1 + .../{ajv => }/dist/esm/client/sse.d.ts | 9 +- .../mcp-sdk/dist/esm/client/sse.d.ts.map | 1 + .../mcp-sdk/{ajv => }/dist/esm/client/sse.js | 48 +- .../mcp-sdk/dist/esm/client/sse.js.map | 1 + .../dist/cjs => dist/esm}/client/stdio.d.ts | 6 + .../{ajv => }/dist/esm/client/stdio.d.ts.map | 2 +- .../{ajv => }/dist/esm/client/stdio.js | 26 +- .../mcp-sdk/dist/esm/client/stdio.js.map | 1 + .../dist/esm/client/streamableHttp.d.ts | 33 +- .../dist/esm/client/streamableHttp.d.ts.map | 1 + .../dist/esm/client/streamableHttp.js | 87 +- .../dist/esm/client/streamableHttp.js.map | 1 + .../{ajv => }/dist/esm/client/websocket.d.ts | 0 .../dist/esm/client/websocket.d.ts.map | 0 .../{ajv => }/dist/esm/client/websocket.js | 0 .../dist/esm/client/websocket.js.map | 0 .../client/multipleClientsParallel.d.ts | 0 .../client/multipleClientsParallel.d.ts.map | 0 .../client/multipleClientsParallel.js | 0 .../client/multipleClientsParallel.js.map | 0 .../client/parallelToolCallsClient.d.ts | 0 .../client/parallelToolCallsClient.d.ts.map | 0 .../client/parallelToolCallsClient.js | 0 .../client/parallelToolCallsClient.js.map | 0 .../examples/client/simpleOAuthClient.d.ts | 0 .../client/simpleOAuthClient.d.ts.map | 0 .../esm/examples/client/simpleOAuthClient.js | 4 +- .../examples/client/simpleOAuthClient.js.map | 2 +- .../examples/client/simpleStreamableHttp.d.ts | 0 .../client/simpleStreamableHttp.d.ts.map | 0 .../examples/client/simpleStreamableHttp.js | 265 +- .../client/simpleStreamableHttp.js.map | 1 + .../streamableHttpWithSseFallbackClient.d.ts | 0 ...reamableHttpWithSseFallbackClient.d.ts.map | 0 .../streamableHttpWithSseFallbackClient.js | 0 ...streamableHttpWithSseFallbackClient.js.map | 0 .../server/demoInMemoryOAuthProvider.d.ts | 16 +- .../server/demoInMemoryOAuthProvider.d.ts.map | 1 + .../server/demoInMemoryOAuthProvider.js | 34 +- .../server/demoInMemoryOAuthProvider.js.map | 1 + .../server/jsonResponseStreamableHttp.d.ts | 0 .../jsonResponseStreamableHttp.d.ts.map | 0 .../server/jsonResponseStreamableHttp.js | 40 +- .../server/jsonResponseStreamableHttp.js.map | 1 + .../server/mcpServerOutputSchema.d.ts | 0 .../server/mcpServerOutputSchema.d.ts.map | 0 .../examples/server/mcpServerOutputSchema.js | 2 +- .../server/mcpServerOutputSchema.js.map | 0 .../esm/examples/server/simpleSseServer.d.ts | 0 .../examples/server/simpleSseServer.d.ts.map | 0 .../esm/examples/server/simpleSseServer.js | 32 +- .../examples/server/simpleSseServer.js.map | 1 + .../server/simpleStatelessStreamableHttp.d.ts | 0 .../simpleStatelessStreamableHttp.d.ts.map | 0 .../server/simpleStatelessStreamableHttp.js | 27 +- .../simpleStatelessStreamableHttp.js.map | 1 + .../examples/server/simpleStreamableHttp.d.ts | 0 .../server/simpleStreamableHttp.d.ts.map | 0 .../examples/server/simpleStreamableHttp.js | 582 + .../server/simpleStreamableHttp.js.map | 1 + .../sseAndStreamableHttpCompatibleServer.d.ts | 0 ...AndStreamableHttpCompatibleServer.d.ts.map | 0 .../sseAndStreamableHttpCompatibleServer.js | 27 +- ...seAndStreamableHttpCompatibleServer.js.map | 1 + .../standaloneSseWithGetStreamableHttp.d.ts | 0 ...tandaloneSseWithGetStreamableHttp.d.ts.map | 0 .../standaloneSseWithGetStreamableHttp.js | 6 +- .../standaloneSseWithGetStreamableHttp.js.map | 2 +- .../examples/server/toolWithSampleServer.d.ts | 2 + .../server/toolWithSampleServer.d.ts.map | 1 + .../examples/server/toolWithSampleServer.js | 47 + .../server/toolWithSampleServer.js.map | 1 + .../examples/shared/inMemoryEventStore.d.ts | 0 .../shared/inMemoryEventStore.d.ts.map | 0 .../esm/examples/shared/inMemoryEventStore.js | 0 .../examples/shared/inMemoryEventStore.js.map | 0 .../mcp-sdk/{ajv => }/dist/esm/inMemory.d.ts | 0 .../{ajv => }/dist/esm/inMemory.d.ts.map | 0 .../mcp-sdk/{ajv => }/dist/esm/inMemory.js | 0 .../{ajv => }/dist/esm/inMemory.js.map | 0 .../mcp-sdk/{ajv => }/dist/esm/package.json | 0 .../cjs => dist/esm}/server/auth/clients.d.ts | 2 +- .../dist/esm/server/auth/clients.d.ts.map | 2 +- .../{ajv => }/dist/esm/server/auth/clients.js | 0 .../dist/esm/server/auth/clients.js.map | 0 .../cjs => dist/esm}/server/auth/errors.d.ts | 51 +- .../dist/esm/server/auth/errors.d.ts.map | 1 + .../{ajv => }/dist/esm/server/auth/errors.js | 101 +- .../dist/esm/server/auth/errors.js.map | 1 + .../esm/server/auth/handlers/authorize.d.ts | 0 .../server/auth/handlers/authorize.d.ts.map | 2 +- .../esm/server/auth/handlers/authorize.js | 8 +- .../esm/server/auth/handlers/authorize.js.map | 1 + .../esm/server/auth/handlers/metadata.d.ts | 0 .../server/auth/handlers/metadata.d.ts.map | 0 .../dist/esm/server/auth/handlers/metadata.js | 0 .../esm/server/auth/handlers/metadata.js.map | 0 .../esm/server/auth/handlers/register.d.ts | 8 +- .../server/auth/handlers/register.d.ts.map | 2 +- .../dist/esm/server/auth/handlers/register.js | 10 +- .../esm/server/auth/handlers/register.js.map | 1 + .../dist/esm/server/auth/handlers/revoke.d.ts | 2 +- .../esm/server/auth/handlers/revoke.d.ts.map | 2 +- .../dist/esm/server/auth/handlers/revoke.js | 12 +- .../esm/server/auth/handlers/revoke.js.map | 1 + .../dist/esm/server/auth/handlers/token.d.ts | 0 .../esm/server/auth/handlers/token.d.ts.map | 2 +- .../dist/esm/server/auth/handlers/token.js | 14 +- .../esm/server/auth/handlers/token.js.map | 1 + .../auth/middleware/allowedMethods.d.ts | 0 .../auth/middleware/allowedMethods.d.ts.map | 0 .../server/auth/middleware/allowedMethods.js | 0 .../auth/middleware/allowedMethods.js.map | 0 .../server/auth/middleware/bearerAuth.d.ts | 0 .../auth/middleware/bearerAuth.d.ts.map | 2 +- .../esm/server/auth/middleware/bearerAuth.js | 8 +- .../server/auth/middleware/bearerAuth.js.map | 1 + .../server/auth/middleware/clientAuth.d.ts | 0 .../auth/middleware/clientAuth.d.ts.map | 2 +- .../esm/server/auth/middleware/clientAuth.js | 3 +- .../server/auth/middleware/clientAuth.js.map | 2 +- .../esm}/server/auth/provider.d.ts | 5 +- .../dist/esm/server/auth/provider.d.ts.map | 1 + .../dist/esm/server/auth/provider.js | 0 .../dist/esm/server/auth/provider.js.map | 0 .../server/auth/providers/proxyProvider.d.ts | 10 +- .../auth/providers/proxyProvider.d.ts.map | 1 + .../server/auth/providers/proxyProvider.js | 25 +- .../auth/providers/proxyProvider.js.map | 1 + .../dist/esm/server/auth/router.d.ts | 0 .../dist/esm/server/auth/router.d.ts.map | 0 .../{ajv => }/dist/esm/server/auth/router.js | 2 +- .../dist/esm/server/auth/router.js.map | 2 +- .../{ajv => }/dist/esm/server/auth/types.d.ts | 5 + .../dist/esm/server/auth/types.d.ts.map | 2 +- .../{ajv => }/dist/esm/server/auth/types.js | 0 .../dist/esm/server/auth/types.js.map | 0 .../dist/esm/server/completable.d.ts | 4 +- .../dist/esm/server/completable.d.ts.map | 1 + .../{ajv => }/dist/esm/server/completable.js | 2 +- .../dist/esm/server/completable.js.map | 1 + .../{ajv => }/dist/esm/server/index.d.ts | 39 +- .../mcp-sdk/dist/esm/server/index.d.ts.map | 1 + .../{ajv => }/dist/esm/server/index.js | 78 +- .../mcp-sdk/dist/esm/server/index.js.map | 1 + .../dist/cjs => dist/esm}/server/mcp.d.ts | 43 +- .../mcp-sdk/dist/esm/server/mcp.d.ts.map | 1 + .../mcp-sdk/{ajv => }/dist/esm/server/mcp.js | 246 +- .../mcp-sdk/dist/esm/server/mcp.js.map | 1 + .../dist/cjs => dist/esm}/server/sse.d.ts | 38 +- .../mcp-sdk/dist/esm/server/sse.d.ts.map | 1 + .../mcp-sdk/{ajv => }/dist/esm/server/sse.js | 50 +- .../mcp-sdk/dist/esm/server/sse.js.map | 1 + .../{ajv => }/dist/esm/server/stdio.d.ts | 0 .../{ajv => }/dist/esm/server/stdio.d.ts.map | 0 .../{ajv => }/dist/esm/server/stdio.js | 0 .../{ajv => }/dist/esm/server/stdio.js.map | 0 .../esm}/server/streamableHttp.d.ts | 46 +- .../dist/esm/server/streamableHttp.d.ts.map | 1 + .../dist/esm/server/streamableHttp.js | 98 +- .../dist/esm/server/streamableHttp.js.map | 1 + .../mcp-sdk/dist/esm/shared/auth-utils.d.ts | 23 + .../dist/esm/shared/auth-utils.d.ts.map | 1 + .../mcp-sdk/dist/esm/shared/auth-utils.js | 44 + .../mcp-sdk/dist/esm/shared/auth-utils.js.map | 1 + .../mcp-sdk/dist/esm/shared/auth.d.ts | 621 + .../mcp-sdk/dist/esm/shared/auth.d.ts.map | 1 + .../mcp-sdk/{ajv => }/dist/esm/shared/auth.js | 100 +- .../mcp-sdk/dist/esm/shared/auth.js.map | 1 + .../dist/esm/shared/metadataUtils.d.ts | 12 + .../dist/esm/shared/metadataUtils.d.ts.map | 1 + .../mcp-sdk/dist/esm/shared/metadataUtils.js | 26 + .../dist/esm/shared/metadataUtils.js.map | 1 + .../{ajv => }/dist/esm/shared/protocol.d.ts | 16 +- .../mcp-sdk/dist/esm/shared/protocol.d.ts.map | 1 + .../{ajv => }/dist/esm/shared/protocol.js | 64 +- .../mcp-sdk/dist/esm/shared/protocol.js.map | 1 + .../{ajv => }/dist/esm/shared/stdio.d.ts | 0 .../{ajv => }/dist/esm/shared/stdio.d.ts.map | 0 .../{ajv => }/dist/esm/shared/stdio.js | 0 .../{ajv => }/dist/esm/shared/stdio.js.map | 0 .../{ajv => }/dist/esm/shared/transport.d.ts | 15 +- .../dist/esm/shared/transport.d.ts.map | 1 + .../{ajv => }/dist/esm/shared/transport.js | 0 .../dist/esm/shared/transport.js.map | 0 .../dist/esm/shared/uriTemplate.d.ts | 0 .../dist/esm/shared/uriTemplate.d.ts.map | 0 .../{ajv => }/dist/esm/shared/uriTemplate.js | 0 .../dist/esm/shared/uriTemplate.js.map | 0 .../third_party/mcp-sdk/dist/esm/types.d.ts | 54852 ++++++++++++++++ .../mcp-sdk/dist/esm/types.d.ts.map | 1 + .../mcp-sdk/{ajv => }/dist/esm/types.js | 342 +- .../third_party/mcp-sdk/dist/esm/types.js.map | 1 + .../third_party/mcp-sdk/dist/zod/zod-esm.js | 8 + front_end/third_party/mcp-sdk/lib/ajv.d.ts | 397 + front_end/third_party/mcp-sdk/lib/ajv.js | 506 + front_end/third_party/mcp-sdk/lib/cache.js | 26 + .../third_party/mcp-sdk/lib/compile/async.js | 90 + .../third_party/mcp-sdk/lib/compile/equal.js | 5 + .../mcp-sdk/lib/compile/error_classes.js | 34 + .../mcp-sdk/lib/compile/formats.js | 142 + .../third_party/mcp-sdk/lib/compile/index.js | 387 + .../mcp-sdk/lib/compile/resolve.js | 270 + .../third_party/mcp-sdk/lib/compile/rules.js | 66 + .../mcp-sdk/lib/compile/schema_obj.js | 9 + .../mcp-sdk/lib/compile/ucs2length.js | 20 + .../third_party/mcp-sdk/lib/compile/util.js | 239 + front_end/third_party/mcp-sdk/lib/data.js | 49 + .../mcp-sdk/lib/definition_schema.js | 37 + .../third_party/mcp-sdk/lib/dot/_limit.jst | 113 + .../mcp-sdk/lib/dot/_limitItems.jst | 12 + .../mcp-sdk/lib/dot/_limitLength.jst | 12 + .../mcp-sdk/lib/dot/_limitProperties.jst | 12 + .../third_party/mcp-sdk/lib/dot/allOf.jst | 32 + .../third_party/mcp-sdk/lib/dot/anyOf.jst | 46 + .../third_party/mcp-sdk/lib/dot/coerce.def | 51 + .../third_party/mcp-sdk/lib/dot/comment.jst | 9 + .../third_party/mcp-sdk/lib/dot/const.jst | 11 + .../third_party/mcp-sdk/lib/dot/contains.jst | 55 + .../third_party/mcp-sdk/lib/dot/custom.jst | 191 + .../third_party/mcp-sdk/lib/dot/defaults.def | 47 + .../mcp-sdk/lib/dot/definitions.def | 203 + .../mcp-sdk/lib/dot/dependencies.jst | 79 + .../third_party/mcp-sdk/lib/dot/enum.jst | 30 + .../third_party/mcp-sdk/lib/dot/errors.def | 194 + .../third_party/mcp-sdk/lib/dot/format.jst | 106 + front_end/third_party/mcp-sdk/lib/dot/if.jst | 73 + .../third_party/mcp-sdk/lib/dot/items.jst | 98 + .../third_party/mcp-sdk/lib/dot/missing.def | 39 + .../mcp-sdk/lib/dot/multipleOf.jst | 22 + front_end/third_party/mcp-sdk/lib/dot/not.jst | 43 + .../third_party/mcp-sdk/lib/dot/oneOf.jst | 54 + .../third_party/mcp-sdk/lib/dot/pattern.jst | 14 + .../mcp-sdk/lib/dot/properties.jst | 245 + .../mcp-sdk/lib/dot/propertyNames.jst | 52 + front_end/third_party/mcp-sdk/lib/dot/ref.jst | 85 + .../third_party/mcp-sdk/lib/dot/required.jst | 108 + .../mcp-sdk/lib/dot/uniqueItems.jst | 62 + .../third_party/mcp-sdk/lib/dot/validate.jst | 276 + .../third_party/mcp-sdk/lib/dotjs/README.md | 3 + .../third_party/mcp-sdk/lib/dotjs/_limit.js | 163 + .../mcp-sdk/lib/dotjs/_limitItems.js | 80 + .../mcp-sdk/lib/dotjs/_limitLength.js | 85 + .../mcp-sdk/lib/dotjs/_limitProperties.js | 80 + .../third_party/mcp-sdk/lib/dotjs/allOf.js | 42 + .../third_party/mcp-sdk/lib/dotjs/anyOf.js | 73 + .../third_party/mcp-sdk/lib/dotjs/comment.js | 14 + .../third_party/mcp-sdk/lib/dotjs/const.js | 56 + .../third_party/mcp-sdk/lib/dotjs/contains.js | 81 + .../third_party/mcp-sdk/lib/dotjs/custom.js | 228 + .../mcp-sdk/lib/dotjs/dependencies.js | 168 + .../third_party/mcp-sdk/lib/dotjs/enum.js | 66 + .../third_party/mcp-sdk/lib/dotjs/format.js | 150 + front_end/third_party/mcp-sdk/lib/dotjs/if.js | 103 + .../third_party/mcp-sdk/lib/dotjs/index.js | 33 + .../third_party/mcp-sdk/lib/dotjs/items.js | 140 + .../mcp-sdk/lib/dotjs/multipleOf.js | 80 + .../third_party/mcp-sdk/lib/dotjs/not.js | 84 + .../third_party/mcp-sdk/lib/dotjs/oneOf.js | 73 + .../third_party/mcp-sdk/lib/dotjs/pattern.js | 75 + .../mcp-sdk/lib/dotjs/properties.js | 335 + .../mcp-sdk/lib/dotjs/propertyNames.js | 81 + .../third_party/mcp-sdk/lib/dotjs/ref.js | 124 + .../third_party/mcp-sdk/lib/dotjs/required.js | 270 + .../mcp-sdk/lib/dotjs/uniqueItems.js | 86 + .../third_party/mcp-sdk/lib/dotjs/validate.js | 482 + front_end/third_party/mcp-sdk/lib/keyword.js | 146 + .../third_party/mcp-sdk/lib/refs/data.json | 17 + .../lib/refs/json-schema-draft-04.json | 149 + .../lib/refs/json-schema-draft-06.json | 154 + .../lib/refs/json-schema-draft-07.json | 168 + .../dist => lib}/refs/json-schema-secure.json | 12 +- front_end/third_party/mcp-sdk/mcp-sdk-v2.ts | 979 + front_end/third_party/mcp-sdk/mcp-sdk.ts | 478 +- front_end/third_party/mcp-sdk/package.json | 106 + .../third_party/mcp-sdk/scripts/.eslintrc.yml | 3 + .../third_party/mcp-sdk/scripts/bundle.js | 61 + .../mcp-sdk/scripts/compile-dots.js | 73 + front_end/third_party/mcp-sdk/scripts/info | 10 + .../third_party/mcp-sdk/scripts/prepare-tests | 12 + .../mcp-sdk/scripts/publish-built-version | 32 + .../mcp-sdk/scripts/travis-gh-pages | 23 + .../third_party/mcp-sdk/zod/dist/cli.d.ts | 2 - .../third_party/mcp-sdk/zod/dist/cli.d.ts.map | 1 - front_end/third_party/mcp-sdk/zod/dist/cli.js | 129 - .../third_party/mcp-sdk/zod/dist/cli.js.map | 1 - .../mcp-sdk/zod/dist/client/index.d.ts | 773 - .../mcp-sdk/zod/dist/client/index.d.ts.map | 1 - .../mcp-sdk/zod/dist/client/index.js | 206 - .../mcp-sdk/zod/dist/client/index.js.map | 1 - .../mcp-sdk/zod/dist/client/index.test.d.ts | 2 - .../zod/dist/client/index.test.d.ts.map | 1 - .../mcp-sdk/zod/dist/client/index.test.js | 393 - .../mcp-sdk/zod/dist/client/index.test.js.map | 1 - .../mcp-sdk/zod/dist/client/sse.d.ts | 22 - .../mcp-sdk/zod/dist/client/sse.d.ts.map | 1 - .../mcp-sdk/zod/dist/client/sse.js | 91 - .../mcp-sdk/zod/dist/client/sse.js.map | 1 - .../mcp-sdk/zod/dist/client/stdio.d.ts | 63 - .../mcp-sdk/zod/dist/client/stdio.d.ts.map | 1 - .../mcp-sdk/zod/dist/client/stdio.js | 148 - .../mcp-sdk/zod/dist/client/stdio.js.map | 1 - .../mcp-sdk/zod/dist/client/stdio.test.d.ts | 2 - .../zod/dist/client/stdio.test.d.ts.map | 1 - .../mcp-sdk/zod/dist/client/stdio.test.js | 51 - .../mcp-sdk/zod/dist/client/stdio.test.js.map | 1 - .../mcp-sdk/zod/dist/client/websocket.d.ts | 17 - .../zod/dist/client/websocket.d.ts.map | 1 - .../mcp-sdk/zod/dist/client/websocket.js | 61 - .../mcp-sdk/zod/dist/client/websocket.js.map | 1 - .../mcp-sdk/zod/dist/inMemory.d.ts | 20 - .../mcp-sdk/zod/dist/inMemory.d.ts.map | 1 - .../third_party/mcp-sdk/zod/dist/inMemory.js | 47 - .../mcp-sdk/zod/dist/inMemory.js.map | 1 - .../mcp-sdk/zod/dist/inMemory.test.d.ts | 2 - .../mcp-sdk/zod/dist/inMemory.test.d.ts.map | 1 - .../mcp-sdk/zod/dist/inMemory.test.js | 74 - .../mcp-sdk/zod/dist/inMemory.test.js.map | 1 - .../mcp-sdk/zod/dist/server/index.d.ts | 112 - .../mcp-sdk/zod/dist/server/index.d.ts.map | 1 - .../mcp-sdk/zod/dist/server/index.js | 182 - .../mcp-sdk/zod/dist/server/index.js.map | 1 - .../mcp-sdk/zod/dist/server/index.test.d.ts | 2 - .../zod/dist/server/index.test.d.ts.map | 1 - .../mcp-sdk/zod/dist/server/index.test.js | 408 - .../mcp-sdk/zod/dist/server/index.test.js.map | 1 - .../mcp-sdk/zod/dist/server/sse.d.ts | 46 - .../mcp-sdk/zod/dist/server/sse.d.ts.map | 1 - .../mcp-sdk/zod/dist/server/sse.js | 116 - .../mcp-sdk/zod/dist/server/sse.js.map | 1 - .../mcp-sdk/zod/dist/server/stdio.d.ts | 28 - .../mcp-sdk/zod/dist/server/stdio.d.ts.map | 1 - .../mcp-sdk/zod/dist/server/stdio.js | 69 - .../mcp-sdk/zod/dist/server/stdio.js.map | 1 - .../mcp-sdk/zod/dist/server/stdio.test.d.ts | 2 - .../zod/dist/server/stdio.test.d.ts.map | 1 - .../mcp-sdk/zod/dist/server/stdio.test.js | 87 - .../mcp-sdk/zod/dist/server/stdio.test.js.map | 1 - .../mcp-sdk/zod/dist/shared/protocol.d.ts | 157 - .../mcp-sdk/zod/dist/shared/protocol.d.ts.map | 1 - .../mcp-sdk/zod/dist/shared/protocol.js | 297 - .../mcp-sdk/zod/dist/shared/protocol.js.map | 1 - .../mcp-sdk/zod/dist/shared/stdio.d.ts | 13 - .../mcp-sdk/zod/dist/shared/stdio.d.ts.map | 1 - .../mcp-sdk/zod/dist/shared/stdio.js | 31 - .../mcp-sdk/zod/dist/shared/stdio.js.map | 1 - .../mcp-sdk/zod/dist/shared/stdio.test.d.ts | 2 - .../zod/dist/shared/stdio.test.d.ts.map | 1 - .../mcp-sdk/zod/dist/shared/stdio.test.js | 27 - .../mcp-sdk/zod/dist/shared/stdio.test.js.map | 1 - .../mcp-sdk/zod/dist/shared/transport.d.ts | 39 - .../zod/dist/shared/transport.d.ts.map | 1 - .../mcp-sdk/zod/dist/shared/transport.js | 2 - .../mcp-sdk/zod/dist/shared/transport.js.map | 1 - .../third_party/mcp-sdk/zod/dist/types.d.ts | 26425 -------- .../mcp-sdk/zod/dist/types.d.ts.map | 1 - .../third_party/mcp-sdk/zod/dist/types.js | 997 - .../third_party/mcp-sdk/zod/dist/types.js.map | 1 - front_end/third_party/mcp-sdk/zod/zod-esm.js | 3 + 1331 files changed, 167981 insertions(+), 124545 deletions(-) create mode 100644 front_end/Images/src/asana-mcp.svg create mode 100644 front_end/Images/src/atlassian-mcp.svg create mode 100644 front_end/Images/src/github-mcp.svg create mode 100644 front_end/Images/src/google-drive-mcp.svg create mode 100644 front_end/Images/src/google-sheets-mcp.svg create mode 100644 front_end/Images/src/huggingface-mcp.svg create mode 100644 front_end/Images/src/intercom-mcp.svg create mode 100644 front_end/Images/src/invideo-mcp.svg create mode 100644 front_end/Images/src/linear-mcp.svg create mode 100644 front_end/Images/src/notion-mcp.svg create mode 100644 front_end/Images/src/sentry-mcp.svg create mode 100644 front_end/Images/src/slack-mcp.svg create mode 100644 front_end/Images/src/socket-mcp.svg create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/ActionAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/ActionVerificationAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/AgentVersion.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/ClickActionAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/ContentWriterAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/DirectURLNavigatorAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/EcommerceProductInfoAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/FormFillActionAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/HoverActionAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/KeyboardInputActionAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/ResearchAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/ScrollActionAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/SearchAgent.ts create mode 100644 front_end/panels/ai_chat/agent_framework/implementation/agents/WebTaskAgent.ts create mode 100644 front_end/panels/ai_chat/core/AgentDescriptorRegistry.ts rename front_end/panels/ai_chat/core/{ => __tests__}/AgentNodes.test.ts (79%) create mode 100644 front_end/panels/ai_chat/core/__tests__/ToolExecutorNode.test.ts create mode 100644 front_end/panels/ai_chat/core/__tests__/ToolNameMap.test.ts rename front_end/panels/ai_chat/core/{ => __tests__}/ToolNameMapping.test.ts (87%) rename front_end/panels/ai_chat/core/{ => __tests__}/ToolSurfaceProvider.test.ts (93%) create mode 100644 front_end/panels/ai_chat/docs/MCP_OAuth_Implementation_Plan.md rename front_end/panels/ai_chat/mcp/{ => __tests__}/MCPClientSDK.test.ts (93%) create mode 100644 front_end/panels/ai_chat/mcp/__tests__/MCPConfig.test.ts create mode 100644 front_end/panels/ai_chat/mcp/__tests__/MCPIntegration.test.ts create mode 100644 front_end/panels/ai_chat/mcp/__tests__/MCPToolRegistration.test.ts rename front_end/panels/ai_chat/ui/{ => __tests__}/AIChatPanel.test.ts (95%) create mode 100644 front_end/panels/ai_chat/ui/mcp/MCPConnectionsDialog.ts create mode 100644 front_end/panels/ai_chat/ui/mcp/MCPConnectorsCatalogDialog.ts create mode 100644 front_end/third_party/mcp-sdk/.tonic_example.js create mode 100644 front_end/third_party/mcp-sdk/MCPOAuthProvider.ts create mode 100644 front_end/third_party/mcp-sdk/README.md delete mode 100644 front_end/third_party/mcp-sdk/ajv/.runkit_example.js create mode 100644 front_end/third_party/mcp-sdk/ajv/.tonic_example.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/2019.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/2019.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/2019.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/2020.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/2020.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/2020.js.map create mode 100644 front_end/third_party/mcp-sdk/ajv/dist/ajv-esm.js create mode 100644 front_end/third_party/mcp-sdk/ajv/dist/ajv.bundle.js mode change 100644 => 120000 front_end/third_party/mcp-sdk/ajv/dist/ajv.d.ts mode change 100644 => 120000 front_end/third_party/mcp-sdk/ajv/dist/ajv.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/ajv.js.map create mode 100644 front_end/third_party/mcp-sdk/ajv/dist/ajv.min.js create mode 100644 front_end/third_party/mcp-sdk/ajv/dist/ajv.min.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/auth.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/auth.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/auth.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/auth.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/sse.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/sse.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/stdio.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/streamableHttp.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/client/streamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/examples/client/simpleStreamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/examples/server/demoInMemoryOAuthProvider.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/examples/server/demoInMemoryOAuthProvider.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/examples/server/jsonResponseStreamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/examples/server/simpleSseServer.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/examples/server/simpleStatelessStreamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/examples/server/simpleStreamableHttp.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/examples/server/simpleStreamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/auth/errors.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/auth/errors.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/auth/handlers/authorize.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/auth/handlers/register.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/auth/handlers/revoke.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/auth/handlers/token.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/auth/middleware/bearerAuth.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/auth/provider.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/auth/providers/proxyProvider.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/auth/providers/proxyProvider.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/completable.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/completable.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/index.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/mcp.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/mcp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/sse.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/sse.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/streamableHttp.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/server/streamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/shared/auth.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/shared/auth.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/shared/auth.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/shared/protocol.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/shared/protocol.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/shared/transport.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/types.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/types.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/cjs/types.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/codegen/code.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/codegen/code.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/codegen/code.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/codegen/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/codegen/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/codegen/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/codegen/scope.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/codegen/scope.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/codegen/scope.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/errors.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/errors.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/errors.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/jtd/parse.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/jtd/parse.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/jtd/parse.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/jtd/serialize.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/jtd/serialize.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/jtd/serialize.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/jtd/types.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/jtd/types.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/jtd/types.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/names.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/names.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/names.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/ref_error.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/ref_error.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/ref_error.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/resolve.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/resolve.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/resolve.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/rules.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/rules.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/rules.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/util.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/util.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/util.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/applicability.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/applicability.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/applicability.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/boolSchema.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/boolSchema.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/boolSchema.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/dataType.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/dataType.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/dataType.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/defaults.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/defaults.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/defaults.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/keyword.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/keyword.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/keyword.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/subschema.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/subschema.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/compile/validate/subschema.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/core.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/core.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/core.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/auth.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/auth.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/auth.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/auth.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/sse.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/sse.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/stdio.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/streamableHttp.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/client/streamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/examples/client/simpleStreamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/examples/server/demoInMemoryOAuthProvider.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/examples/server/demoInMemoryOAuthProvider.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/examples/server/jsonResponseStreamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/examples/server/simpleSseServer.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/examples/server/simpleStatelessStreamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/examples/server/simpleStreamableHttp.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/examples/server/simpleStreamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/auth/errors.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/auth/errors.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/auth/handlers/authorize.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/auth/handlers/register.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/auth/handlers/revoke.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/auth/handlers/token.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/auth/middleware/bearerAuth.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/auth/provider.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/auth/providers/proxyProvider.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/auth/providers/proxyProvider.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/completable.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/completable.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/index.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/mcp.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/mcp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/sse.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/sse.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/streamableHttp.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/server/streamableHttp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/shared/auth.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/shared/auth.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/shared/auth.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/shared/protocol.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/shared/protocol.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/shared/transport.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/types.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/types.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/esm/types.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/jtd.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/jtd.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/jtd.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/data.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2019-09/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2019-09/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2019-09/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2019-09/meta/applicator.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2019-09/meta/content.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2019-09/meta/core.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2019-09/meta/format.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2019-09/meta/meta-data.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2019-09/meta/validation.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2019-09/schema.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/meta/applicator.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/meta/content.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/meta/core.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/meta/format-annotation.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/meta/meta-data.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/meta/unevaluated.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/meta/validation.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-2020-12/schema.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-draft-06.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/json-schema-draft-07.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/jtd-schema.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/jtd-schema.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/refs/jtd-schema.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/equal.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/equal.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/equal.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/parseJson.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/parseJson.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/parseJson.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/quote.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/quote.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/quote.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/re2.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/re2.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/re2.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/timestamp.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/timestamp.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/timestamp.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/ucs2length.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/ucs2length.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/ucs2length.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/uri.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/uri.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/uri.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/validation_error.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/validation_error.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/runtime/validation_error.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/standalone/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/standalone/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/standalone/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/standalone/instance.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/standalone/instance.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/standalone/instance.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/types/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/types/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/types/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/types/json-schema.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/types/json-schema.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/types/json-schema.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/types/jtd-schema.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/types/jtd-schema.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/types/jtd-schema.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/additionalItems.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/additionalItems.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/additionalItems.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/additionalProperties.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/additionalProperties.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/additionalProperties.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/allOf.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/allOf.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/allOf.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/anyOf.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/anyOf.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/anyOf.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/contains.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/contains.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/contains.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/dependencies.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/dependencies.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/dependencies.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/dependentSchemas.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/dependentSchemas.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/dependentSchemas.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/if.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/if.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/if.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/items.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/items.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/items.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/items2020.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/items2020.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/items2020.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/not.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/not.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/not.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/oneOf.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/oneOf.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/oneOf.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/patternProperties.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/patternProperties.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/patternProperties.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/prefixItems.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/prefixItems.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/prefixItems.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/properties.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/properties.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/properties.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/propertyNames.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/propertyNames.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/propertyNames.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/thenElse.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/thenElse.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/applicator/thenElse.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/code.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/code.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/code.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/core/id.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/core/id.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/core/id.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/core/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/core/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/core/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/core/ref.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/core/ref.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/core/ref.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/discriminator/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/discriminator/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/discriminator/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/discriminator/types.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/discriminator/types.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/discriminator/types.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/draft2020.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/draft2020.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/draft2020.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/draft7.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/draft7.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/draft7.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/dynamicAnchor.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/dynamicAnchor.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/dynamicAnchor.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/dynamicRef.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/dynamicRef.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/dynamicRef.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/recursiveAnchor.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/recursiveAnchor.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/recursiveAnchor.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/recursiveRef.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/recursiveRef.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/dynamic/recursiveRef.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/errors.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/errors.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/errors.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/format/format.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/format/format.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/format/format.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/format/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/format/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/format/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/discriminator.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/discriminator.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/discriminator.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/elements.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/elements.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/elements.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/enum.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/enum.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/enum.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/error.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/error.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/error.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/metadata.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/metadata.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/metadata.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/nullable.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/nullable.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/nullable.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/optionalProperties.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/optionalProperties.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/optionalProperties.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/properties.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/properties.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/properties.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/ref.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/ref.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/ref.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/type.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/type.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/type.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/union.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/union.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/union.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/values.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/values.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/jtd/values.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/metadata.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/metadata.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/metadata.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/next.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/next.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/next.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/unevaluated/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/unevaluated/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/unevaluated/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/unevaluated/unevaluatedItems.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/unevaluated/unevaluatedItems.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/unevaluated/unevaluatedItems.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/unevaluated/unevaluatedProperties.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/const.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/const.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/const.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/dependentRequired.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/dependentRequired.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/dependentRequired.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/enum.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/enum.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/enum.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitContains.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitContains.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitContains.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitItems.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitItems.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitItems.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitLength.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitLength.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitLength.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitNumber.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitNumber.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitNumber.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitProperties.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitProperties.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/limitProperties.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/multipleOf.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/multipleOf.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/multipleOf.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/pattern.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/pattern.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/pattern.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/required.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/required.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/required.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/uniqueItems.d.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/uniqueItems.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/dist/vocabularies/validation/uniqueItems.js.map delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/2019.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/2020.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/ajv.d.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/ajv.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/ajv.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/cache.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/async.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/codegen/code.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/codegen/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/codegen/scope.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/equal.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/error_classes.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/errors.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/formats.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/index.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/jtd/parse.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/jtd/serialize.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/jtd/types.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/names.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/ref_error.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/resolve.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/resolve.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/rules.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/rules.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/schema_obj.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/ucs2length.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/util.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/util.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/validate/applicability.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/validate/boolSchema.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/validate/dataType.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/validate/defaults.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/validate/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/validate/keyword.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/compile/validate/subschema.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/core.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/data.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/definition_schema.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/_limit.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/_limitItems.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/_limitLength.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/_limitProperties.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/allOf.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/anyOf.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/coerce.def create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/comment.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/const.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/contains.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/custom.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/defaults.def create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/definitions.def create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/dependencies.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/enum.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/errors.def create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/format.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/if.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/items.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/missing.def create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/multipleOf.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/not.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/oneOf.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/pattern.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/properties.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/propertyNames.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/ref.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/required.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/uniqueItems.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dot/validate.jst create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/README.md create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/_limit.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/_limitItems.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/_limitLength.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/_limitProperties.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/allOf.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/anyOf.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/comment.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/const.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/contains.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/custom.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/dependencies.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/enum.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/format.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/if.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/index.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/items.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/multipleOf.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/not.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/oneOf.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/pattern.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/properties.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/propertyNames.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/ref.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/required.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/uniqueItems.js create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/dotjs/validate.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/jtd.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/keyword.js delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2019-09/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2019-09/meta/applicator.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2019-09/meta/content.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2019-09/meta/core.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2019-09/meta/format.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2019-09/meta/meta-data.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2019-09/meta/validation.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2019-09/schema.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2020-12/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2020-12/meta/applicator.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2020-12/meta/content.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2020-12/meta/core.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2020-12/meta/format-annotation.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2020-12/meta/meta-data.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2020-12/meta/unevaluated.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2020-12/meta/validation.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-2020-12/schema.json create mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/json-schema-draft-04.json delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/refs/jtd-schema.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/runtime/equal.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/runtime/parseJson.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/runtime/quote.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/runtime/re2.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/runtime/timestamp.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/runtime/ucs2length.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/runtime/uri.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/runtime/validation_error.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/standalone/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/standalone/instance.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/types/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/types/json-schema.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/types/jtd-schema.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/additionalItems.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/additionalProperties.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/allOf.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/anyOf.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/contains.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/dependencies.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/dependentSchemas.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/if.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/items.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/items2020.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/not.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/oneOf.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/patternProperties.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/prefixItems.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/properties.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/propertyNames.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/applicator/thenElse.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/code.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/core/id.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/core/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/core/ref.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/discriminator/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/discriminator/types.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/draft2020.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/draft7.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/dynamic/dynamicAnchor.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/dynamic/dynamicRef.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/dynamic/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/dynamic/recursiveAnchor.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/dynamic/recursiveRef.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/errors.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/format/format.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/format/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/discriminator.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/elements.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/enum.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/error.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/metadata.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/nullable.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/optionalProperties.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/properties.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/ref.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/type.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/union.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/jtd/values.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/metadata.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/next.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/unevaluated/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/unevaluated/unevaluatedItems.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/unevaluated/unevaluatedProperties.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/const.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/dependentRequired.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/enum.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/index.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/limitContains.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/limitItems.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/limitLength.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/limitNumber.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/limitProperties.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/multipleOf.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/pattern.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/required.ts delete mode 100644 front_end/third_party/mcp-sdk/ajv/lib/vocabularies/validation/uniqueItems.ts create mode 100644 front_end/third_party/mcp-sdk/ajv/scripts/.eslintrc.yml create mode 100644 front_end/third_party/mcp-sdk/ajv/scripts/bundle.js create mode 100644 front_end/third_party/mcp-sdk/ajv/scripts/compile-dots.js create mode 100644 front_end/third_party/mcp-sdk/ajv/scripts/info create mode 100644 front_end/third_party/mcp-sdk/ajv/scripts/prepare-tests create mode 100644 front_end/third_party/mcp-sdk/ajv/scripts/publish-built-version create mode 100644 front_end/third_party/mcp-sdk/ajv/scripts/travis-gh-pages create mode 100644 front_end/third_party/mcp-sdk/dist/ajv.bundle.js create mode 100644 front_end/third_party/mcp-sdk/dist/ajv.min.js create mode 100644 front_end/third_party/mcp-sdk/dist/ajv.min.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/cli.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/cli.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/cli.js (96%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/cli.js.map (63%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/auth.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/auth.d.ts.map create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/auth.js create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/auth.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/index.d.ts rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/index.d.ts.map (55%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/index.js (95%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/index.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/middleware.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/middleware.d.ts.map create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/middleware.js create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/middleware.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/sse.d.ts (92%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/sse.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/sse.js (79%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/sse.js.map rename front_end/third_party/mcp-sdk/{ajv/dist/esm => dist/cjs}/client/stdio.d.ts (93%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/stdio.d.ts.map (76%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/stdio.js (86%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/stdio.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/streamableHttp.d.ts (83%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/streamableHttp.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/streamableHttp.js (85%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/client/streamableHttp.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/websocket.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/websocket.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/websocket.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/client/websocket.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/multipleClientsParallel.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/multipleClientsParallel.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/multipleClientsParallel.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/multipleClientsParallel.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/parallelToolCallsClient.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/parallelToolCallsClient.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/parallelToolCallsClient.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/parallelToolCallsClient.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/simpleOAuthClient.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/simpleOAuthClient.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/simpleOAuthClient.js (99%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/simpleOAuthClient.js.map (59%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/simpleStreamableHttp.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/simpleStreamableHttp.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/simpleStreamableHttp.js (57%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/client/simpleStreamableHttp.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/demoInMemoryOAuthProvider.d.ts (84%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/demoInMemoryOAuthProvider.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/demoInMemoryOAuthProvider.js (82%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/demoInMemoryOAuthProvider.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/jsonResponseStreamableHttp.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/jsonResponseStreamableHttp.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/jsonResponseStreamableHttp.js (84%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/jsonResponseStreamableHttp.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/mcpServerOutputSchema.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/mcpServerOutputSchema.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/mcpServerOutputSchema.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/mcpServerOutputSchema.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/simpleSseServer.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/simpleSseServer.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/simpleSseServer.js (88%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/simpleSseServer.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/simpleStatelessStreamableHttp.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/simpleStatelessStreamableHttp.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/simpleStatelessStreamableHttp.js (86%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/simpleStatelessStreamableHttp.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/simpleStreamableHttp.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/simpleStreamableHttp.d.ts.map (100%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/simpleStreamableHttp.js create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/simpleStreamableHttp.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js (93%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.js (97%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.js.map (85%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/toolWithSampleServer.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/toolWithSampleServer.d.ts.map create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/toolWithSampleServer.js create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/examples/server/toolWithSampleServer.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/shared/inMemoryEventStore.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/shared/inMemoryEventStore.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/shared/inMemoryEventStore.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/examples/shared/inMemoryEventStore.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/inMemory.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/inMemory.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/inMemory.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/inMemory.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/package.json (100%) rename front_end/third_party/mcp-sdk/{ajv/dist/esm => dist/cjs}/server/auth/clients.d.ts (86%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/clients.d.ts.map (70%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/clients.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/clients.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv/dist/esm => dist/cjs}/server/auth/errors.d.ts (77%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/auth/errors.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/errors.js (60%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/auth/errors.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/authorize.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/authorize.d.ts.map (75%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/authorize.js (96%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/auth/handlers/authorize.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/metadata.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/metadata.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/metadata.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/metadata.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/register.d.ts (76%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/register.d.ts.map (70%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/register.js (93%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/auth/handlers/register.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/revoke.d.ts (89%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/revoke.d.ts.map (75%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/revoke.js (88%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/auth/handlers/revoke.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/token.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/token.d.ts.map (76%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/handlers/token.js (92%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/auth/handlers/token.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/allowedMethods.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/allowedMethods.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/allowedMethods.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/allowedMethods.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/bearerAuth.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/bearerAuth.d.ts.map (90%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/bearerAuth.js (90%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/auth/middleware/bearerAuth.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/clientAuth.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/clientAuth.d.ts.map (98%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/clientAuth.js (96%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/middleware/clientAuth.js.map (82%) rename front_end/third_party/mcp-sdk/{ajv/dist/esm => dist/cjs}/server/auth/provider.d.ts (95%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/auth/provider.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/provider.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/provider.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv/dist/esm => dist/cjs}/server/auth/providers/proxyProvider.d.ts (86%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/auth/providers/proxyProvider.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/providers/proxyProvider.js (85%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/auth/providers/proxyProvider.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/router.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/router.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/router.js (99%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/router.js.map (71%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/types.d.ts (76%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/types.d.ts.map (59%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/types.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/auth/types.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/completable.d.ts (89%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/completable.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/completable.js (100%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/completable.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/index.d.ts (60%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/index.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/index.js (68%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/index.js.map rename front_end/third_party/mcp-sdk/{ajv/dist/esm => dist/cjs}/server/mcp.d.ts (87%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/mcp.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/mcp.js (77%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/mcp.js.map rename front_end/third_party/mcp-sdk/{ajv/dist/esm => dist/cjs}/server/sse.d.ts (58%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/sse.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/sse.js (69%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/sse.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/stdio.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/stdio.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/stdio.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/stdio.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv/dist/esm => dist/cjs}/server/streamableHttp.d.ts (74%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/streamableHttp.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/server/streamableHttp.js (84%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/server/streamableHttp.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/auth-utils.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/auth-utils.d.ts.map create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/auth-utils.js create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/auth-utils.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/auth.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/auth.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/auth.js (51%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/auth.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/metadataUtils.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/metadataUtils.d.ts.map create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/metadataUtils.js create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/metadataUtils.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/protocol.d.ts (92%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/protocol.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/protocol.js (81%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/protocol.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/stdio.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/stdio.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/stdio.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/stdio.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/transport.d.ts (80%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/shared/transport.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/transport.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/transport.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/uriTemplate.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/uriTemplate.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/uriTemplate.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/shared/uriTemplate.js.map (100%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/types.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/types.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/cjs/types.js (72%) create mode 100644 front_end/third_party/mcp-sdk/dist/cjs/types.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/cli.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/cli.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/cli.js (96%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/cli.js.map (66%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/auth.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/auth.d.ts.map create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/auth.js create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/auth.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/index.d.ts rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/index.d.ts.map (55%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/index.js (95%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/index.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/middleware.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/middleware.d.ts.map create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/middleware.js create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/middleware.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/sse.d.ts (92%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/sse.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/sse.js (78%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/sse.js.map rename front_end/third_party/mcp-sdk/{ajv/dist/cjs => dist/esm}/client/stdio.d.ts (93%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/stdio.d.ts.map (76%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/stdio.js (85%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/stdio.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/streamableHttp.d.ts (83%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/streamableHttp.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/streamableHttp.js (85%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/client/streamableHttp.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/websocket.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/websocket.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/websocket.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/client/websocket.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/multipleClientsParallel.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/multipleClientsParallel.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/multipleClientsParallel.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/multipleClientsParallel.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/parallelToolCallsClient.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/parallelToolCallsClient.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/parallelToolCallsClient.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/parallelToolCallsClient.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/simpleOAuthClient.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/simpleOAuthClient.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/simpleOAuthClient.js (99%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/simpleOAuthClient.js.map (60%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/simpleStreamableHttp.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/simpleStreamableHttp.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/simpleStreamableHttp.js (57%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/client/simpleStreamableHttp.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/streamableHttpWithSseFallbackClient.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/streamableHttpWithSseFallbackClient.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/streamableHttpWithSseFallbackClient.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/client/streamableHttpWithSseFallbackClient.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/demoInMemoryOAuthProvider.d.ts (84%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/demoInMemoryOAuthProvider.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/demoInMemoryOAuthProvider.js (80%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/demoInMemoryOAuthProvider.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/jsonResponseStreamableHttp.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/jsonResponseStreamableHttp.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/jsonResponseStreamableHttp.js (83%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/jsonResponseStreamableHttp.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/mcpServerOutputSchema.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/mcpServerOutputSchema.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/mcpServerOutputSchema.js (98%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/mcpServerOutputSchema.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/simpleSseServer.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/simpleSseServer.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/simpleSseServer.js (87%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/simpleSseServer.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/simpleStatelessStreamableHttp.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/simpleStatelessStreamableHttp.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/simpleStatelessStreamableHttp.js (85%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/simpleStatelessStreamableHttp.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/simpleStreamableHttp.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/simpleStreamableHttp.d.ts.map (100%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/simpleStreamableHttp.js create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/simpleStreamableHttp.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js (92%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.js (96%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.js.map (86%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/toolWithSampleServer.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/toolWithSampleServer.d.ts.map create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/toolWithSampleServer.js create mode 100644 front_end/third_party/mcp-sdk/dist/esm/examples/server/toolWithSampleServer.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/shared/inMemoryEventStore.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/shared/inMemoryEventStore.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/shared/inMemoryEventStore.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/examples/shared/inMemoryEventStore.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/inMemory.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/inMemory.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/inMemory.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/inMemory.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/package.json (100%) rename front_end/third_party/mcp-sdk/{ajv/dist/cjs => dist/esm}/server/auth/clients.d.ts (86%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/clients.d.ts.map (70%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/clients.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/clients.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv/dist/cjs => dist/esm}/server/auth/errors.d.ts (77%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/auth/errors.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/errors.js (61%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/auth/errors.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/authorize.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/authorize.d.ts.map (75%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/authorize.js (96%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/auth/handlers/authorize.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/metadata.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/metadata.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/metadata.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/metadata.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/register.d.ts (76%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/register.d.ts.map (70%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/register.js (93%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/auth/handlers/register.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/revoke.d.ts (89%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/revoke.d.ts.map (75%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/revoke.js (85%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/auth/handlers/revoke.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/token.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/token.d.ts.map (76%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/handlers/token.js (91%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/auth/handlers/token.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/allowedMethods.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/allowedMethods.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/allowedMethods.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/allowedMethods.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/bearerAuth.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/bearerAuth.d.ts.map (90%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/bearerAuth.js (90%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/auth/middleware/bearerAuth.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/clientAuth.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/clientAuth.d.ts.map (98%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/clientAuth.js (95%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/middleware/clientAuth.js.map (85%) rename front_end/third_party/mcp-sdk/{ajv/dist/cjs => dist/esm}/server/auth/provider.d.ts (95%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/auth/provider.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/provider.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/provider.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv/dist/cjs => dist/esm}/server/auth/providers/proxyProvider.d.ts (86%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/auth/providers/proxyProvider.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/providers/proxyProvider.js (84%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/auth/providers/proxyProvider.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/router.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/router.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/router.js (99%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/router.js.map (73%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/types.d.ts (76%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/types.d.ts.map (59%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/types.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/auth/types.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/completable.d.ts (89%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/completable.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/completable.js (97%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/completable.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/index.d.ts (60%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/index.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/index.js (67%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/index.js.map rename front_end/third_party/mcp-sdk/{ajv/dist/cjs => dist/esm}/server/mcp.d.ts (87%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/mcp.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/mcp.js (76%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/mcp.js.map rename front_end/third_party/mcp-sdk/{ajv/dist/cjs => dist/esm}/server/sse.d.ts (58%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/sse.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/sse.js (67%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/sse.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/stdio.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/stdio.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/stdio.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/stdio.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv/dist/cjs => dist/esm}/server/streamableHttp.d.ts (74%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/streamableHttp.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/server/streamableHttp.js (84%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/server/streamableHttp.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/auth-utils.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/auth-utils.d.ts.map create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/auth-utils.js create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/auth-utils.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/auth.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/auth.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/auth.js (50%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/auth.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/metadataUtils.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/metadataUtils.d.ts.map create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/metadataUtils.js create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/metadataUtils.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/protocol.d.ts (92%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/protocol.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/protocol.js (81%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/protocol.js.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/stdio.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/stdio.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/stdio.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/stdio.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/transport.d.ts (80%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/shared/transport.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/transport.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/transport.js.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/uriTemplate.d.ts (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/uriTemplate.d.ts.map (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/uriTemplate.js (100%) rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/shared/uriTemplate.js.map (100%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/types.d.ts create mode 100644 front_end/third_party/mcp-sdk/dist/esm/types.d.ts.map rename front_end/third_party/mcp-sdk/{ajv => }/dist/esm/types.js (77%) create mode 100644 front_end/third_party/mcp-sdk/dist/esm/types.js.map create mode 100644 front_end/third_party/mcp-sdk/dist/zod/zod-esm.js create mode 100644 front_end/third_party/mcp-sdk/lib/ajv.d.ts create mode 100644 front_end/third_party/mcp-sdk/lib/ajv.js create mode 100644 front_end/third_party/mcp-sdk/lib/cache.js create mode 100644 front_end/third_party/mcp-sdk/lib/compile/async.js create mode 100644 front_end/third_party/mcp-sdk/lib/compile/equal.js create mode 100644 front_end/third_party/mcp-sdk/lib/compile/error_classes.js create mode 100644 front_end/third_party/mcp-sdk/lib/compile/formats.js create mode 100644 front_end/third_party/mcp-sdk/lib/compile/index.js create mode 100644 front_end/third_party/mcp-sdk/lib/compile/resolve.js create mode 100644 front_end/third_party/mcp-sdk/lib/compile/rules.js create mode 100644 front_end/third_party/mcp-sdk/lib/compile/schema_obj.js create mode 100644 front_end/third_party/mcp-sdk/lib/compile/ucs2length.js create mode 100644 front_end/third_party/mcp-sdk/lib/compile/util.js create mode 100644 front_end/third_party/mcp-sdk/lib/data.js create mode 100644 front_end/third_party/mcp-sdk/lib/definition_schema.js create mode 100644 front_end/third_party/mcp-sdk/lib/dot/_limit.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/_limitItems.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/_limitLength.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/_limitProperties.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/allOf.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/anyOf.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/coerce.def create mode 100644 front_end/third_party/mcp-sdk/lib/dot/comment.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/const.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/contains.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/custom.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/defaults.def create mode 100644 front_end/third_party/mcp-sdk/lib/dot/definitions.def create mode 100644 front_end/third_party/mcp-sdk/lib/dot/dependencies.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/enum.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/errors.def create mode 100644 front_end/third_party/mcp-sdk/lib/dot/format.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/if.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/items.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/missing.def create mode 100644 front_end/third_party/mcp-sdk/lib/dot/multipleOf.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/not.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/oneOf.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/pattern.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/properties.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/propertyNames.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/ref.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/required.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/uniqueItems.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dot/validate.jst create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/README.md create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/_limit.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/_limitItems.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/_limitLength.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/_limitProperties.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/allOf.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/anyOf.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/comment.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/const.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/contains.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/custom.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/dependencies.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/enum.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/format.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/if.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/index.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/items.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/multipleOf.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/not.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/oneOf.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/pattern.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/properties.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/propertyNames.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/ref.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/required.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/uniqueItems.js create mode 100644 front_end/third_party/mcp-sdk/lib/dotjs/validate.js create mode 100644 front_end/third_party/mcp-sdk/lib/keyword.js create mode 100644 front_end/third_party/mcp-sdk/lib/refs/data.json create mode 100644 front_end/third_party/mcp-sdk/lib/refs/json-schema-draft-04.json create mode 100644 front_end/third_party/mcp-sdk/lib/refs/json-schema-draft-06.json create mode 100644 front_end/third_party/mcp-sdk/lib/refs/json-schema-draft-07.json rename front_end/third_party/mcp-sdk/{ajv/dist => lib}/refs/json-schema-secure.json (88%) create mode 100644 front_end/third_party/mcp-sdk/mcp-sdk-v2.ts create mode 100644 front_end/third_party/mcp-sdk/package.json create mode 100644 front_end/third_party/mcp-sdk/scripts/.eslintrc.yml create mode 100644 front_end/third_party/mcp-sdk/scripts/bundle.js create mode 100644 front_end/third_party/mcp-sdk/scripts/compile-dots.js create mode 100644 front_end/third_party/mcp-sdk/scripts/info create mode 100644 front_end/third_party/mcp-sdk/scripts/prepare-tests create mode 100644 front_end/third_party/mcp-sdk/scripts/publish-built-version create mode 100644 front_end/third_party/mcp-sdk/scripts/travis-gh-pages delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/cli.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/cli.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/cli.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/cli.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/index.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/index.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/index.test.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/index.test.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/index.test.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/index.test.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/sse.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/sse.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/sse.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/sse.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/stdio.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/stdio.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/stdio.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/stdio.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/stdio.test.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/stdio.test.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/stdio.test.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/stdio.test.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/websocket.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/websocket.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/websocket.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/client/websocket.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/inMemory.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/inMemory.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/inMemory.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/inMemory.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/inMemory.test.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/inMemory.test.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/inMemory.test.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/inMemory.test.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/index.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/index.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/index.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/index.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/index.test.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/index.test.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/index.test.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/index.test.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/sse.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/sse.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/sse.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/sse.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/stdio.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/stdio.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/stdio.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/stdio.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/stdio.test.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/stdio.test.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/stdio.test.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/server/stdio.test.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/protocol.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/protocol.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/protocol.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/protocol.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/stdio.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/stdio.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/stdio.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/stdio.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/stdio.test.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/stdio.test.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/stdio.test.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/stdio.test.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/transport.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/transport.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/transport.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/shared/transport.js.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/types.d.ts delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/types.d.ts.map delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/types.js delete mode 100644 front_end/third_party/mcp-sdk/zod/dist/types.js.map create mode 100644 front_end/third_party/mcp-sdk/zod/zod-esm.js diff --git a/config/gni/devtools_grd_files.gni b/config/gni/devtools_grd_files.gni index 0daec877f6b..260e1a15653 100644 --- a/config/gni/devtools_grd_files.gni +++ b/config/gni/devtools_grd_files.gni @@ -313,6 +313,19 @@ grd_files_bundled_sources = [ "front_end/Images/whatsnew.svg", "front_end/Images/width.svg", "front_end/Images/zoom-in.svg", + "front_end/Images/asana-mcp.svg", + "front_end/Images/atlassian-mcp.svg", + "front_end/Images/github-mcp.svg", + "front_end/Images/google-drive-mcp.svg", + "front_end/Images/google-sheets-mcp.svg", + "front_end/Images/huggingface-mcp.svg", + "front_end/Images/intercom-mcp.svg", + "front_end/Images/invideo-mcp.svg", + "front_end/Images/linear-mcp.svg", + "front_end/Images/notion-mcp.svg", + "front_end/Images/sentry-mcp.svg", + "front_end/Images/slack-mcp.svg", + "front_end/Images/socket-mcp.svg", "front_end/Tests.js", "front_end/application_tokens.css", "front_end/core/common/common.js", @@ -638,10 +651,13 @@ grd_files_bundled_sources = [ "front_end/panels/ai_chat/ui/HelpDialog.js", "front_end/panels/ai_chat/ui/PromptEditDialog.js", "front_end/panels/ai_chat/ui/SettingsDialog.js", + "front_end/panels/ai_chat/ui/mcp/MCPConnectionsDialog.js", + "front_end/panels/ai_chat/ui/mcp/MCPConnectorsCatalogDialog.js", "front_end/panels/ai_chat/ui/EvaluationDialog.js", "front_end/panels/ai_chat/core/AgentService.js", "front_end/panels/ai_chat/core/State.js", "front_end/panels/ai_chat/core/Graph.js", + "front_end/panels/ai_chat/core/BuildConfig.js", "front_end/panels/ai_chat/core/Types.js", "front_end/panels/ai_chat/core/Constants.js", "front_end/panels/ai_chat/core/ConfigurableGraph.js", @@ -655,6 +671,7 @@ grd_files_bundled_sources = [ "front_end/panels/ai_chat/core/StateGraph.js", "front_end/panels/ai_chat/core/Logger.js", "front_end/panels/ai_chat/core/AgentErrorHandler.js", + "front_end/panels/ai_chat/core/AgentDescriptorRegistry.js", "front_end/panels/ai_chat/core/Version.js", "front_end/panels/ai_chat/core/VersionChecker.js", "front_end/panels/ai_chat/LLM/LLMTypes.js", @@ -720,6 +737,20 @@ grd_files_bundled_sources = [ "front_end/panels/ai_chat/agent_framework/AgentSessionTypes.js", "front_end/panels/ai_chat/agent_framework/ConfigurableAgentTool.js", "front_end/panels/ai_chat/agent_framework/implementation/ConfiguredAgents.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/ActionAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/ActionVerificationAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/AgentVersion.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/ClickActionAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/ContentWriterAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/DirectURLNavigatorAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/EcommerceProductInfoAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/FormFillActionAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/HoverActionAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/KeyboardInputActionAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/ResearchAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/ScrollActionAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/WebTaskAgent.js", + "front_end/panels/ai_chat/agent_framework/implementation/agents/SearchAgent.js", "front_end/panels/ai_chat/common/MarkdownViewerUtil.js", "front_end/panels/ai_chat/evaluation/runner/VisionAgentEvaluationRunner.js", "front_end/panels/ai_chat/evaluation/runner/EvaluationRunner.js", @@ -880,6 +911,7 @@ grd_files_bundled_sources = [ "front_end/third_party/lighthouse/report/report.js", "front_end/third_party/lit/lit.js", "front_end/third_party/mcp-sdk/mcp-sdk.js", + "front_end/third_party/mcp-sdk/mcp-sdk-v2.js", "front_end/third_party/marked/marked.js", "front_end/third_party/puppeteer-replay/puppeteer-replay.js", "front_end/third_party/puppeteer/puppeteer.js", @@ -2239,15 +2271,25 @@ grd_files_unbundled_sources = [ "front_end/third_party/lit/lib/static-html.js", "front_end/third_party/marked/package/lib/marked.esm.js", "front_end/third_party/mcp-sdk/ajv/dist/ajv.js", + "front_end/third_party/mcp-sdk/ajv/dist/ajv.bundle.js", + "front_end/third_party/mcp-sdk/ajv/dist/ajv-esm.js", "front_end/third_party/mcp-sdk/eventsource-parser/package/dist/index.js", "front_end/third_party/mcp-sdk/eventsource-parser/package/dist/stream.js", - "front_end/third_party/mcp-sdk/package/dist/client/index.js", - "front_end/third_party/mcp-sdk/package/dist/client/sse.js", - "front_end/third_party/mcp-sdk/package/dist/shared/protocol.js", - "front_end/third_party/mcp-sdk/package/dist/shared/transport.js", - "front_end/third_party/mcp-sdk/package/dist/types.js", + "front_end/third_party/mcp-sdk/dist/esm/client/auth.js", + "front_end/third_party/mcp-sdk/dist/esm/client/index.js", + "front_end/third_party/mcp-sdk/dist/esm/client/sse.js", + "front_end/third_party/mcp-sdk/dist/esm/client/streamableHttp.js", + "front_end/third_party/mcp-sdk/dist/esm/server/index.js", + "front_end/third_party/mcp-sdk/dist/esm/server/auth/errors.js", + "front_end/third_party/mcp-sdk/dist/esm/shared/auth.js", + "front_end/third_party/mcp-sdk/dist/esm/shared/auth-utils.js", + "front_end/third_party/mcp-sdk/dist/esm/shared/protocol.js", + "front_end/third_party/mcp-sdk/dist/esm/shared/transport.js", + "front_end/third_party/mcp-sdk/dist/esm/types.js", + "front_end/third_party/mcp-sdk/dist/zod/zod-esm.js", "front_end/third_party/mcp-sdk/zod/lib/index.js", "front_end/third_party/mcp-sdk/zod/lib/index.mjs", + "front_end/third_party/mcp-sdk/zod/zod-esm.js", "front_end/third_party/puppeteer-replay/package/lib/main.js", "front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.js", "front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BrowserContext.js", diff --git a/config/gni/devtools_image_files.gni b/config/gni/devtools_image_files.gni index 46c6e51e7dd..9ac14171838 100644 --- a/config/gni/devtools_image_files.gni +++ b/config/gni/devtools_image_files.gni @@ -308,6 +308,19 @@ devtools_svg_sources = [ "whatsnew.svg", "width.svg", "zoom-in.svg", + "asana-mcp.svg", + "atlassian-mcp.svg", + "github-mcp.svg", + "google-drive-mcp.svg", + "google-sheets-mcp.svg", + "huggingface-mcp.svg", + "intercom-mcp.svg", + "invideo-mcp.svg", + "linear-mcp.svg", + "notion-mcp.svg", + "sentry-mcp.svg", + "slack-mcp.svg", + "socket-mcp.svg", ] devtools_src_svg_files = [] diff --git a/eval-server/nodejs/evals/schema-extractor/amazon-product-001.yaml b/eval-server/nodejs/evals/schema-extractor/amazon-product-001.yaml index bfeb975979c..42e4738f0a9 100644 --- a/eval-server/nodejs/evals/schema-extractor/amazon-product-001.yaml +++ b/eval-server/nodejs/evals/schema-extractor/amazon-product-001.yaml @@ -9,7 +9,7 @@ target: wait_for: "networkidle" wait_timeout: 5000 -tool: "extract_schema_data" +tool: "extract_data" timeout: 60000 input: diff --git a/eval-server/nodejs/evals/schema-extractor/bbc-news-001.yaml b/eval-server/nodejs/evals/schema-extractor/bbc-news-001.yaml index e434d2a874a..68431471596 100644 --- a/eval-server/nodejs/evals/schema-extractor/bbc-news-001.yaml +++ b/eval-server/nodejs/evals/schema-extractor/bbc-news-001.yaml @@ -9,7 +9,7 @@ target: wait_for: "networkidle" wait_timeout: 5000 -tool: "extract_schema_data" +tool: "extract_data" timeout: 30000 input: diff --git a/eval-server/nodejs/evals/schema-extractor/bing-search-001.yaml b/eval-server/nodejs/evals/schema-extractor/bing-search-001.yaml index 8488f341b43..7e7d674c2d6 100644 --- a/eval-server/nodejs/evals/schema-extractor/bing-search-001.yaml +++ b/eval-server/nodejs/evals/schema-extractor/bing-search-001.yaml @@ -9,7 +9,7 @@ target: wait_for: "networkidle" wait_timeout: 5000 -tool: "extract_schema_data" +tool: "extract_data" timeout: 45000 input: diff --git a/eval-server/nodejs/evals/schema-extractor/github-repo-001.yaml b/eval-server/nodejs/evals/schema-extractor/github-repo-001.yaml index 7a01a14043e..669357779b7 100644 --- a/eval-server/nodejs/evals/schema-extractor/github-repo-001.yaml +++ b/eval-server/nodejs/evals/schema-extractor/github-repo-001.yaml @@ -9,7 +9,7 @@ target: wait_for: "networkidle" wait_timeout: 5000 -tool: "extract_schema_data" +tool: "extract_data" timeout: 30000 input: diff --git a/eval-server/nodejs/evals/schema-extractor/google-flights-001.yaml b/eval-server/nodejs/evals/schema-extractor/google-flights-001.yaml index 80da1bb7bb5..ab2e53c91e9 100644 --- a/eval-server/nodejs/evals/schema-extractor/google-flights-001.yaml +++ b/eval-server/nodejs/evals/schema-extractor/google-flights-001.yaml @@ -9,7 +9,7 @@ target: wait_for: "networkidle" wait_timeout: 5000 -tool: "extract_schema_data" +tool: "extract_data" timeout: 60000 input: diff --git a/eval-server/nodejs/evals/schema-extractor/google-search-001.yaml b/eval-server/nodejs/evals/schema-extractor/google-search-001.yaml index 7e6f0e6a4eb..5763ba83a33 100644 --- a/eval-server/nodejs/evals/schema-extractor/google-search-001.yaml +++ b/eval-server/nodejs/evals/schema-extractor/google-search-001.yaml @@ -9,7 +9,7 @@ target: wait_for: "networkidle" wait_timeout: 5000 -tool: "extract_schema_data" +tool: "extract_data" timeout: 45000 input: diff --git a/eval-server/nodejs/evals/schema-extractor/homedepot-001.yaml b/eval-server/nodejs/evals/schema-extractor/homedepot-001.yaml index 4e8b835b66d..2eb4883ffdf 100644 --- a/eval-server/nodejs/evals/schema-extractor/homedepot-001.yaml +++ b/eval-server/nodejs/evals/schema-extractor/homedepot-001.yaml @@ -9,7 +9,7 @@ target: wait_for: "networkidle" wait_timeout: 5000 -tool: "extract_schema_data" +tool: "extract_data" timeout: 60000 input: diff --git a/eval-server/nodejs/evals/schema-extractor/macys-001.yaml b/eval-server/nodejs/evals/schema-extractor/macys-001.yaml index 23a4e37dec0..81e05f9690d 100644 --- a/eval-server/nodejs/evals/schema-extractor/macys-001.yaml +++ b/eval-server/nodejs/evals/schema-extractor/macys-001.yaml @@ -9,7 +9,7 @@ target: wait_for: "networkidle" wait_timeout: 5000 -tool: "extract_schema_data" +tool: "extract_data" timeout: 60000 input: diff --git a/eval-server/nodejs/evals/schema-extractor/wikipedia-search-001.yaml b/eval-server/nodejs/evals/schema-extractor/wikipedia-search-001.yaml index ad5f2f43b82..616f0d6a82c 100644 --- a/eval-server/nodejs/evals/schema-extractor/wikipedia-search-001.yaml +++ b/eval-server/nodejs/evals/schema-extractor/wikipedia-search-001.yaml @@ -9,7 +9,7 @@ target: wait_for: "networkidle" wait_timeout: 5000 -tool: "extract_schema_data" +tool: "extract_data" timeout: 30000 input: diff --git a/eval-server/nodejs/evals/web-task-agent/jobs-001.yaml b/eval-server/nodejs/evals/web-task-agent/jobs-001.yaml index 47df0060949..7a6caa8cee7 100644 --- a/eval-server/nodejs/evals/web-task-agent/jobs-001.yaml +++ b/eval-server/nodejs/evals/web-task-agent/jobs-001.yaml @@ -46,7 +46,7 @@ validation: - "Either used construct_direct_url for LinkedIn job search OR used traditional form interaction" - "If using direct URL: constructed proper LinkedIn job search URL with keywords and location" - "If using forms: delegated keyword and location input to action_agent" - - "Extracted job listings using schema_based_extractor" + - "Extracted job listings using extract_data" - "Returned structured job data in readable text format (not JSON)" - "Each job listing includes title, company, location, and other relevant fields" - "Results are numbered or organized clearly for easy reading" diff --git a/eval-server/nodejs/evals/web-task-agent/realestate-001.yaml b/eval-server/nodejs/evals/web-task-agent/realestate-001.yaml index d8f36d1cc4c..5fd824ea79f 100644 --- a/eval-server/nodejs/evals/web-task-agent/realestate-001.yaml +++ b/eval-server/nodejs/evals/web-task-agent/realestate-001.yaml @@ -49,7 +49,7 @@ validation: - "Delegated price filter setting to action_agent" - "Coordinated property type selection through action_agent" - "Applied search filters through proper action_agent calls" - - "Extracted property listings with schema_based_extractor" + - "Extracted property listings with extract_data" - "Returned structured property data in readable text format (not JSON)" - "Each property includes address, price, bedrooms, bathrooms, and other key details" - "Properties are clearly numbered or organized for easy comparison" diff --git a/eval-server/nodejs/evals/web-task-agent/web-task-agent-jobs-001.yaml b/eval-server/nodejs/evals/web-task-agent/web-task-agent-jobs-001.yaml index a5f80d377b4..2c72df325aa 100644 --- a/eval-server/nodejs/evals/web-task-agent/web-task-agent-jobs-001.yaml +++ b/eval-server/nodejs/evals/web-task-agent/web-task-agent-jobs-001.yaml @@ -46,7 +46,7 @@ validation: - "Either used construct_direct_url for LinkedIn job search OR used traditional form interaction" - "If using direct URL: constructed proper LinkedIn job search URL with keywords and location" - "If using forms: delegated keyword and location input to action_agent" - - "Extracted job listings using schema_based_extractor" + - "Extracted job listings using extract_data" - "Returned structured job data in readable text format (not JSON)" - "Each job listing includes title, company, location, and other relevant fields" - "Results are numbered or organized clearly for easy reading" diff --git a/eval-server/nodejs/evals/web-task-agent/web-task-agent-realestate-001.yaml b/eval-server/nodejs/evals/web-task-agent/web-task-agent-realestate-001.yaml index 69757d302a4..f22bc139b96 100644 --- a/eval-server/nodejs/evals/web-task-agent/web-task-agent-realestate-001.yaml +++ b/eval-server/nodejs/evals/web-task-agent/web-task-agent-realestate-001.yaml @@ -49,7 +49,7 @@ validation: - "Delegated price filter setting to action_agent" - "Coordinated property type selection through action_agent" - "Applied search filters through proper action_agent calls" - - "Extracted property listings with schema_based_extractor" + - "Extracted property listings with extract_data" - "Returned structured property data in readable text format (not JSON)" - "Each property includes address, price, bedrooms, bathrooms, and other key details" - "Properties are clearly numbered or organized for easy comparison" diff --git a/eval-server/nodejs/schemas/client.schema.json b/eval-server/nodejs/schemas/client.schema.json index e23faab01f3..8dfdd3b172a 100644 --- a/eval-server/nodejs/schemas/client.schema.json +++ b/eval-server/nodejs/schemas/client.schema.json @@ -130,7 +130,7 @@ "tool": { "type": "string", "enum": [ - "extract_schema_data", + "extract_data", "extract_schema_streamlined", "research_agent", "action_agent", diff --git a/eval-server/nodejs/templates/default-client.yaml b/eval-server/nodejs/templates/default-client.yaml index 82cf18ee6ed..6ada1309532 100644 --- a/eval-server/nodejs/templates/default-client.yaml +++ b/eval-server/nodejs/templates/default-client.yaml @@ -27,7 +27,7 @@ evaluations: wait_for: "networkidle" wait_timeout: 5000 - tool: "extract_schema_data" + tool: "extract_data" timeout: 30000 input: diff --git a/front_end/Images/src/asana-mcp.svg b/front_end/Images/src/asana-mcp.svg new file mode 100644 index 00000000000..d627b4ea1cc --- /dev/null +++ b/front_end/Images/src/asana-mcp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/front_end/Images/src/atlassian-mcp.svg b/front_end/Images/src/atlassian-mcp.svg new file mode 100644 index 00000000000..a5078add21c --- /dev/null +++ b/front_end/Images/src/atlassian-mcp.svg @@ -0,0 +1,4 @@ + + + + diff --git a/front_end/Images/src/github-mcp.svg b/front_end/Images/src/github-mcp.svg new file mode 100644 index 00000000000..4af7f573f8a --- /dev/null +++ b/front_end/Images/src/github-mcp.svg @@ -0,0 +1,4 @@ + + + + diff --git a/front_end/Images/src/google-drive-mcp.svg b/front_end/Images/src/google-drive-mcp.svg new file mode 100644 index 00000000000..bd14ef66e2a --- /dev/null +++ b/front_end/Images/src/google-drive-mcp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/front_end/Images/src/google-sheets-mcp.svg b/front_end/Images/src/google-sheets-mcp.svg new file mode 100644 index 00000000000..2444a02398f --- /dev/null +++ b/front_end/Images/src/google-sheets-mcp.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/front_end/Images/src/huggingface-mcp.svg b/front_end/Images/src/huggingface-mcp.svg new file mode 100644 index 00000000000..2defcde95d2 --- /dev/null +++ b/front_end/Images/src/huggingface-mcp.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/front_end/Images/src/intercom-mcp.svg b/front_end/Images/src/intercom-mcp.svg new file mode 100644 index 00000000000..098eeffdb1b --- /dev/null +++ b/front_end/Images/src/intercom-mcp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front_end/Images/src/invideo-mcp.svg b/front_end/Images/src/invideo-mcp.svg new file mode 100644 index 00000000000..470fd078b14 --- /dev/null +++ b/front_end/Images/src/invideo-mcp.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/front_end/Images/src/linear-mcp.svg b/front_end/Images/src/linear-mcp.svg new file mode 100644 index 00000000000..bb830fd5ead --- /dev/null +++ b/front_end/Images/src/linear-mcp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front_end/Images/src/notion-mcp.svg b/front_end/Images/src/notion-mcp.svg new file mode 100644 index 00000000000..1cf62d78ac8 --- /dev/null +++ b/front_end/Images/src/notion-mcp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front_end/Images/src/sentry-mcp.svg b/front_end/Images/src/sentry-mcp.svg new file mode 100644 index 00000000000..657bc83c518 --- /dev/null +++ b/front_end/Images/src/sentry-mcp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/front_end/Images/src/slack-mcp.svg b/front_end/Images/src/slack-mcp.svg new file mode 100644 index 00000000000..5a9fb8ba2b6 --- /dev/null +++ b/front_end/Images/src/slack-mcp.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/front_end/Images/src/socket-mcp.svg b/front_end/Images/src/socket-mcp.svg new file mode 100644 index 00000000000..003ece6495b --- /dev/null +++ b/front_end/Images/src/socket-mcp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/front_end/panels/ai_chat/BUILD.gn b/front_end/panels/ai_chat/BUILD.gn index 2645f33ef0c..63b24e29f3c 100644 --- a/front_end/panels/ai_chat/BUILD.gn +++ b/front_end/panels/ai_chat/BUILD.gn @@ -53,6 +53,7 @@ devtools_module("ai_chat") { "core/GraphConfigs.ts", "core/ConfigurableGraph.ts", "core/BaseOrchestratorAgent.ts", + "core/AgentDescriptorRegistry.ts", "core/PageInfoManager.ts", "core/AgentNodes.ts", "core/GraphHelpers.ts", @@ -94,6 +95,20 @@ devtools_module("ai_chat") { "agent_framework/AgentRunner.ts", "agent_framework/AgentRunnerEventBus.ts", "agent_framework/AgentSessionTypes.ts", + "agent_framework/implementation/agents/AgentVersion.ts", + "agent_framework/implementation/agents/DirectURLNavigatorAgent.ts", + "agent_framework/implementation/agents/ResearchAgent.ts", + "agent_framework/implementation/agents/ContentWriterAgent.ts", + "agent_framework/implementation/agents/ActionAgent.ts", + "agent_framework/implementation/agents/ActionVerificationAgent.ts", + "agent_framework/implementation/agents/ClickActionAgent.ts", + "agent_framework/implementation/agents/FormFillActionAgent.ts", + "agent_framework/implementation/agents/KeyboardInputActionAgent.ts", + "agent_framework/implementation/agents/HoverActionAgent.ts", + "agent_framework/implementation/agents/ScrollActionAgent.ts", + "agent_framework/implementation/agents/WebTaskAgent.ts", + "agent_framework/implementation/agents/EcommerceProductInfoAgent.ts", + "agent_framework/implementation/agents/SearchAgent.ts", "agent_framework/implementation/ConfiguredAgents.ts", "evaluation/framework/types.ts", "evaluation/framework/judges/LLMEvaluator.ts", @@ -129,6 +144,8 @@ devtools_module("ai_chat") { "mcp/MCPToolAdapter.ts", "mcp/MCPRegistry.ts", "mcp/MCPMetaTools.ts", + "ui/mcp/MCPConnectionsDialog.ts", + "ui/mcp/MCPConnectorsCatalogDialog.ts", ] deps = [ @@ -139,6 +156,7 @@ devtools_module("ai_chat") { "../../generated:protocol", "../../models/logs:bundle", "../../third_party/mcp-sdk:bundle", + "../../third_party/mcp-sdk:bundle_v2", "../../ui/components/helpers:bundle", "../../ui/components/markdown_view:bundle", "../../ui/components/snackbars:bundle", @@ -177,6 +195,8 @@ _ai_chat_sources = [ "ui/PromptEditDialog.ts", "ui/SettingsDialog.ts", "ui/EvaluationDialog.ts", + "ui/mcp/MCPConnectionsDialog.ts", + "ui/mcp/MCPConnectorsCatalogDialog.ts", "ai_chat_impl.ts", "models/ChatTypes.ts", "core/Graph.ts", @@ -189,6 +209,7 @@ _ai_chat_sources = [ "core/GraphConfigs.ts", "core/ConfigurableGraph.ts", "core/BaseOrchestratorAgent.ts", + "core/AgentDescriptorRegistry.ts", "core/PageInfoManager.ts", "core/AgentNodes.ts", "core/GraphHelpers.ts", @@ -231,6 +252,20 @@ _ai_chat_sources = [ "agent_framework/AgentRunnerEventBus.ts", "agent_framework/AgentSessionTypes.ts", "agent_framework/implementation/ConfiguredAgents.ts", + "agent_framework/implementation/agents/ActionAgent.ts", + "agent_framework/implementation/agents/ActionVerificationAgent.ts", + "agent_framework/implementation/agents/AgentVersion.ts", + "agent_framework/implementation/agents/ClickActionAgent.ts", + "agent_framework/implementation/agents/ContentWriterAgent.ts", + "agent_framework/implementation/agents/DirectURLNavigatorAgent.ts", + "agent_framework/implementation/agents/EcommerceProductInfoAgent.ts", + "agent_framework/implementation/agents/FormFillActionAgent.ts", + "agent_framework/implementation/agents/HoverActionAgent.ts", + "agent_framework/implementation/agents/KeyboardInputActionAgent.ts", + "agent_framework/implementation/agents/ResearchAgent.ts", + "agent_framework/implementation/agents/ScrollActionAgent.ts", + "agent_framework/implementation/agents/SearchAgent.ts", + "agent_framework/implementation/agents/WebTaskAgent.ts", "evaluation/framework/types.ts", "evaluation/framework/judges/LLMEvaluator.ts", "evaluation/framework/GenericToolEvaluator.ts", @@ -361,8 +396,19 @@ ts_library("unittests") { "agent_framework/__tests__/AgentRunner.sanitizeToolResult.test.ts", "agent_framework/__tests__/AgentRunner.computeToolResultText.test.ts", "agent_framework/__tests__/AgentRunner.run.flows.test.ts", - "mcp/MCPClientSDK.test.ts", - "core/ToolSurfaceProvider.test.ts", + "mcp/__tests__/MCPClientSDK.test.ts", + "mcp/__tests__/MCPConfig.test.ts", + "mcp/__tests__/MCPIntegration.test.ts", + "mcp/__tests__/MCPToolRegistration.test.ts", + "core/__tests__/AgentNodes.test.ts", + "core/__tests__/AgentNodesSanitize.test.ts", + "core/__tests__/ToolExecutorNode.test.ts", + "core/__tests__/ToolNameMap.test.ts", + "core/__tests__/ToolNameMapping.test.ts", + "core/__tests__/ToolSurfaceProvider.test.ts", + "ui/__tests__/AIChatPanel.test.ts", + "ui/__tests__/LiveAgentSessionComponent.test.ts", + "ui/message/__tests__/MessageList.test.ts", ] deps = [ diff --git a/front_end/panels/ai_chat/agent_framework/AgentRunner.ts b/front_end/panels/ai_chat/agent_framework/AgentRunner.ts index a0ff010650a..6d87b0a3b48 100644 --- a/front_end/panels/ai_chat/agent_framework/AgentRunner.ts +++ b/front_end/panels/ai_chat/agent_framework/AgentRunner.ts @@ -3,6 +3,7 @@ // found in the LICENSE file. import { enhancePromptWithPageContext } from '../core/PageInfoManager.js'; +import type { AgentDescriptor } from '../core/AgentDescriptorRegistry.js'; import { LLMClient } from '../LLM/LLMClient.js'; import type { LLMResponse, LLMMessage, LLMProvider } from '../LLM/LLMTypes.js'; import type { Tool } from '../tools/Tools.js'; @@ -38,6 +39,8 @@ export interface AgentRunnerConfig { miniModel?: string; /** Nano model for smallest/fastest operations */ nanoModel?: string; + /** Descriptor describing this agent configuration */ + agentDescriptor?: AgentDescriptor; } /** @@ -397,11 +400,12 @@ export class AgentRunner { hooks: AgentRunnerHooks, executingAgent: ConfigurableAgentTool | null, parentSession?: AgentSession, // For natural nesting - overrides?: { sessionId?: string; parentSessionId?: string; traceId?: string } + overrides?: { sessionId?: string; parentSessionId?: string; traceId?: string }, + abortSignal?: AbortSignal ): Promise { const agentName = executingAgent?.name || 'Unknown'; logger.info(`Starting execution loop for agent: ${agentName}`); - const { apiKey, modelName, systemPrompt, tools, maxIterations, temperature } = config; + const { apiKey, modelName, systemPrompt, tools, maxIterations, temperature, agentDescriptor } = config; const { prepareInitialMessages, createSuccessResult, createErrorResult } = hooks; @@ -422,7 +426,8 @@ export class AgentRunner { config: executingAgent?.config, maxIterations, modelUsed: modelName, - iterationCount: 0 + iterationCount: 0, + descriptor: agentDescriptor }; // Use local session variable instead of static @@ -547,6 +552,13 @@ export class AgentRunner { let iteration = 0; // Initialize iteration counter for (iteration = 0; iteration < maxIterations; iteration++) { + // Check if execution has been aborted + if (abortSignal?.aborted) { + logger.info(`${agentName} execution aborted at iteration ${iteration + 1}/${maxIterations}`); + const abortResult = createErrorResult('Execution was cancelled', messages, 'error'); + return { ...abortResult, agentSession: currentSession }; + } + // Update session iteration count if (currentSession) { currentSession.iterationCount = iteration + 1; @@ -609,7 +621,12 @@ export class AgentRunner { agentName, iteration: iteration + 1, maxIterations, - phase: 'llm_generation' + phase: 'llm_generation', + ...(agentDescriptor ? { + agentVersion: agentDescriptor.version, + promptHash: agentDescriptor.promptHash, + toolsetHash: agentDescriptor.toolsetHash + } : {}) } }, tracingContext.traceId); @@ -675,7 +692,12 @@ export class AgentRunner { agentName, iteration: iteration + 1, phase: 'completed', - duration: Date.now() - generationStartTime.getTime() + duration: Date.now() - generationStartTime.getTime(), + ...(agentDescriptor ? { + agentVersion: agentDescriptor.version, + promptHash: agentDescriptor.promptHash, + toolsetHash: agentDescriptor.toolsetHash + } : {}) } }); @@ -702,7 +724,12 @@ export class AgentRunner { source: 'AgentRunner', agentName, iteration: iteration + 1, - phase: 'error' + phase: 'error', + ...(agentDescriptor ? { + agentVersion: agentDescriptor.version, + promptHash: agentDescriptor.promptHash, + toolsetHash: agentDescriptor.toolsetHash + } : {}) } }); } diff --git a/front_end/panels/ai_chat/agent_framework/AgentSessionTypes.ts b/front_end/panels/ai_chat/agent_framework/AgentSessionTypes.ts index 881146a3cfc..41aae1eba09 100644 --- a/front_end/panels/ai_chat/agent_framework/AgentSessionTypes.ts +++ b/front_end/panels/ai_chat/agent_framework/AgentSessionTypes.ts @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import type { AgentDescriptor } from '../core/AgentDescriptorRegistry.js'; import type { AgentToolConfig } from './ConfigurableAgentTool.js'; /** @@ -30,6 +31,7 @@ export interface AgentSession { reasoning?: string; tools: string[]; config?: AgentToolConfig; + descriptor?: AgentDescriptor; // Execution metadata iterationCount?: number; @@ -159,4 +161,4 @@ export function getAgentUIConfig(agentName: string, config?: AgentToolConfig) { color: config?.ui?.color || DEFAULT_AGENT_UI.color, backgroundColor: config?.ui?.backgroundColor || DEFAULT_AGENT_UI.backgroundColor }; -} \ No newline at end of file +} diff --git a/front_end/panels/ai_chat/agent_framework/ConfigurableAgentTool.ts b/front_end/panels/ai_chat/agent_framework/ConfigurableAgentTool.ts index f13079556c3..6ef74bf40a9 100644 --- a/front_end/panels/ai_chat/agent_framework/ConfigurableAgentTool.ts +++ b/front_end/panels/ai_chat/agent_framework/ConfigurableAgentTool.ts @@ -5,12 +5,14 @@ import type { Tool } from '../tools/Tools.js'; import { ChatMessageEntity, type ChatMessage } from '../models/ChatTypes.js'; import { createLogger } from '../core/Logger.js'; +import { AgentDescriptorRegistry, type AgentDescriptor } from '../core/AgentDescriptorRegistry.js'; import { getCurrentTracingContext } from '../tracing/TracingConfig.js'; import { MODEL_SENTINELS } from '../core/Constants.js'; import type { AgentSession } from './AgentSessionTypes.js'; import type { LLMProvider } from '../LLM/LLMTypes.js'; const logger = createLogger('ConfigurableAgentTool'); +const DEFAULT_AGENT_TOOL_VERSION = '2025-09-17'; import { AgentRunner, type AgentRunnerConfig, type AgentRunnerHooks } from './AgentRunner.js'; @@ -26,6 +28,8 @@ export interface CallCtx { overrideSessionId?: string, overrideParentSessionId?: string, overrideTraceId?: string, + abortSignal?: AbortSignal, + agentDescriptor?: AgentDescriptor, } /** @@ -111,6 +115,11 @@ export interface AgentToolConfig { */ tools: string[]; + /** + * Semantic version identifier for this agent configuration + */ + version?: string; + /** * Defines potential handoffs to other agents. * Handoffs triggered by 'llm_tool_call' are presented as tools to the LLM. @@ -315,6 +324,21 @@ export class ConfigurableAgentTool implements Tool config.systemPrompt, + toolNamesProvider: () => [...config.tools], + metadataProvider: () => ({ + handoffs: (config.handoffs || []).map(handoff => ({ + targetAgentName: handoff.targetAgentName, + trigger: handoff.trigger || 'llm_tool_call', + includeToolResults: handoff.includeToolResults ? [...handoff.includeToolResults] : undefined + })) + }) + }); + // Call custom init function directly if provided if (config.init) { config.init(this); @@ -481,6 +505,12 @@ export class ConfigurableAgentTool implements Tool new NavigateBackTool()); ToolRegistry.registerToolFactory('node_ids_to_urls', () => new NodeIDsToURLsTool()); ToolRegistry.registerToolFactory('fetcher_tool', () => new FetcherTool()); - ToolRegistry.registerToolFactory('schema_based_extractor', () => new SchemaBasedExtractorTool()); - ToolRegistry.registerToolFactory('extract_schema_data', () => new SchemaBasedExtractorTool()); + ToolRegistry.registerToolFactory('extract_data', () => new SchemaBasedExtractorTool()); ToolRegistry.registerToolFactory('extract_schema_streamlined', () => new StreamlinedSchemaExtractorTool()); ToolRegistry.registerToolFactory('finalize_with_critique', () => new FinalizeWithCritiqueTool()); ToolRegistry.registerToolFactory('perform_action', () => new PerformActionTool()); @@ -128,6 +64,11 @@ export function initializeConfiguredAgents(): void { const researchAgent = new ConfigurableAgentTool(researchAgentConfig); ToolRegistry.registerToolFactory('research_agent', () => researchAgent); + // Create and register Search Agent + const searchAgentConfig = createSearchAgentConfig(); + const searchAgent = new ConfigurableAgentTool(searchAgentConfig); + ToolRegistry.registerToolFactory('search_agent', () => searchAgent); + // Create and register Content Writer Agent const contentWriterAgentConfig = createContentWriterAgentConfig(); const contentWriterAgent = new ConfigurableAgentTool(contentWriterAgentConfig); @@ -175,1278 +116,3 @@ export function initializeConfiguredAgents(): void { ToolRegistry.registerToolFactory('ecommerce_product_info_fetcher_tool', () => ecommerceProductInfoAgent); } - -/** - * Create the configuration for the Research Agent - */ -function createResearchAgentConfig(): AgentToolConfig { - return { - name: 'research_agent', - description: 'Performs in-depth research on a specific query autonomously using multiple steps and internal tool calls (navigation, fetching, extraction). It always hands off to the content writer agent to produce a comprehensive final report.', - ui: { - displayName: 'Research Agent', - avatar: '🔍', - color: '#3b82f6', - backgroundColor: '#f8fafc' - }, - systemPrompt: `You are a research subagent working as part of a team. You have been given a specific research task with clear requirements. Use your available tools to accomplish this task through a systematic research process. - -## Understanding Your Task - -You will receive: -- **task**: The specific research objective to accomplish -- **reasoning**: Why this research is being conducted (shown to the user) -- **context**: Additional details about constraints or focus areas (optional) -- **scope**: Whether this is a focused, comprehensive, or exploratory investigation -- **priority_sources**: Specific sources to prioritize if provided - -Adapt your research approach based on the scope: -- **Focused**: 3-5 tool calls, quick specific answers -- **Comprehensive**: 5-10 tool calls, in-depth analysis from multiple sources -- **Exploratory**: 10-15 tool calls, broad investigation of the topic landscape - -## Research Process - -### 1. Planning Phase -First, think through the task thoroughly: -- Review the task requirements and any provided context -- Note the scope (focused/comprehensive/exploratory) to determine effort level -- Check for priority_sources to guide your search strategy -- Determine your research budget based on scope: - - Focused scope: 3-5 tool calls for quick, specific answers - - Comprehensive scope: 5-10 tool calls for detailed analysis - - Exploratory scope: 10-15 tool calls for broad investigation -- Identify which tools are most relevant for the task - -### 2. Tool Selection Strategy -Choose tools based on task requirements: -- **navigate_url** + **fetcher_tool**: Core research loop - navigate to search engines, then fetch complete content -- **schema_based_extractor**: Extract structured data from search results (URLs, titles, snippets) -- **fetcher_tool**: BATCH PROCESS multiple URLs at once - accepts an array of URLs to save tool calls -- **document_search**: Search within documents for specific information -- **bookmark_store**: Save important sources for reference - -**CRITICAL - Batch URL Fetching**: -- The fetcher_tool accepts an ARRAY of URLs: {urls: [url1, url2, url3], reasoning: "..."} -- ALWAYS batch multiple URLs together instead of calling fetcher_tool multiple times -- Example: After extracting 5 URLs from search results, call fetcher_tool ONCE with all 5 URLs -- This dramatically reduces tool calls and improves efficiency - -### 3. Research Loop (OODA) -Execute an excellent Observe-Orient-Decide-Act loop: - -**Observe**: What information has been gathered? What's still needed? -**Orient**: What tools/queries would best gather needed information? -**Decide**: Make informed decisions on specific tool usage -**Act**: Execute the tool call - -**Efficient Research Workflow**: -1. Use navigate_url to search for your topic -2. Use schema_based_extractor to collect ALL URLs from search results -3. Call fetcher_tool ONCE with the array of all extracted URLs -4. Analyze the batch results and determine if more searches are needed -5. Repeat with different search queries if necessary - -- Execute a MINIMUM of 5 distinct tool calls for comprehensive research -- Maximum of 15 tool calls to prevent system overload -- Batch processing URLs counts as ONE tool call, making research much more efficient -- NEVER repeat the same query - adapt based on findings -- If hitting diminishing returns, complete the task immediately - -### 4. Source Quality Evaluation -Think critically about sources: -- Distinguish facts from speculation (watch for "could", "may", future tense) -- Identify problematic sources (aggregators vs. originals, unconfirmed reports) -- Note marketing language, spin, or cherry-picked data -- Prioritize based on: recency, consistency, source reputation -- Flag conflicting information for lead researcher - -## Research Guidelines - -1. **Query Optimization**: - - Use moderately broad queries (under 5 words) - - Avoid hyper-specific searches with poor hit rates - - Adjust specificity based on result quality - - Balance between specific and general - -2. **Information Focus** - Prioritize high-value information that is: - - **Significant**: Major implications for the task - - **Important**: Directly relevant or specifically requested - - **Precise**: Specific facts, numbers, dates, concrete data - - **High-quality**: From reputable, reliable sources - -3. **Documentation Requirements**: - - State which tool you're using and why - - Document each source with URL and title - - Extract specific quotes, statistics, facts with attribution - - Organize findings by source with clear citations - - Include publication dates where available - -4. **Efficiency Principles**: - - BATCH PROCESS URLs: Always use fetcher_tool with multiple URLs at once - - Use parallel tool calls when possible (2 tools simultaneously) - - Complete task as soon as sufficient information is gathered - - Stop at ~15 tool calls or when hitting diminishing returns - - Be detailed in process but concise in reporting - - Remember: Fetching 10 URLs in one batch = 1 tool call vs 10 individual calls - -## Output Structure -Structure findings as: -- Source 1: [Title] (URL) - [Date if available] - - Key facts: [specific quotes/data] - - Statistics: [numbers with context] - - Expert opinions: [attributed quotes] -- Source 2: [Title] (URL) - - [Continue pattern...] - -## Critical Reminders -- This is autonomous tool execution - complete the full task in one run -- NO conversational elements - execute research automatically -- Gather from 3-5+ diverse sources minimum -- DO NOT generate markdown reports or final content yourself -- Focus on gathering raw research data with proper citations - -## IMPORTANT: Handoff Protocol -When your research is complete: -1. NEVER generate markdown content or final reports yourself -2. Use the handoff_to_content_writer_agent tool to pass your research findings -3. The handoff tool expects: {query: "research topic", reasoning: "explanation for user"} -4. The content_writer_agent will create the final report from your research data - -Remember: You gather data, content_writer_agent writes the report. Always hand off when research is complete.`, - tools: [ - 'navigate_url', - 'navigate_back', - 'fetcher_tool', - 'schema_based_extractor', - 'node_ids_to_urls', - 'bookmark_store', - 'document_search' - ], - maxIterations: 15, - modelName: MODEL_SENTINELS.USE_MINI, - temperature: 0, - schema: { - type: 'object', - properties: { - query: { - type: 'string', - description: 'The specific research task to accomplish, including clear requirements and expected deliverables.' - }, - reasoning: { - type: 'string', - description: 'Clear explanation for the user about why this research is being conducted and what to expect.' - }, - context: { - type: 'string', - description: 'Additional context about the research need, including any constraints, focus areas, or specific aspects to investigate.' - }, - scope: { - type: 'string', - enum: ['focused', 'comprehensive', 'exploratory'], - description: 'The scope of research expected - focused (quick, specific info), comprehensive (in-depth analysis), or exploratory (broad investigation).', - default: 'comprehensive' - }, - }, - required: ['query', 'reasoning'] - }, - prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { - // For the action agent, we use the objective as the primary input, not the query field - return [{ - entity: ChatMessageEntity.USER, - text: `Task: ${args.query? `${args.query}` : ''} - ${args.task? `${args.task}` : ''} - ${args.objective? `${args.objective}` : ''} -${args.context ? `Context: ${args.context}` : ''} -${args.scope ? `The scope of research expected: ${args.scope}` : ''} -`, - }]; - }, - handoffs: [ - { - targetAgentName: 'content_writer_agent', - trigger: 'llm_tool_call', - includeToolResults: ['fetcher_tool', 'schema_based_extractor'] - }, - { - targetAgentName: 'content_writer_agent', - trigger: 'max_iterations', - includeToolResults: ['fetcher_tool', 'schema_based_extractor'] - } - ], - }; -} - -/** - * Create the configuration for the Content Writer Agent - */ -function createContentWriterAgentConfig(): AgentToolConfig { - return { - name: 'content_writer_agent', - description: 'Writes detailed, well-structured reports based on research data. Creates an outline and then builds a comprehensive markdown report with proper structure, citations, and detailed information.', - ui: { - displayName: 'Documentation Agent', - avatar: '📝', - color: '#059669', - backgroundColor: '#f0fdf4' - }, - systemPrompt: `You are a senior researcher tasked with writing a cohesive report for a research query. -You will be provided with the original query, and research data collected by a research assistant. - -## Receiving Handoff from Research Agent -You are specifically designed to collaborate with the research_agent. When you receive a handoff, you'll be provided with: -- The original research query -- Collected research data, which may include web content, extractions, analysis, and other information -- Your job is to organize this information into a comprehensive, well-structured report - -Your process should follow these steps: -1. Carefully analyze all the research data provided during the handoff -2. Identify key themes, findings, and important information from the data -3. Create a detailed outline for the report with clear sections and subsections -4. Generate a comprehensive report following your outline - -## Here is an example of the final report structure (you can come up with your own structure that is better for the user's query): - -1. **Title**: A concise, descriptive title for the report -2. **Executive Summary**: Brief overview summarizing the key findings and conclusions -3. **Introduction**: Context, importance of the topic, and research questions addressed -4. **Methodology**: How the research was conducted (when applicable) -5. **Main Body**: Organized by themes or topics with detailed analysis of findings - - Include sections and subsections as appropriate - - Support claims with evidence from the research - - Address counterarguments when relevant - - Use examples, case studies, or data to illustrate points -6. **Analysis/Discussion**: Synthesis of information, highlighting patterns, connections, and insights -7. **Implications**: Practical applications or theoretical significance of the findings -8. **Limitations**: Acknowledge limitations of the research or data -9. **Conclusion**: Summary of key points and final thoughts -10. **References**: Properly formatted citations for all sources used - -The final output should be in markdown format, and it should be lengthy and detailed. Aim for 5-10 pages of content, at least 1000 words.`, - tools: [], - maxIterations: 3, - modelName: MODEL_SENTINELS.USE_MINI, - temperature: 0.3, - schema: { - type: 'object', - properties: { - query: { - type: 'string', - description: 'The original research question or topic that was investigated.' - }, - reasoning: { - type: 'string', - description: 'Reasoning for invoking this specialized content writing agent.' - }, - }, - required: ['query', 'reasoning'] - }, - handoffs: [], - }; -} - -/** - * Create the configuration for the Action Agent - */ -function createActionAgentConfig(): AgentToolConfig { - return { - name: 'action_agent', - description: 'Executes a single, low-level browser action with enhanced targeting precision (such as clicking a button, filling a field, selecting an option, or scrolling) on the current web page, based on a clear, actionable objective. ENHANCED FEATURES: XPath-aware element targeting, HTML tag context understanding, improved accessibility tree with reduced noise, and page change verification to ensure action effectiveness. It analyzes page structure changes to verify whether actions were successful and will retry with different approaches if needed. Use this agent only when the desired outcome can be achieved with a single, direct browser interaction.', - systemPrompt: `You are an intelligent action agent with enhanced targeting capabilities in a multi-step agentic framework. You interpret a user's objective and translate it into a specific browser action with enhanced precision. Your task is to: - -1. Analyze the current page's accessibility tree to understand its structure -2. Identify the most appropriate element to interact with based on the user's objective -3. Determine the correct action to perform (click, fill, type, etc.) -4. Execute that action precisely -5. **Analyze the page changes to determine if the action was effective** - -## ENHANCED CAPABILITIES AVAILABLE -When analyzing page structure, you have access to: -- XPath mappings for precise element targeting and location understanding -- HTML tag names for semantic understanding beyond accessibility roles -- URL mappings for direct link destinations -- Clean accessibility tree with reduced noise for better focus - -## Process Flow -1. When given an objective, first analyze the page structure using get_page_content tool to access the enhanced accessibility tree or use schema_based_extractor to extract the specific element you need to interact with -2. Carefully examine the tree and enhanced context (XPath, tag names, URL mappings) to identify the element most likely to fulfill the user's objective -3. Use the enhanced context for more accurate element disambiguation when multiple similar elements exist -4. Determine the appropriate action method based on the element type and objective: - - For links, buttons: use 'click' - - For checkboxes: use 'check' (to check), 'uncheck' (to uncheck), or 'setChecked' (to set to specific state) - - For radio buttons: use 'click' - - For input fields: use 'fill' with appropriate text - - For dropdown/select elements: use 'selectOption' with the option value or text -5. Execute the action using perform_action tool -6. **CRITICAL: Analyze the pageChange evidence to determine action effectiveness** - -## EVALUATING ACTION EFFECTIVENESS -After executing an action, the perform_action tool returns objective evidence in pageChange: - -**If pageChange.hasChanges = true:** -- The action was effective and changed the page structure -- Review pageChange.summary to understand what changed -- Check pageChange.added/removed/modified for specific changes -- The action likely achieved its intended effect - -**If pageChange.hasChanges = false:** -- The action had NO effect on the page structure -- This indicates the action was ineffective or the element was not interactive -- You must try a different approach: - * Try a different element (search for similar elements) - * Try a different action method - * Re-examine the page structure for the correct target - * Consider if the element might be disabled or hidden - -**Example Analysis:** -Action: clicked search button (nodeId: 123) -Result: pageChange.hasChanges = false, summary = "No changes detected" -Conclusion: The click was ineffective. Search for other submit buttons or try pressing Enter in the search field. - -**Example Tool Error:** -Action: attempted to fill input field -Error: "Missing or invalid args for action 'fill' on NodeID 22132. Expected an object with a string property 'text'. Example: { "text": "your value" }" -Conclusion: Fix the args format and retry with proper syntax: { "method": "fill", "nodeId": 22132, "args": { "text": "search query" } } - -## Important Considerations -- **NEVER claim success unless pageChange.hasChanges = true** -- Be precise in your element selection, using the exact nodeId from the accessibility tree -- Leverage XPath information when available for more precise element targeting -- Use HTML tag context to better understand element semantics -- Use URL mappings to identify link destinations when relevant to the objective -- Match the action type to the element type (don't try to 'fill' a button or 'click' a select element) -- When filling forms, ensure the data format matches what the field expects -- For checkboxes, prefer 'check'/'uncheck' over 'click' for better reliability -- For dropdowns, use 'selectOption' with the visible text or value of the option you want to select -- If pageChange shows no changes, immediately try an alternative approach - -## Method Examples -- perform_action with method='check' for checkboxes: { "method": "check", "nodeId": 123 } -- perform_action with method='selectOption' for dropdowns: { "method": "selectOption", "nodeId": 456, "args": { "text": "United States" } } -- perform_action with method='setChecked' for specific checkbox state: { "method": "setChecked", "nodeId": 789, "args": { "checked": true } }`, - tools: [ - 'get_page_content', - 'perform_action', - 'schema_based_extractor', - 'node_ids_to_urls', - 'scroll_page', - 'take_screenshot', - ], - maxIterations: 10, - modelName: MODEL_SENTINELS.USE_MINI, - temperature: 0.5, - schema: { - type: 'object', - properties: { - objective: { - type: 'string', - description: 'The natural language description of the desired action (e.g., "click the login button", "fill the search box with \'query\'").' - }, - reasoning: { - type: 'string', - description: 'Reasoning for invoking this specialized action agent.' - }, - hint: { - type: 'string', - description: 'Feedback for the previous action agent failure. Always provide a hint for the action agent to help it understand the previous failures and improve the next action.' - }, - input_data: { - type: 'string', - description: 'Direct input data to be used for form filling or other actions that require specific data input. Provide the data in xml format.' - } - }, - required: ['objective', 'reasoning'] - }, - prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { - // For the action agent, we use the objective as the primary input, not the query field - return [{ - entity: ChatMessageEntity.USER, - text: `Objective: ${args.objective}\n -Reasoning: ${args.reasoning}\n -${args.hint ? `Hint: ${args.hint}` : ''} -${args.input_data ? `Input Data: ${args.input_data}` : ''} -`, - }]; - }, - handoffs: [ - { - targetAgentName: 'action_verification_agent', - trigger: 'llm_tool_call', - includeToolResults: ['perform_action', 'get_page_content'] - } - ], - }; -} - -/** - * Create the configuration for the Action Verification Agent - */ -function createActionVerificationAgentConfig(): AgentToolConfig { - return { - name: 'action_verification_agent', - description: 'Verifies that actions performed by the action agent were successful by analyzing the page state after action execution and confirming expected outcomes.', - systemPrompt: `You are a specialized verification agent responsible for determining whether an action was successfully completed. Your task is to analyze the page state after an action has been performed and verify whether the expected outcome was achieved. - -## Verification Process -1. Review the original objective that was given to the action agent -2. Understand what action was attempted (click, fill, etc.) and on which element -3. Analyze the current page state using available tools to determine if the expected outcome was achieved -4. Provide a clear verification result with supporting evidence - -## Verification Methods -Based on the action type, use different verification strategies: - -### For Click Actions: -- Check if a new page loaded or the URL changed -- Verify if expected elements appeared or disappeared -- Look for confirmation messages or success indicators -- Check if any error messages appeared - -### For Form Fill Actions: -- Verify the field contains the expected value -- Look for validation messages (success or error) -- Check if form was successfully submitted -- Monitor for any error messages - -### For Navigation Actions: -- Confirm the URL matches the expected destination -- Verify page title or key content matches expectations -- Check for any navigation errors in console logs - -### Visual Verification: -- Use take_screenshot tool to capture the current page state -- Compare visual elements to expected outcomes -- Document any visual anomalies or unexpected UI states - -## Tools to Use -- get_page_content: Examine the updated page structure -- search_content: Look for specific text indicating success/failure -- inspect_element: Check properties of specific elements -- get_console_logs: Check for errors or success messages in the console -- schema_based_extractor: Extract structured data to verify expected outcomes - -## Output Format -Provide a clear verification report with: -1. Action Summary: Brief description of the action that was attempted -2. Verification Result: Clear SUCCESS or FAILURE classification -3. Confidence Level: High, Medium, or Low confidence in your verification -4. Evidence: Specific observations that support your conclusion -5. Explanation: Reasoning behind your verification result - -Remember that verification is time-sensitive - the page state might change during your analysis, so perform verifications promptly and efficiently.`, - tools: [ - 'search_content', - 'inspect_element', - 'get_console_logs', - 'schema_based_extractor', - 'take_screenshot' - ], - maxIterations: 3, - modelName: MODEL_SENTINELS.USE_MINI, - temperature: 0.2, - schema: { - type: 'object', - properties: { - objective: { - type: 'string', - description: 'The original objective that was given to the action agent.' - }, - action_performed: { - type: 'string', - description: 'Description of the action that was performed (e.g., "clicked login button", "filled search field").' - }, - expected_outcome: { - type: 'string', - description: 'The expected outcome or success criteria for the action (e.g., "form submitted", "new page loaded").' - }, - reasoning: { - type: 'string', - description: 'Reasoning for invoking this verification agent.' - }, - }, - required: ['objective', 'reasoning'] - }, - prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { - return [{ - entity: ChatMessageEntity.USER, - text: `Verification Request: -Objective: ${args.objective} -${args.action_performed ? `Action Performed: ${args.action_performed}` : ''} -${args.expected_outcome ? `Expected Outcome: ${args.expected_outcome}` : ''} -Reasoning: ${args.reasoning} - -Please verify if the action was successfully completed and achieved its intended outcome.`, - }]; - }, - handoffs: [], - }; -} - -/** - * Create the configuration for the Click Action Agent - */ -function createClickActionAgentConfig(): AgentToolConfig { - return { - name: 'click_action_agent', - description: 'Specialized agent for clicking buttons, links, and other clickable elements on a webpage. Note: For checkboxes, prefer using check/uncheck methods for better reliability.', - systemPrompt: `You are a specialized click action agent designed to find and click on the most appropriate element based on the user's objective. - -## Your Specialized Skills -You excel at: -1. Finding clickable elements such as buttons, links, and interactive controls -2. Determining which element best matches the user's intention -3. Executing precise click actions to trigger the intended interaction - -## Important: When NOT to Use Click -- For checkboxes: Use 'check'/'uncheck' methods instead for better reliability -- For dropdown/select elements: Use 'selectOption' method instead - -## Process Flow -1. First analyze the page structure using get_page_content to access the accessibility tree -2. Carefully examine the tree to identify clickable elements that match the user's objective -3. Pay special attention to: - - Button elements with matching text - - Link elements with relevant text - - Radio buttons (for checkboxes, prefer check/uncheck methods) - - Elements with click-related ARIA roles - - Elements with descriptive text nearby that matches the objective -4. Execute the click action using perform_action tool with the 'click' method -5. If a click fails, try alternative elements that might fulfill the same function - -## Selection Guidelines -When selecting an element to click, prioritize: -- Elements with exact text matches to the user's request -- Elements with clear interactive roles (button, link) -- Elements positioned logically in the page context -- Elements with appropriate ARIA labels or descriptions -- Elements that are currently visible and enabled`, - tools: [ - 'get_page_content', - 'perform_action', - 'schema_based_extractor', - 'node_ids_to_urls', - ], - maxIterations: 5, - modelName: MODEL_SENTINELS.USE_MINI, - temperature: 0.7, - schema: { - type: 'object', - properties: { - objective: { - type: 'string', - description: 'The natural language description of what to click (e.g., "click the login button", "select the checkbox").' - }, - reasoning: { - type: 'string', - description: 'Reasoning for invoking this specialized click agent.' - }, - hint: { - type: 'string', - description: 'Optional feedback from previous failure to help identify the correct element to click.' - } - }, - required: ['objective', 'reasoning'] - }, - prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { - return [{ - entity: ChatMessageEntity.USER, - text: `Click Objective: ${args.objective}\n -Reasoning: ${args.reasoning}\n -${args.hint ? `Hint: ${args.hint}` : ''} -`, - }]; - }, - handoffs: [], - }; -} - -/** - * Create the configuration for the Form Fill Action Agent - */ -function createFormFillActionAgentConfig(): AgentToolConfig { - return { - name: 'form_fill_action_agent', - description: 'Specialized agent for filling form input fields like text boxes, search fields, and text areas with appropriate text.', - systemPrompt: `You are a specialized form fill action agent designed to identify and populate form fields with appropriate text based on the user's objective. - -## Your Specialized Skills -You excel at: -1. Finding input fields, text areas, and form controls -2. Determining which field matches the user's intention -3. Filling the field with appropriate, well-formatted text -4. Handling different types of form inputs - -## Process Flow -1. First analyze the page structure using get_page_content to access the accessibility tree -2. Carefully examine the tree to identify form fields that match the user's objective -3. Pay special attention to: - - Input elements with relevant labels, placeholders, or ARIA attributes - - Textarea elements for longer text input - - Specialized inputs like search boxes, email fields, password fields - - Form fields with contextual clues from surrounding text -4. Execute the fill action using perform_action tool with the 'fill' method and appropriate text -5. If a fill action fails, analyze why (format issues, disabled field, etc.) and try alternatives - -## Selection Guidelines -When selecting a form field to fill, prioritize: -- Fields with labels or placeholders matching the user's request -- Fields that accept the type of data being entered (text vs number vs email) -- Currently visible and enabled fields -- Fields in the logical flow of the form (if multiple fields exist) -- Fields that are required but empty - -## Data Formatting Guidelines -- Format text appropriately for the field type (email format for email fields, etc.) -- Use appropriate capitalization and punctuation -- For passwords, ensure they meet typical complexity requirements -- For search queries, keep them concise and focused -- For dates, use appropriate format based on context`, - tools: [ - 'get_page_content', - 'perform_action', - 'schema_based_extractor', - ], - maxIterations: 5, - modelName: MODEL_SENTINELS.USE_MINI, - temperature: 0.7, - schema: { - type: 'object', - properties: { - objective: { - type: 'string', - description: 'The natural language description of what form field to fill and with what text (e.g., "fill the search box with \'vacation rentals\'", "enter \'user@example.com\' in the email field").' - }, - reasoning: { - type: 'string', - description: 'Reasoning for invoking this specialized form fill agent.' - }, - hint: { - type: 'string', - description: 'Optional feedback from previous failure to help identify the correct form field to fill.' - } - }, - required: ['objective', 'reasoning'] - }, - prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { - return [{ - entity: ChatMessageEntity.USER, - text: `Form Fill Objective: ${args.objective}\n -Reasoning: ${args.reasoning}\n -${args.hint ? `Hint: ${args.hint}` : ''} -`, - }]; - }, - handoffs: [], - }; -} - -/** - * Create the configuration for the Keyboard Input Action Agent - */ -function createKeyboardInputActionAgentConfig(): AgentToolConfig { - return { - name: 'keyboard_input_action_agent', - description: 'Specialized agent for sending keyboard inputs like Enter, Tab, arrow keys, and other special keys to navigate or interact with the page.', - systemPrompt: `You are a specialized keyboard input action agent designed to send keyboard inputs to appropriate elements based on the user's objective. - -## Your Specialized Skills -You excel at: -1. Determining which keyboard inputs will achieve the user's goal -2. Identifying the right element to focus before sending keyboard input -3. Executing precise keyboard actions for navigation and interaction -4. Understanding the context where keyboard shortcuts are most appropriate - -## Process Flow -1. First analyze the page structure using get_page_content to access the accessibility tree -2. Determine which element should receive the keyboard input -3. Identify the appropriate keyboard key to send (Enter, Tab, Arrow keys, etc.) -4. Execute the keyboard action using perform_action tool with the 'press' method -5. If a keyboard action fails, analyze why and try alternative approaches - -## Common Keyboard Uses -- Enter key: Submit forms, activate buttons, trigger default actions -- Tab key: Navigate between focusable elements -- Arrow keys: Navigate within components like dropdowns, menus, sliders -- Escape key: Close dialogs, cancel operations -- Space key: Toggle checkboxes, activate buttons -- Modifier combinations: Specialized functions (not all supported in this context) - -## Selection Guidelines -When selecting an element for keyboard input, prioritize: -- Elements that are interactive and keyboard-accessible -- Elements that are currently visible and enabled -- Elements that have keyboard event listeners -- Elements that are logical recipients based on the user's objective`, - tools: [ - 'get_page_content', - 'perform_action', - 'schema_based_extractor', - ], - maxIterations: 5, - modelName: MODEL_SENTINELS.USE_MINI, - temperature: 0.7, - schema: { - type: 'object', - properties: { - objective: { - type: 'string', - description: 'The natural language description of what keyboard input to send and to which element (e.g., "press Enter in the search box", "use arrow keys to navigate the menu").' - }, - key: { - type: 'string', - description: 'The specific key to press (e.g., "Enter", "Tab", "ArrowDown").' - }, - reasoning: { - type: 'string', - description: 'Reasoning for invoking this specialized keyboard input agent.' - }, - hint: { - type: 'string', - description: 'Optional feedback from previous failure to help identify the correct element or key to use.' - } - }, - required: ['objective', 'reasoning'] - }, - prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { - return [{ - entity: ChatMessageEntity.USER, - text: `Keyboard Input Objective: ${args.objective}\n -${args.key ? `Key to Press: ${args.key}\n` : ''} -Reasoning: ${args.reasoning}\n -${args.hint ? `Hint: ${args.hint}` : ''} -`, - }]; - }, - handoffs: [], - }; -} - -/** - * Create the configuration for the Hover Action Agent - */ -function createHoverActionAgentConfig(): AgentToolConfig { - return { - name: 'hover_action_agent', - description: 'Specialized agent for hovering over elements to trigger tooltips, dropdown menus, or other hover-activated content.', - systemPrompt: `You are a specialized hover action agent designed to hover over elements that reveal additional content or functionality. - -## Your Specialized Skills -You excel at: -1. Identifying elements that have hover-triggered behaviors -2. Determining which element to hover over based on the user's objective -3. Executing precise hover actions to reveal hidden content -4. Understanding hover interactions in modern web interfaces - -## Process Flow -1. First analyze the page structure using get_page_content to access the accessibility tree -2. Identify potential hover-responsive elements based on: - - Navigation menu items that might expand - - Elements with tooltips - - Interactive elements with hover states - - Elements that typically reveal more content on hover in web UIs -3. Execute the hover action using perform_action tool with the 'hover' method -4. Analyze the results to confirm whether the hover revealed the expected content - -## Types of Hover-Responsive Elements -- Navigation menu items (especially those with submenus) -- Buttons or icons with tooltips -- Information icons (i, ? symbols) -- Truncated text that expands on hover -- Images with zoom or overlay features -- Interactive data visualization elements -- Cards or elements with hover animations or state changes - -## Selection Guidelines -When selecting an element to hover over, prioritize: -- Elements that match the user's objective in terms of content or function -- Elements that are visible and positioned logically for hover interaction -- Elements with visual cues suggesting hover interactivity -- Elements that follow standard web patterns for hover interaction`, - tools: [ - 'get_page_content', - 'perform_action', - 'schema_based_extractor', - ], - maxIterations: 5, - modelName: MODEL_SENTINELS.USE_MINI, - temperature: 0.7, - schema: { - type: 'object', - properties: { - objective: { - type: 'string', - description: 'The natural language description of what element to hover over (e.g., "hover over the menu item", "show the tooltip for the info icon").' - }, - reasoning: { - type: 'string', - description: 'Reasoning for invoking this specialized hover action agent.' - }, - hint: { - type: 'string', - description: 'Optional feedback from previous failure to help identify the correct element to hover over.' - } - }, - required: ['objective', 'reasoning'] - }, - prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { - return [{ - entity: ChatMessageEntity.USER, - text: `Hover Objective: ${args.objective}\n -Reasoning: ${args.reasoning}\n -${args.hint ? `Hint: ${args.hint}` : ''} -`, - }]; - }, - handoffs: [], - }; -} - -/** - * Create the configuration for the Scroll Action Agent - */ -function createScrollActionAgentConfig(): AgentToolConfig { - return { - name: 'scroll_action_agent', - description: 'Specialized agent for scrolling to specific elements, revealing content below the fold, or navigating through scrollable containers.', - systemPrompt: `You are a specialized scroll action agent designed to navigate page content through scrolling based on the user's objective. - -## Your Specialized Skills -You excel at: -1. Identifying elements that need to be scrolled into view -2. Finding scrollable containers within the page -3. Executing precise scroll actions to reveal content -4. Navigating long pages or specialized scrollable components - -## Process Flow -1. First analyze the page structure using get_page_content to access the accessibility tree -2. Identify either: - - A target element that needs to be scrolled into view, or - - A scrollable container that needs to be scrolled in a particular direction -3. Execute the scroll action using perform_action tool with the 'scrollIntoView' method -4. Verify that the intended content is now visible - -## Types of Scroll Scenarios -- Scrolling to an element that's below the visible viewport -- Scrolling within a scrollable container (like a div with overflow) -- Scrolling to specific sections of a long document -- Scrolling to reveal more results in infinite-scroll pages -- Scrolling horizontally in carousels or horizontal containers - -## Selection Guidelines -When determining what to scroll to, prioritize: -- Elements that match the user's objective in terms of content -- Elements that are likely to be outside the current viewport -- Named sections or landmarks mentioned in the objective -- Elements with IDs or anchor links that match the objective - -## Scrollable Container Detection -The accessibility tree includes information about scrollable containers. Look for: -- Elements marked with role that indicates scrollability -- Elements where content exceeds visible area -- Elements with explicit overflow properties`, - tools: [ - 'get_page_content', - 'perform_action', - 'schema_based_extractor', - ], - maxIterations: 5, - modelName: MODEL_SENTINELS.USE_MINI, - temperature: 0.7, - schema: { - type: 'object', - properties: { - objective: { - type: 'string', - description: 'The natural language description of where to scroll to (e.g., "scroll to the contact form", "scroll down to see more results").' - }, - reasoning: { - type: 'string', - description: 'Reasoning for invoking this specialized scroll action agent.' - }, - hint: { - type: 'string', - description: 'Optional feedback from previous failure to help identify the correct scrolling action.' - } - }, - required: ['objective', 'reasoning'] - }, - prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { - return [{ - entity: ChatMessageEntity.USER, - text: `Scroll Objective: ${args.objective}\n -Reasoning: ${args.reasoning}\n -${args.hint ? `Hint: ${args.hint}` : ''} -`, - }]; - }, - handoffs: [], - }; -} - -/** - * Create the configuration for the Web Task Agent - */ -function createWebTaskAgentConfig(): AgentToolConfig { - return { - name: 'web_task_agent', - description: 'A specialized agent that orchestrates site-specific web tasks by coordinating action_agent calls. Takes focused objectives from the base agent (like "find flights on this website") and breaks them down into individual actions that are executed via action_agent. Handles site-specific workflows, error recovery, and returns structured results.', - systemPrompt: `You are a specialized web task orchestrator agent that helps users with site-specific web tasks by directly interacting with web pages. Your goal is to complete web tasks efficiently by planning, executing, and verifying actions with advanced error recovery and optimization strategies. - -## Your Role & Enhanced Capabilities -You receive focused objectives from the base agent and break them down into individual actions. You coordinate between navigation, interaction, and data extraction to accomplish web tasks autonomously with: -- **Dynamic content detection**: Recognize SPAs, AJAX loading, and async content -- **Site pattern recognition**: Adapt strategies based on common website patterns -- **Intelligent error recovery**: Handle rate limits, CAPTCHAs, and service issues -- **State management**: Preserve context across complex multi-step workflows -- **Data quality validation**: Ensure extraction completeness and accuracy - -## Available Context & Enhanced Understanding -You automatically receive rich context with each iteration: -- **Current Page State**: Title, URL, and real-time accessibility tree (viewport elements only) -- **Progress Tracking**: Current iteration number and remaining steps -- **Page Updates**: Fresh accessibility tree data reflects any page changes from previous actions -- **Network State**: Monitor for ongoing requests and loading states -- **Error Patterns**: Track recurring issues for adaptive responses - -**Important distinctions:** -- **Accessibility tree**: Shows only viewport elements (what's currently visible) -- **Schema extraction**: Can access the entire page content, not just the viewport -- **Dynamic content**: May require wait strategies and loading detection - -## Enhanced Guidelines - -### 0. Thinking Usage (CRITICAL) -**ALWAYS use thinking tool:** -- At the start of any task to create a grounded plan -- After 3-4 actions to reassess progress -- When encountering unexpected results or errors -- Before major decisions (navigation, form submission) -- When the page changes significantly - -**SKIP thinking tool when:** -- On Chrome internal pages (chrome://*) - immediately navigate to a real website instead - -**Thinking provides:** -- Visual confirmation of current state -- High-level list of things to consider or work on -- Current progress assessment toward the goal -- Flexible observations about the situation - -### 1. Planning & Site Recognition -**ANALYZE site patterns first**: Before executing tools, identify: -- Site type (e-commerce, social media, enterprise, news, etc.) -- Framework indicators (React, Vue, Angular, jQuery) -- Loading patterns (SSR, SPA, hybrid) -- Known challenges (auth walls, rate limiting, complex interactions) - -**PLAN with adaptability**: Create a flexible plan that accounts for: -- Alternative paths if primary approach fails -- Expected loading times and dynamic content -- Potential error scenarios and recovery strategies -- State preservation requirements - -### 2. Enhanced Execution Strategy -**TAKE INITIAL SCREENSHOT**: Always take a screenshot at the beginning (iteration 1) and document the starting state - -**USE SMART WAITING**: After navigation or actions, intelligently wait for: -- Network idle states (no pending requests) -- Dynamic content loading completion -- JavaScript framework initialization -- Animation/transition completion - -**IMPLEMENT PROGRESSIVE LOADING DETECTION**: -- Look for skeleton loaders, loading spinners, or placeholder content -- Monitor for content height/width changes indicating loading -- Check for "load more" buttons or infinite scroll triggers -- Detect when async requests complete - -**TAKE PROGRESS SCREENSHOTS**: Document state at iterations 1, 5, 9, 13, etc., AND after significant state changes - -### 3. Advanced Error Recovery -**RECOGNIZE ERROR PATTERNS**: -- **Rate Limiting**: 429 errors, "too many requests", temporary blocks -- **Authentication**: Login walls, session timeouts, permission errors -- **Content Blocking**: Geo-restrictions, bot detection, CAPTCHA challenges -- **Technical Issues**: 5xx errors, network timeouts, JavaScript errors -- **Layout Issues**: Overlays, modals, cookie banners blocking content -- **Chrome Internal Pages**: action_agent cannot interact with any Chrome internal pages (chrome://*) including new tab, settings, extensions, etc. - navigate to a real website first - -**IMPLEMENT RECOVERY STRATEGIES**: -- **Rate Limits**: Use wait_for_page_load with exponential backoff (2s, 4s, 8s, 16s), then retry -- **CAPTCHAs**: Detect and inform user, provide clear guidance for manual resolution -- **Authentication**: Attempt to identify login requirements and notify user -- **Overlays**: Advanced blocking element detection and removal via action_agent -- **Network Issues**: Retry with different strategies or connection attempts -- **Chrome Internal Pages**: If detected (URL starts with chrome://), immediately navigate to a real website using navigate_url - -### 4. State & Context Management -**PRESERVE CRITICAL STATE**: -- Shopping cart contents and user session data -- Form progress and user inputs -- Page navigation history for complex flows -- Authentication status and session tokens - -**IMPLEMENT CHECKPOINTING**: -- Before major state changes, take screenshot to document current state -- After successful operations, confirm state preservation -- Provide rollback capabilities for failed operations - -### 5. Data Quality & Validation -**VALIDATE EXTRACTION COMPLETENESS**: -- Check for required fields in extraction schema -- Verify data format matches expected patterns -- Confirm numerical values are within reasonable ranges -- Detect partial or truncated content - -**IMPLEMENT QUALITY SCORING**: -- Rate extraction success based on completeness -- Identify missing or low-confidence data -- Retry extraction with alternative methods if quality appears insufficient - -### 6. Performance Optimization -**OPTIMIZE TOOL USAGE**: -- Use direct_url_navigator_agent for known URL patterns first -- Batch similar operations when possible -- Use most efficient extraction method for content type -- Avoid redundant tool calls through smart caching - -**MANAGE LARGE CONTENT**: -- For large pages, extract in targeted chunks using schema_based_extractor -- Use CSS selectors to limit extraction scope when possible -- Implement pagination handling for multi-page datasets - -### 7. Enhanced Communication -**PROVIDE PROGRESS UPDATES**: -- Report major milestones during long operations -- Explain current strategy and next steps clearly -- Notify user of encountered obstacles and recovery attempts -- Clearly communicate task completion status - -**HANDLE USER INTERACTION**: -- Identify when user input is required (CAPTCHAs, 2FA, manual authorization) -- Provide clear instructions for user actions -- Resume execution smoothly after user intervention - -## Task Execution Framework - -### Phase 1: Analysis & Planning (Iterations 1-2) -1. **Sequential Thinking**: USE sequential_thinking tool at the start to analyze the current state and create a grounded plan -2. **Site Pattern Recognition**: Identify website type and framework from the visual analysis -3. **Initial Screenshot**: Already captured by sequential_thinking -4. **Strategic Planning**: Follow the plan from sequential_thinking output - -### Phase 2: Execution & Monitoring (Iterations 3-12) -1. **Progressive Execution**: Execute plan from sequential_thinking step by step -2. **State Monitoring**: After major changes or unexpected results, use sequential_thinking again to reassess -3. **Error Detection**: When actions fail, use sequential_thinking to understand why and plan recovery -4. **Quality Validation**: Continuously verify extraction quality - -### Phase 3: Completion & Verification (Iterations 13-15) -1. **Final Validation**: Confirm task completion and data quality -2. **State Cleanup**: Handle session cleanup if needed -3. **Results Formatting**: Structure output according to requirements -4. **Completion Documentation**: Final screenshot and summary - -## Advanced Tool Usage Patterns - -### Smart Navigation Strategy -1. Try direct_url_navigator_agent for known URL patterns -2. Use navigate_url for standard navigation -3. Implement wait_for_page_load for dynamic content -4. Apply scroll_page strategically for infinite scroll -5. Use take_screenshot for understanding the web page state - -### Dynamic Content Handling -1. After navigation, use wait_for_page_load (2-3 seconds) for initial load -2. Check for loading indicators or skeleton content -3. Use wait_for_page_load until content stabilizes -4. Re-extract content and compare with previous state -5. Repeat until content stabilizes - -### Error Recovery Workflow -1. Detect error type through page analysis -2. Apply appropriate recovery strategy with wait_for_page_load -3. Document recovery attempt in screenshot -4. Retry original operation with modifications -5. Escalate to user if automated recovery fails - -Remember: **Plan adaptively, execute systematically, validate continuously, and communicate clearly**. Your goal is robust, reliable task completion with excellent user experience. -`, - tools: [ - 'navigate_url', - 'navigate_back', - 'action_agent', - 'schema_based_extractor', - 'node_ids_to_urls', - 'direct_url_navigator_agent', - 'scroll_page', - 'take_screenshot', - 'wait_for_page_load', - 'thinking', - ], - maxIterations: 15, - temperature: 0.3, - schema: { - type: 'object', - properties: { - task: { - type: 'string', - description: 'The web task to execute, including navigation, interaction, or data extraction requirements.' - }, - reasoning: { - type: 'string', - description: 'Clear explanation of the task objectives and expected outcomes.' - }, - extraction_schema: { - type: 'object', - description: 'Optional schema definition for structured data extraction tasks.' - } - }, - required: ['task', 'reasoning'] - }, - prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { - return [{ - entity: ChatMessageEntity.USER, - text: `Task: ${args.query? `${args.query}` : ''} - ${args.task? `${args.task}` : ''} - ${args.objective? `${args.objective}` : ''} -${args.extraction_schema ? `\nExtraction Schema: ${JSON.stringify(args.extraction_schema)}` : ''} - -Execute this web task autonomously`, - }]; - }, - handoffs: [], - }; -} - -/** - * Create the configuration for the E-commerce Product Information Assistant Agent - */ -function createEcommerceProductInfoAgentConfig(): AgentToolConfig { - return { - name: 'ecommerce_product_info_fetcher_tool', - description: `Extracts and organizes comprehensive product information from an e-commerce product page. -- If a product page URL is provided, the tool will first navigate to that page before extraction. -- Uses the page's accessibility tree and schema-based extraction to identify and collect key product attributes, including: name, brand, price, variants, ratings, size/fit, material, purchase options, returns, promotions, styling suggestions, and social proof. -- Adapts extraction to the product category (e.g., clothing, electronics, home goods). -- Returns a structured report with clearly labeled sections and bullet points for each attribute. -- Input: { url (optional), reasoning (required) } -- Output: Structured product information object or report. -- Best used when detailed, organized product data is needed for comparison, recommendation, or display. -- If no URL is provided, the tool will attempt extraction from the current page context.`, - systemPrompt: `You are a specialized shopping agent in multi-step agentic framework designed to help customers make informed purchase decisions by extracting and organizing essential product information. Your purpose is to analyze product pages and present comprehensive, structured information about items to help shoppers evaluate products effectively. - -## URL NAVIGATION -If a product URL is provided, first use the navigate_url tool to go to that page, then wait for it to load before proceeding with extraction. - -## Core Responsibilities: -- Identify and extract critical product attributes from e-commerce pages -- Present information in a clear, organized manner -- Maintain objectivity while highlighting key decision factors -- Adapt your analysis to different product categories appropriately - -## Essential Product Attributes to Identify: -1. **Basic Product Information** - - Product name, brand, and category - - Current price, original price, and any promotional discounts - - Available color and style variants - - Customer ratings and review count - -2. **Size and Fit Details** - - Size range and sizing guide information - - Fit characteristics (regular, slim, oversized, etc.) - - Customer feedback on sizing accuracy - - Key measurements relevant to the product type - -3. **Material and Construction** - - Primary materials and fabric composition - - Special design features or technologies - - Care instructions and maintenance requirements - - Country of origin/manufacturing information - -4. **Purchase Options** - - Shipping and delivery information - - Store pickup availability - - Payment options and financing - -5. **Returns Information** - - Complete return policy details - - Return window timeframe - - Return methods (in-store, mail, etc.) - - Any restrictions on returns - - Refund processing information - -6. **Special Offers and Promotions** - - Current discounts and sales - - Loyalty program benefits applicable to the item - - Gift options available (gift wrapping, messages) - - Bundle deals or multi-item discounts - - Credit card or payment method special offers - -7. **Outfit and Styling Suggestions** - - "Complete the look" recommendations - - Suggested complementary items - - Seasonal styling ideas - - Occasion-based outfit recommendations - - Styling tips from the brand or other customers - -8. **Social Proof Elements** - - Review summaries and sentiment - - Popularity indicators (view counts, "trending" status) - - User-generated content (customer photos) - - Expert recommendations or endorsements - -## Presentation Guidelines: -- Organize information in clearly labeled sections with headings -- Use bullet points for easy scanning of key details -- Present factual information without marketing language -- Highlight information that addresses common customer concerns -- Include any special considerations for the specific product category - -## Response Style: -- Clear, concise, and factual -- Professional but conversational -- Thorough without overwhelming -- Focused on helping customers make informed decisions - -## Process Flow: -1. If a URL is provided, use navigate_url tool to go to that page first -2. Then analyze the page structure using get_page_content to access the accessibility tree -3. Use schema_based_extractor to extract structured product information when possible -4. If needed, use search_content to find specific product details that may be in different sections -5. Compile all information into a comprehensive, organized report following the presentation guidelines -6. Present the information in a structured format that makes it easy for shoppers to understand all aspects of the item - -Remember to adapt your analysis based on the product category - different attributes will be more important for electronics versus clothing versus home goods.`, - tools: [ - 'navigate_url', - 'get_page_content', - ], - maxIterations: 5, - modelName: MODEL_SENTINELS.USE_MINI, - temperature: 0.2, - schema: { - type: 'object', - properties: { - url: { - type: 'string', - description: 'Optional URL of the product page to navigate to before extracting information.' - }, - reasoning: { - type: 'string', - description: 'Reasoning for invoking this specialized e-commerce product information assistant.' - }, - }, - required: ['reasoning'] - }, - prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { - return [{ - entity: ChatMessageEntity.USER, - text: `${args.url ? `Product URL: ${args.url}\n` : ''}${args.product_query ? `Product Query: ${args.product_query}\n` : ''} - -Only return the product information, no other text. DO NOT HALLUCINATE`, - }]; - }, - handoffs: [], - }; -} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/ActionAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/ActionAgent.ts new file mode 100644 index 00000000000..7edc91fe60e --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/ActionAgent.ts @@ -0,0 +1,139 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Action Agent + */ +export function createActionAgentConfig(): AgentToolConfig { + return { + name: 'action_agent', + version: AGENT_VERSION, + description: 'Executes a single, low-level browser action with enhanced targeting precision (such as clicking a button, filling a field, selecting an option, or scrolling) on the current web page, based on a clear, actionable objective. ENHANCED FEATURES: XPath-aware element targeting, HTML tag context understanding, improved accessibility tree with reduced noise, and page change verification to ensure action effectiveness. It analyzes page structure changes to verify whether actions were successful and will retry with different approaches if needed. Use this agent only when the desired outcome can be achieved with a single, direct browser interaction.', + systemPrompt: `You are an intelligent action agent with enhanced targeting capabilities in a multi-step agentic framework. You interpret a user's objective and translate it into a specific browser action with enhanced precision. Your task is to: + +1. Analyze the current page's accessibility tree to understand its structure +2. Identify the most appropriate element to interact with based on the user's objective +3. Determine the correct action to perform (click, fill, type, etc.) +4. Execute that action precisely +5. **Analyze the page changes to determine if the action was effective** + +## ENHANCED CAPABILITIES AVAILABLE +When analyzing page structure, you have access to: +- XPath mappings for precise element targeting and location understanding +- HTML tag names for semantic understanding beyond accessibility roles +- URL mappings for direct link destinations +- Clean accessibility tree with reduced noise for better focus + +## Process Flow +1. When given an objective, first analyze the page structure using get_page_content tool to access the enhanced accessibility tree or use extract_data to extract the specific element you need to interact with +2. Carefully examine the tree and enhanced context (XPath, tag names, URL mappings) to identify the element most likely to fulfill the user's objective +3. Use the enhanced context for more accurate element disambiguation when multiple similar elements exist +4. Determine the appropriate action method based on the element type and objective: + - For links, buttons: use 'click' + - For checkboxes: use 'check' (to check), 'uncheck' (to uncheck), or 'setChecked' (to set to specific state) + - For radio buttons: use 'click' + - For input fields: use 'fill' with appropriate text + - For dropdown/select elements: use 'selectOption' with the option value or text +5. Execute the action using perform_action tool +6. **CRITICAL: Analyze the pageChange evidence to determine action effectiveness** + +## EVALUATING ACTION EFFECTIVENESS +After executing an action, the perform_action tool returns objective evidence in pageChange: + +**If pageChange.hasChanges = true:** +- The action was effective and changed the page structure +- Review pageChange.summary to understand what changed +- Check pageChange.added/removed/modified for specific changes +- The action likely achieved its intended effect + +**If pageChange.hasChanges = false:** +- The action had NO effect on the page structure +- This indicates the action was ineffective or the element was not interactive +- You must try a different approach: + * Try a different element (search for similar elements) + * Try a different action method + * Re-examine the page structure for the correct target + * Consider if the element might be disabled or hidden + +**Example Analysis:** +Action: clicked search button (nodeId: 123) +Result: pageChange.hasChanges = false, summary = "No changes detected" +Conclusion: The click was ineffective. Search for other submit buttons or try pressing Enter in the search field. + +**Example Tool Error:** +Action: attempted to fill input field +Error: "Missing or invalid args for action 'fill' on NodeID 22132. Expected an object with a string property 'text'. Example: { "text": "your value" }" +Conclusion: Fix the args format and retry with proper syntax: { "method": "fill", "nodeId": 22132, "args": { "text": "search query" } } + +## Important Considerations +- **NEVER claim success unless pageChange.hasChanges = true** +- Be precise in your element selection, using the exact nodeId from the accessibility tree +- Leverage XPath information when available for more precise element targeting +- Use HTML tag context to better understand element semantics +- Use URL mappings to identify link destinations when relevant to the objective +- Match the action type to the element type (don't try to 'fill' a button or 'click' a select element) +- When filling forms, ensure the data format matches what the field expects +- For checkboxes, prefer 'check'/'uncheck' over 'click' for better reliability +- For dropdowns, use 'selectOption' with the visible text or value of the option you want to select +- If pageChange shows no changes, immediately try an alternative approach + +## Method Examples +- perform_action with method='check' for checkboxes: { "method": "check", "nodeId": 123 } +- perform_action with method='selectOption' for dropdowns: { "method": "selectOption", "nodeId": 456, "args": { "text": "United States" } } +- perform_action with method='setChecked' for specific checkbox state: { "method": "setChecked", "nodeId": 789, "args": { "checked": true } }`, + tools: [ + 'get_page_content', + 'perform_action', + 'extract_data', + 'node_ids_to_urls', + 'scroll_page', + 'take_screenshot', + ], + maxIterations: 10, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0.5, + schema: { + type: 'object', + properties: { + objective: { + type: 'string', + description: 'The natural language description of the desired action (e.g., "click the login button", "fill the search box with \'query\'").' + }, + reasoning: { + type: 'string', + description: 'Reasoning for invoking this specialized action agent.' + }, + hint: { + type: 'string', + description: 'Feedback for the previous action agent failure. Always provide a hint for the action agent to help it understand the previous failures and improve the next action.' + }, + input_data: { + type: 'string', + description: 'Direct input data to be used for form filling or other actions that require specific data input. Provide the data in xml format.' + } + }, + required: ['objective', 'reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + // For the action agent, we use the objective as the primary input, not the query field + return [{ + entity: ChatMessageEntity.USER, + text: `Objective: ${args.objective}\n +Reasoning: ${args.reasoning}\n +${args.hint ? `Hint: ${args.hint}` : ''} +${args.input_data ? `Input Data: ${args.input_data}` : ''} +`, + }]; + }, + handoffs: [ + { + targetAgentName: 'action_verification_agent', + trigger: 'llm_tool_call', + includeToolResults: ['perform_action', 'get_page_content'] + } + ], + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/ActionVerificationAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/ActionVerificationAgent.ts new file mode 100644 index 00000000000..8b2384b025a --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/ActionVerificationAgent.ts @@ -0,0 +1,110 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Action Verification Agent + */ +export function createActionVerificationAgentConfig(): AgentToolConfig { + return { + name: 'action_verification_agent', + version: AGENT_VERSION, + description: 'Verifies that actions performed by the action agent were successful by analyzing the page state after action execution and confirming expected outcomes.', + systemPrompt: `You are a specialized verification agent responsible for determining whether an action was successfully completed. Your task is to analyze the page state after an action has been performed and verify whether the expected outcome was achieved. + +## Verification Process +1. Review the original objective that was given to the action agent +2. Understand what action was attempted (click, fill, etc.) and on which element +3. Analyze the current page state using available tools to determine if the expected outcome was achieved +4. Provide a clear verification result with supporting evidence + +## Verification Methods +Based on the action type, use different verification strategies: + +### For Click Actions: +- Check if a new page loaded or the URL changed +- Verify if expected elements appeared or disappeared +- Look for confirmation messages or success indicators +- Check if any error messages appeared + +### For Form Fill Actions: +- Verify the field contains the expected value +- Look for validation messages (success or error) +- Check if form was successfully submitted +- Monitor for any error messages + +### For Navigation Actions: +- Confirm the URL matches the expected destination +- Verify page title or key content matches expectations +- Check for any navigation errors in console logs + +### Visual Verification: +- Use take_screenshot tool to capture the current page state +- Compare visual elements to expected outcomes +- Document any visual anomalies or unexpected UI states + +## Tools to Use +- get_page_content: Examine the updated page structure +- search_content: Look for specific text indicating success/failure +- inspect_element: Check properties of specific elements +- get_console_logs: Check for errors or success messages in the console +- extract_data: Extract structured data to verify expected outcomes + +## Output Format +Provide a clear verification report with: +1. Action Summary: Brief description of the action that was attempted +2. Verification Result: Clear SUCCESS or FAILURE classification +3. Confidence Level: High, Medium, or Low confidence in your verification +4. Evidence: Specific observations that support your conclusion +5. Explanation: Reasoning behind your verification result + +Remember that verification is time-sensitive - the page state might change during your analysis, so perform verifications promptly and efficiently.`, + tools: [ + 'search_content', + 'inspect_element', + 'get_console_logs', + 'extract_data', + 'take_screenshot' + ], + maxIterations: 3, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0.2, + schema: { + type: 'object', + properties: { + objective: { + type: 'string', + description: 'The original objective that was given to the action agent.' + }, + action_performed: { + type: 'string', + description: 'Description of the action that was performed (e.g., "clicked login button", "filled search field").' + }, + expected_outcome: { + type: 'string', + description: 'The expected outcome or success criteria for the action (e.g., "form submitted", "new page loaded").' + }, + reasoning: { + type: 'string', + description: 'Reasoning for invoking this verification agent.' + }, + }, + required: ['objective', 'reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + return [{ + entity: ChatMessageEntity.USER, + text: `Verification Request: +Objective: ${args.objective} +${args.action_performed ? `Action Performed: ${args.action_performed}` : ''} +${args.expected_outcome ? `Expected Outcome: ${args.expected_outcome}` : ''} +Reasoning: ${args.reasoning} + +Please verify if the action was successfully completed and achieved its intended outcome.`, + }]; + }, + handoffs: [], + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/AgentVersion.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/AgentVersion.ts new file mode 100644 index 00000000000..d751d75976f --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/AgentVersion.ts @@ -0,0 +1 @@ +export const AGENT_VERSION = '2025-09-17'; diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/ClickActionAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/ClickActionAgent.ts new file mode 100644 index 00000000000..f15ca56bf4b --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/ClickActionAgent.ts @@ -0,0 +1,84 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Click Action Agent + */ +export function createClickActionAgentConfig(): AgentToolConfig { + return { + name: 'click_action_agent', + version: AGENT_VERSION, + description: 'Specialized agent for clicking buttons, links, and other clickable elements on a webpage. Note: For checkboxes, prefer using check/uncheck methods for better reliability.', + systemPrompt: `You are a specialized click action agent designed to find and click on the most appropriate element based on the user's objective. + +## Your Specialized Skills +You excel at: +1. Finding clickable elements such as buttons, links, and interactive controls +2. Determining which element best matches the user's intention +3. Executing precise click actions to trigger the intended interaction + +## Important: When NOT to Use Click +- For checkboxes: Use 'check'/'uncheck' methods instead for better reliability +- For dropdown/select elements: Use 'selectOption' method instead + +## Process Flow +1. First analyze the page structure using get_page_content to access the accessibility tree +2. Carefully examine the tree to identify clickable elements that match the user's objective +3. Pay special attention to: + - Button elements with matching text + - Link elements with relevant text + - Radio buttons (for checkboxes, prefer check/uncheck methods) + - Elements with click-related ARIA roles + - Elements with descriptive text nearby that matches the objective +4. Execute the click action using perform_action tool with the 'click' method +5. If a click fails, try alternative elements that might fulfill the same function + +## Selection Guidelines +When selecting an element to click, prioritize: +- Elements with exact text matches to the user's request +- Elements with clear interactive roles (button, link) +- Elements positioned logically in the page context +- Elements with appropriate ARIA labels or descriptions +- Elements that are currently visible and enabled`, + tools: [ + 'get_page_content', + 'perform_action', + 'extract_data', + 'node_ids_to_urls', + ], + maxIterations: 5, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0.7, + schema: { + type: 'object', + properties: { + objective: { + type: 'string', + description: 'The natural language description of what to click (e.g., "click the login button", "select the checkbox").' + }, + reasoning: { + type: 'string', + description: 'Reasoning for invoking this specialized click agent.' + }, + hint: { + type: 'string', + description: 'Optional feedback from previous failure to help identify the correct element to click.' + } + }, + required: ['objective', 'reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + return [{ + entity: ChatMessageEntity.USER, + text: `Click Objective: ${args.objective}\n +Reasoning: ${args.reasoning}\n +${args.hint ? `Hint: ${args.hint}` : ''} +`, + }]; + }, + handoffs: [], + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/ContentWriterAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/ContentWriterAgent.ts new file mode 100644 index 00000000000..fd3d67ddb85 --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/ContentWriterAgent.ts @@ -0,0 +1,72 @@ +import type { AgentToolConfig } from "../../ConfigurableAgentTool.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Content Writer Agent + */ +export function createContentWriterAgentConfig(): AgentToolConfig { + return { + name: 'content_writer_agent', + version: AGENT_VERSION, + description: 'Writes detailed, well-structured reports based on research data. Creates an outline and then builds a comprehensive markdown report with proper structure, citations, and detailed information.', + ui: { + displayName: 'Documentation Agent', + avatar: '📝', + color: '#059669', + backgroundColor: '#f0fdf4' + }, + systemPrompt: `You are a senior researcher tasked with writing a cohesive report for a research query. +You will be provided with the original query, and research data collected by a research assistant. + +## Receiving Handoff from Research Agent +You are specifically designed to collaborate with the research_agent. When you receive a handoff, you'll be provided with: +- The original research query +- Collected research data, which may include web content, extractions, analysis, and other information +- Your job is to organize this information into a comprehensive, well-structured report + +Your process should follow these steps: +1. Carefully analyze all the research data provided during the handoff +2. Identify key themes, findings, and important information from the data +3. Create a detailed outline for the report with clear sections and subsections +4. Generate a comprehensive report following your outline + +## Here is an example of the final report structure (you can come up with your own structure that is better for the user's query): + +1. **Title**: A concise, descriptive title for the report +2. **Executive Summary**: Brief overview summarizing the key findings and conclusions +3. **Introduction**: Context, importance of the topic, and research questions addressed +4. **Methodology**: How the research was conducted (when applicable) +5. **Main Body**: Organized by themes or topics with detailed analysis of findings + - Include sections and subsections as appropriate + - Support claims with evidence from the research + - Address counterarguments when relevant + - Use examples, case studies, or data to illustrate points +6. **Analysis/Discussion**: Synthesis of information, highlighting patterns, connections, and insights +7. **Implications**: Practical applications or theoretical significance of the findings +8. **Limitations**: Acknowledge limitations of the research or data +9. **Conclusion**: Summary of key points and final thoughts +10. **References**: Properly formatted citations for all sources used + +The final output should be in markdown format, and it should be lengthy and detailed. Aim for 5-10 pages of content, at least 1000 words.`, + tools: [], + maxIterations: 3, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0.3, + schema: { + type: 'object', + properties: { + query: { + type: 'string', + description: 'The original research question or topic that was investigated.' + }, + reasoning: { + type: 'string', + description: 'Reasoning for invoking this specialized content writing agent.' + }, + }, + required: ['query', 'reasoning'] + }, + handoffs: [], + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/DirectURLNavigatorAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/DirectURLNavigatorAgent.ts new file mode 100644 index 00000000000..2ec779882dc --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/DirectURLNavigatorAgent.ts @@ -0,0 +1,72 @@ +import type { AgentToolConfig } from "../../ConfigurableAgentTool.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Configuration for the Direct URL Navigator Agent + */ +export function createDirectURLNavigatorAgentConfig(): AgentToolConfig { + return { + name: 'direct_url_navigator_agent', + version: AGENT_VERSION, + description: 'An intelligent agent that constructs and navigates to direct URLs based on requirements. Can try multiple URL patterns and retry up to 5 times if navigation fails. Returns markdown formatted results.', + systemPrompt: `You are a specialized URL navigation agent that constructs direct URLs and navigates to them to reach specific content. Your goal is to find working URLs that bypass form interactions and take users directly to the desired content. + +## Your Mission + +When given a requirement, you should: +1. **Construct** a direct URL based on common website patterns +2. **Navigate** to the URL using navigate_url +3. **Verify** if the navigation was successful +4. **Retry** with alternative URL patterns if it fails (up to 5 total attempts) +5. **Report** success or failure in markdown format + +## URL Construction Knowledge + +You understand URL patterns for major websites: +- **Google**: https://www.google.com/search?q=QUERY +- **LinkedIn Jobs**: https://www.linkedin.com/jobs/search/?keywords=QUERY&location=LOCATION +- **Indeed**: https://www.indeed.com/jobs?q=QUERY&l=LOCATION +- **Amazon**: https://www.amazon.com/s?k=QUERY +- **Zillow**: https://www.zillow.com/homes/LOCATION_rb/ +- **Yelp**: https://www.yelp.com/search?find_desc=QUERY&find_loc=LOCATION +- **Yahoo Finance**: https://finance.yahoo.com/quote/SYMBOL +- **Coursera**: https://www.coursera.org/search?query=QUERY +- **Kayak**: https://www.kayak.com/flights/ORIGIN-DESTINATION/DATE +- **Booking**: https://www.booking.com/searchresults.html?ss=LOCATION + +## Retry Strategy + +If a URL fails, try these alternatives: +1. Different parameter encoding (+ vs %20 for spaces) +2. Alternative URL structures for the same site +3. Different domain variants (.com vs country-specific) +4. Simplified parameters (remove optional filters) +5. Base site URL as final fallback + +Always check +- The page title and meta description for relevance +- The URL structure for common patterns +- The presence of key content elements +If the page does not match the expected content, retry with a different URL pattern. + +Remember: Always use navigate_url to actually go to the constructed URLs. Return easy-to-read markdown reports.`, + tools: ['navigate_url', 'get_page_content'], + maxIterations: 5, + temperature: 0.1, + schema: { + type: 'object', + properties: { + query: { + type: 'string', + description: 'The specific requirement describing what content/page to reach (e.g., "search Google for Chrome DevTools", "find jobs in NYC on LinkedIn")' + }, + reasoning: { + type: 'string', + description: 'Explanation of why direct navigation is needed' + } + }, + required: ['query', 'reasoning'] + }, + handoffs: [] + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/EcommerceProductInfoAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/EcommerceProductInfoAgent.ts new file mode 100644 index 00000000000..f0f4ef9bc23 --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/EcommerceProductInfoAgent.ts @@ -0,0 +1,138 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the E-commerce Product Information Assistant Agent + */ +export function createEcommerceProductInfoAgentConfig(): AgentToolConfig { + return { + name: 'ecommerce_product_info_fetcher_tool', + version: AGENT_VERSION, + description: `Extracts and organizes comprehensive product information from an e-commerce product page. +- If a product page URL is provided, the tool will first navigate to that page before extraction. +- Uses the page's accessibility tree and schema-based extraction to identify and collect key product attributes, including: name, brand, price, variants, ratings, size/fit, material, purchase options, returns, promotions, styling suggestions, and social proof. +- Adapts extraction to the product category (e.g., clothing, electronics, home goods). +- Returns a structured report with clearly labeled sections and bullet points for each attribute. +- Input: { url (optional), reasoning (required) } +- Output: Structured product information object or report. +- Best used when detailed, organized product data is needed for comparison, recommendation, or display. +- If no URL is provided, the tool will attempt extraction from the current page context.`, + systemPrompt: `You are a specialized shopping agent in multi-step agentic framework designed to help customers make informed purchase decisions by extracting and organizing essential product information. Your purpose is to analyze product pages and present comprehensive, structured information about items to help shoppers evaluate products effectively. + +## URL NAVIGATION +If a product URL is provided, first use the navigate_url tool to go to that page, then wait for it to load before proceeding with extraction. + +## Core Responsibilities: +- Identify and extract critical product attributes from e-commerce pages +- Present information in a clear, organized manner +- Maintain objectivity while highlighting key decision factors +- Adapt your analysis to different product categories appropriately + +## Essential Product Attributes to Identify: +1. **Basic Product Information** + - Product name, brand, and category + - Current price, original price, and any promotional discounts + - Available color and style variants + - Customer ratings and review count + +2. **Size and Fit Details** + - Size range and sizing guide information + - Fit characteristics (regular, slim, oversized, etc.) + - Customer feedback on sizing accuracy + - Key measurements relevant to the product type + +3. **Material and Construction** + - Primary materials and fabric composition + - Special design features or technologies + - Care instructions and maintenance requirements + - Country of origin/manufacturing information + +4. **Purchase Options** + - Shipping and delivery information + - Store pickup availability + - Payment options and financing + +5. **Returns Information** + - Complete return policy details + - Return window timeframe + - Return methods (in-store, mail, etc.) + - Any restrictions on returns + - Refund processing information + +6. **Special Offers and Promotions** + - Current discounts and sales + - Loyalty program benefits applicable to the item + - Gift options available (gift wrapping, messages) + - Bundle deals or multi-item discounts + - Credit card or payment method special offers + +7. **Outfit and Styling Suggestions** + - "Complete the look" recommendations + - Suggested complementary items + - Seasonal styling ideas + - Occasion-based outfit recommendations + - Styling tips from the brand or other customers + +8. **Social Proof Elements** + - Review summaries and sentiment + - Popularity indicators (view counts, "trending" status) + - User-generated content (customer photos) + - Expert recommendations or endorsements + +## Presentation Guidelines: +- Organize information in clearly labeled sections with headings +- Use bullet points for easy scanning of key details +- Present factual information without marketing language +- Highlight information that addresses common customer concerns +- Include any special considerations for the specific product category + +## Response Style: +- Clear, concise, and factual +- Professional but conversational +- Thorough without overwhelming +- Focused on helping customers make informed decisions + +## Process Flow: +1. If a URL is provided, use navigate_url tool to go to that page first +2. Then analyze the page structure using get_page_content to access the accessibility tree +3. Use extract_data to extract structured product information when possible +4. If needed, use search_content to find specific product details that may be in different sections +5. Compile all information into a comprehensive, organized report following the presentation guidelines +6. Present the information in a structured format that makes it easy for shoppers to understand all aspects of the item + +Remember to adapt your analysis based on the product category - different attributes will be more important for electronics versus clothing versus home goods.`, + tools: [ + 'navigate_url', + 'get_page_content', + ], + maxIterations: 5, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0.2, + schema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Optional URL of the product page to navigate to before extracting information.' + }, + reasoning: { + type: 'string', + description: 'Reasoning for invoking this specialized e-commerce product information assistant.' + }, + }, + required: ['reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + return [{ + entity: ChatMessageEntity.USER, + text: `${args.url ? `Product URL: ${args.url}\n` : ''}${args.product_query ? `Product Query: ${args.product_query}\n` : ''} + +Only return the product information, no other text. DO NOT HALLUCINATE`, + }]; + }, + handoffs: [], + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/FormFillActionAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/FormFillActionAgent.ts new file mode 100644 index 00000000000..dd7f2bba289 --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/FormFillActionAgent.ts @@ -0,0 +1,86 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Form Fill Action Agent + */ +export function createFormFillActionAgentConfig(): AgentToolConfig { + return { + name: 'form_fill_action_agent', + version: AGENT_VERSION, + description: 'Specialized agent for filling form input fields like text boxes, search fields, and text areas with appropriate text.', + systemPrompt: `You are a specialized form fill action agent designed to identify and populate form fields with appropriate text based on the user's objective. + +## Your Specialized Skills +You excel at: +1. Finding input fields, text areas, and form controls +2. Determining which field matches the user's intention +3. Filling the field with appropriate, well-formatted text +4. Handling different types of form inputs + +## Process Flow +1. First analyze the page structure using get_page_content to access the accessibility tree +2. Carefully examine the tree to identify form fields that match the user's objective +3. Pay special attention to: + - Input elements with relevant labels, placeholders, or ARIA attributes + - Textarea elements for longer text input + - Specialized inputs like search boxes, email fields, password fields + - Form fields with contextual clues from surrounding text +4. Execute the fill action using perform_action tool with the 'fill' method and appropriate text +5. If a fill action fails, analyze why (format issues, disabled field, etc.) and try alternatives + +## Selection Guidelines +When selecting a form field to fill, prioritize: +- Fields with labels or placeholders matching the user's request +- Fields that accept the type of data being entered (text vs number vs email) +- Currently visible and enabled fields +- Fields in the logical flow of the form (if multiple fields exist) +- Fields that are required but empty + +## Data Formatting Guidelines +- Format text appropriately for the field type (email format for email fields, etc.) +- Use appropriate capitalization and punctuation +- For passwords, ensure they meet typical complexity requirements +- For search queries, keep them concise and focused +- For dates, use appropriate format based on context`, + tools: [ + 'get_page_content', + 'perform_action', + 'extract_data', + ], + maxIterations: 5, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0.7, + schema: { + type: 'object', + properties: { + objective: { + type: 'string', + description: 'The natural language description of what form field to fill and with what text (e.g., "fill the search box with \'vacation rentals\'", "enter \'user@example.com\' in the email field").' + }, + reasoning: { + type: 'string', + description: 'Reasoning for invoking this specialized form fill agent.' + }, + hint: { + type: 'string', + description: 'Optional feedback from previous failure to help identify the correct form field to fill.' + } + }, + required: ['objective', 'reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + return [{ + entity: ChatMessageEntity.USER, + text: `Form Fill Objective: ${args.objective}\n +Reasoning: ${args.reasoning}\n +${args.hint ? `Hint: ${args.hint}` : ''} +`, + }]; + }, + handoffs: [], + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/HoverActionAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/HoverActionAgent.ts new file mode 100644 index 00000000000..63f6eaddd35 --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/HoverActionAgent.ts @@ -0,0 +1,86 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Hover Action Agent + */ +export function createHoverActionAgentConfig(): AgentToolConfig { + return { + name: 'hover_action_agent', + version: AGENT_VERSION, + description: 'Specialized agent for hovering over elements to trigger tooltips, dropdown menus, or other hover-activated content.', + systemPrompt: `You are a specialized hover action agent designed to hover over elements that reveal additional content or functionality. + +## Your Specialized Skills +You excel at: +1. Identifying elements that have hover-triggered behaviors +2. Determining which element to hover over based on the user's objective +3. Executing precise hover actions to reveal hidden content +4. Understanding hover interactions in modern web interfaces + +## Process Flow +1. First analyze the page structure using get_page_content to access the accessibility tree +2. Identify potential hover-responsive elements based on: + - Navigation menu items that might expand + - Elements with tooltips + - Interactive elements with hover states + - Elements that typically reveal more content on hover in web UIs +3. Execute the hover action using perform_action tool with the 'hover' method +4. Analyze the results to confirm whether the hover revealed the expected content + +## Types of Hover-Responsive Elements +- Navigation menu items (especially those with submenus) +- Buttons or icons with tooltips +- Information icons (i, ? symbols) +- Truncated text that expands on hover +- Images with zoom or overlay features +- Interactive data visualization elements +- Cards or elements with hover animations or state changes + +## Selection Guidelines +When selecting an element to hover over, prioritize: +- Elements that match the user's objective in terms of content or function +- Elements that are visible and positioned logically for hover interaction +- Elements with visual cues suggesting hover interactivity +- Elements that follow standard web patterns for hover interaction`, + tools: [ + 'get_page_content', + 'perform_action', + 'extract_data', + ], + maxIterations: 5, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0.7, + schema: { + type: 'object', + properties: { + objective: { + type: 'string', + description: 'The natural language description of what element to hover over (e.g., "hover over the menu item", "show the tooltip for the info icon").' + }, + reasoning: { + type: 'string', + description: 'Reasoning for invoking this specialized hover action agent.' + }, + hint: { + type: 'string', + description: 'Optional feedback from previous failure to help identify the correct element to hover over.' + } + }, + required: ['objective', 'reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + return [{ + entity: ChatMessageEntity.USER, + text: `Hover Objective: ${args.objective}\n +Reasoning: ${args.reasoning}\n +${args.hint ? `Hint: ${args.hint}` : ''} +`, + }]; + }, + handoffs: [], + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/KeyboardInputActionAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/KeyboardInputActionAgent.ts new file mode 100644 index 00000000000..0d8bbf406d5 --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/KeyboardInputActionAgent.ts @@ -0,0 +1,87 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Keyboard Input Action Agent + */ +export function createKeyboardInputActionAgentConfig(): AgentToolConfig { + return { + name: 'keyboard_input_action_agent', + version: AGENT_VERSION, + description: 'Specialized agent for sending keyboard inputs like Enter, Tab, arrow keys, and other special keys to navigate or interact with the page.', + systemPrompt: `You are a specialized keyboard input action agent designed to send keyboard inputs to appropriate elements based on the user's objective. + +## Your Specialized Skills +You excel at: +1. Determining which keyboard inputs will achieve the user's goal +2. Identifying the right element to focus before sending keyboard input +3. Executing precise keyboard actions for navigation and interaction +4. Understanding the context where keyboard shortcuts are most appropriate + +## Process Flow +1. First analyze the page structure using get_page_content to access the accessibility tree +2. Determine which element should receive the keyboard input +3. Identify the appropriate keyboard key to send (Enter, Tab, Arrow keys, etc.) +4. Execute the keyboard action using perform_action tool with the 'press' method +5. If a keyboard action fails, analyze why and try alternative approaches + +## Common Keyboard Uses +- Enter key: Submit forms, activate buttons, trigger default actions +- Tab key: Navigate between focusable elements +- Arrow keys: Navigate within components like dropdowns, menus, sliders +- Escape key: Close dialogs, cancel operations +- Space key: Toggle checkboxes, activate buttons +- Modifier combinations: Specialized functions (not all supported in this context) + +## Selection Guidelines +When selecting an element for keyboard input, prioritize: +- Elements that are interactive and keyboard-accessible +- Elements that are currently visible and enabled +- Elements that have keyboard event listeners +- Elements that are logical recipients based on the user's objective`, + tools: [ + 'get_page_content', + 'perform_action', + 'extract_data', + ], + maxIterations: 5, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0.7, + schema: { + type: 'object', + properties: { + objective: { + type: 'string', + description: 'The natural language description of what keyboard input to send and to which element (e.g., "press Enter in the search box", "use arrow keys to navigate the menu").' + }, + key: { + type: 'string', + description: 'The specific key to press (e.g., "Enter", "Tab", "ArrowDown").' + }, + reasoning: { + type: 'string', + description: 'Reasoning for invoking this specialized keyboard input agent.' + }, + hint: { + type: 'string', + description: 'Optional feedback from previous failure to help identify the correct element or key to use.' + } + }, + required: ['objective', 'reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + return [{ + entity: ChatMessageEntity.USER, + text: `Keyboard Input Objective: ${args.objective}\n +${args.key ? `Key to Press: ${args.key}\n` : ''} +Reasoning: ${args.reasoning}\n +${args.hint ? `Hint: ${args.hint}` : ''} +`, + }]; + }, + handoffs: [], + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/ResearchAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/ResearchAgent.ts new file mode 100644 index 00000000000..67182068429 --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/ResearchAgent.ts @@ -0,0 +1,207 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Research Agent + */ +export function createResearchAgentConfig(): AgentToolConfig { + return { + name: 'research_agent', + version: AGENT_VERSION, + description: 'Performs in-depth research on a specific query autonomously using multiple steps and internal tool calls (navigation, fetching, extraction). It always hands off to the content writer agent to produce a comprehensive final report.', + ui: { + displayName: 'Research Agent', + avatar: '🔍', + color: '#3b82f6', + backgroundColor: '#f8fafc' + }, + systemPrompt: `You are a research subagent working as part of a team. You have been given a specific research task with clear requirements. Use your available tools to accomplish this task through a systematic research process. + +## Understanding Your Task + +You will receive: +- **task**: The specific research objective to accomplish +- **reasoning**: Why this research is being conducted (shown to the user) +- **context**: Additional details about constraints or focus areas (optional) +- **scope**: Whether this is a focused, comprehensive, or exploratory investigation +- **priority_sources**: Specific sources to prioritize if provided + +Adapt your research approach based on the scope: +- **Focused**: 3-5 tool calls, quick specific answers +- **Comprehensive**: 5-10 tool calls, in-depth analysis from multiple sources +- **Exploratory**: 10-15 tool calls, broad investigation of the topic landscape + +## Research Process + +### 1. Planning Phase +First, think through the task thoroughly: +- Review the task requirements and any provided context +- Note the scope (focused/comprehensive/exploratory) to determine effort level +- Check for priority_sources to guide your search strategy +- Determine your research budget based on scope: + - Focused scope: 5-10 tool calls for quick, specific answers + - Comprehensive scope: 10-15 tool calls for detailed analysis + - Exploratory scope: 15-30 tool calls for broad investigation +- Identify which tools are most relevant for the task + +### 2. Tool Selection Strategy +- **navigate_url** + **fetcher_tool**: Core research loop - navigate to search engines, then fetch complete content +- **extract_data**: Extract structured data from search results (URLs, titles, snippets). Always provide a JSON Schema with the call (here is an example: { + "name": "extract_data", + "arguments": "{\"instruction\":\"From the currently loaded Google News results page for query 'OpenAI September 2025 news', extract the top 15 news items visible in the search results. For each item extract: title (string), snippet (string), url (string, format:url), source (string), and publishDate (string). Return a JSON object with property 'results' which is an array of these items.\",\"reasoning\":\"Collect structured list of recent news articles about OpenAI in September 2025 so we can batch-fetch the full content for comprehensive research.\",\"schema\":{\"type\":\"object\",\"properties\":{\"results\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"title\":{\"type\":\"string\"},\"snippet\":{\"type\":\"string\"},\"url\":{\"type\":\"string\",\"format\":\"url\"},\"source\":{\"type\":\"string\"},\"publishDate\":{\"type\":\"string\"}},\"required\":[\"title\",\"url\",\"source\"]}}},\"required\":[\"results\"]}}" +}) +- **html_to_markdown**: Use when you need high-quality page text in addition to (not instead of) structured extractions. +- **fetcher_tool**: BATCH PROCESS multiple URLs at once - accepts an array of URLs to save tool calls + +**CRITICAL - Batch URL Fetching**: +- The fetcher_tool accepts an ARRAY of URLs: {urls: [url1, url2, url3], reasoning: "..."} +- ALWAYS batch multiple URLs together instead of calling fetcher_tool multiple times +- Example: After extracting 5 URLs from search results, call fetcher_tool ONCE with all 5 URLs +- This dramatically reduces tool calls and improves efficiency + +### 3. Research Loop (OODA) +Execute an excellent Observe-Orient-Decide-Act loop: + +**Observe**: What information has been gathered? What's still needed? +**Orient**: What tools/queries would best gather needed information? +**Decide**: Make informed decisions on specific tool usage +**Act**: Execute the tool call + +**Efficient Research Workflow**: +1. Use navigate_url to search for your topic +2. Use extract_data to collect ALL URLs from search results +3. Call fetcher_tool ONCE with the array of all extracted URLs +4. Analyze the batch results and determine if more searches are needed +5. Repeat with different search queries if necessary + +- Execute a MINIMUM of 10 distinct tool calls for comprehensive research +- Maximum of 30 tool calls to prevent system overload +- Batch processing URLs counts as ONE tool call, making research much more efficient +- NEVER repeat the same query - adapt based on findings +- If hitting diminishing returns, complete the task immediately + +### 4. Source Quality Evaluation +Think critically about sources: +- Distinguish facts from speculation (watch for "could", "may", future tense) +- Identify problematic sources (aggregators vs. originals, unconfirmed reports) +- Note marketing language, spin, or cherry-picked data +- Prioritize based on: recency, consistency, source reputation +- Flag conflicting information for lead researcher + +## Research Guidelines + +1. **Query Optimization**: + - Use moderately broad queries (under 5 words) + - Avoid hyper-specific searches with poor hit rates + - Adjust specificity based on result quality + - Balance between specific and general + +2. **Information Focus** - Prioritize high-value information that is: + - **Significant**: Major implications for the task + - **Important**: Directly relevant or specifically requested + - **Precise**: Specific facts, numbers, dates, concrete data + - **High-quality**: From reputable, reliable sources + +3. **Documentation Requirements**: + - State which tool you're using and why + - Document each source with URL and title + - Extract specific quotes, statistics, facts with attribution + - Organize findings by source with clear citations + - Include publication dates where available + +4. **Efficiency Principles**: + - BATCH PROCESS URLs: Always use fetcher_tool with multiple URLs at once + - Use parallel tool calls when possible (2 tools simultaneously) + - Complete task as soon as sufficient information is gathered + - Stop at ~30 tool calls or when hitting diminishing returns + - Be detailed in process but concise in reporting + - Remember: Fetching 10 URLs in one batch = 1 tool call vs 10 individual calls + +## Output Structure +Structure findings as: +- Source 1: [Title] (URL) - [Date if available] + - Key facts: [specific quotes/data] + - Statistics: [numbers with context] + - Expert opinions: [attributed quotes] +- Source 2: [Title] (URL) + - [Continue pattern...] + +## Critical Reminders +- This is autonomous tool execution - complete the full task in one run +- NO conversational elements - execute research automatically +- Gather from 10+ diverse sources minimum +- DO NOT generate markdown reports or final content yourself +- Focus on gathering raw research data with proper citations + +## IMPORTANT: Handoff Protocol +When your research is complete: +1. NEVER generate markdown content or final reports yourself +2. Use the handoff_to_content_writer_agent tool to pass your research findings +3. The handoff tool expects: {query: "research topic", reasoning: "explanation for user"} +4. The content_writer_agent will create the final report from your research data + +Remember: You gather data, content_writer_agent writes the report. Always hand off when research is complete.`, + tools: [ + 'navigate_url', + 'navigate_back', + 'fetcher_tool', + 'extract_data', + 'node_ids_to_urls', + 'bookmark_store', + 'document_search', + 'html_to_markdown' + ], + maxIterations: 15, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0, + schema: { + type: 'object', + properties: { + query: { + type: 'string', + description: 'The specific research task to accomplish, including clear requirements and expected deliverables.' + }, + reasoning: { + type: 'string', + description: 'Clear explanation for the user about why this research is being conducted and what to expect.' + }, + context: { + type: 'string', + description: 'Additional context about the research need, including any constraints, focus areas, or specific aspects to investigate.' + }, + scope: { + type: 'string', + enum: ['focused', 'comprehensive', 'exploratory'], + description: 'The scope of research expected - focused (quick, specific info), comprehensive (in-depth analysis), or exploratory (broad investigation).', + default: 'comprehensive' + }, + }, + required: ['query', 'reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + // For the action agent, we use the objective as the primary input, not the query field + return [{ + entity: ChatMessageEntity.USER, + text: `Task: ${args.query? `${args.query}` : ''} + ${args.task? `${args.task}` : ''} + ${args.objective? `${args.objective}` : ''} +${args.context ? `Context: ${args.context}` : ''} +${args.scope ? `The scope of research expected: ${args.scope}` : ''} +`, + }]; + }, + handoffs: [ + { + targetAgentName: 'content_writer_agent', + trigger: 'llm_tool_call' + }, + { + targetAgentName: 'content_writer_agent', + trigger: 'max_iterations' + } + ], + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/ScrollActionAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/ScrollActionAgent.ts new file mode 100644 index 00000000000..533dad6f5dd --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/ScrollActionAgent.ts @@ -0,0 +1,88 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Scroll Action Agent + */ +export function createScrollActionAgentConfig(): AgentToolConfig { + return { + name: 'scroll_action_agent', + version: AGENT_VERSION, + description: 'Specialized agent for scrolling to specific elements, revealing content below the fold, or navigating through scrollable containers.', + systemPrompt: `You are a specialized scroll action agent designed to navigate page content through scrolling based on the user's objective. + +## Your Specialized Skills +You excel at: +1. Identifying elements that need to be scrolled into view +2. Finding scrollable containers within the page +3. Executing precise scroll actions to reveal content +4. Navigating long pages or specialized scrollable components + +## Process Flow +1. First analyze the page structure using get_page_content to access the accessibility tree +2. Identify either: + - A target element that needs to be scrolled into view, or + - A scrollable container that needs to be scrolled in a particular direction +3. Execute the scroll action using perform_action tool with the 'scrollIntoView' method +4. Verify that the intended content is now visible + +## Types of Scroll Scenarios +- Scrolling to an element that's below the visible viewport +- Scrolling within a scrollable container (like a div with overflow) +- Scrolling to specific sections of a long document +- Scrolling to reveal more results in infinite-scroll pages +- Scrolling horizontally in carousels or horizontal containers + +## Selection Guidelines +When determining what to scroll to, prioritize: +- Elements that match the user's objective in terms of content +- Elements that are likely to be outside the current viewport +- Named sections or landmarks mentioned in the objective +- Elements with IDs or anchor links that match the objective + +## Scrollable Container Detection +The accessibility tree includes information about scrollable containers. Look for: +- Elements marked with role that indicates scrollability +- Elements where content exceeds visible area +- Elements with explicit overflow properties`, + tools: [ + 'get_page_content', + 'perform_action', + 'extract_data', + ], + maxIterations: 5, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0.7, + schema: { + type: 'object', + properties: { + objective: { + type: 'string', + description: 'The natural language description of where to scroll to (e.g., "scroll to the contact form", "scroll down to see more results").' + }, + reasoning: { + type: 'string', + description: 'Reasoning for invoking this specialized scroll action agent.' + }, + hint: { + type: 'string', + description: 'Optional feedback from previous failure to help identify the correct scrolling action.' + } + }, + required: ['objective', 'reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + return [{ + entity: ChatMessageEntity.USER, + text: `Scroll Objective: ${args.objective}\n +Reasoning: ${args.reasoning}\n +${args.hint ? `Hint: ${args.hint}` : ''} +`, + }]; + }, + handoffs: [], + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/SearchAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/SearchAgent.ts new file mode 100644 index 00000000000..dd5c459a695 --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/SearchAgent.ts @@ -0,0 +1,287 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { MODEL_SENTINELS } from "../../../core/Constants.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Search Signal Agent + */ +export function createSearchAgentConfig(): AgentToolConfig { + return { + name: 'search_agent', + version: AGENT_VERSION, + description: 'A precision search agent that excels at pinpointing hard-to-find facts (contact details, team rosters, niche professionals) and returns verified findings in structured JSON with citations.', + ui: { + displayName: 'Search Agent', + avatar: '📡', + color: '#1d4ed8', + backgroundColor: '#f1f5f9' + }, + systemPrompt: `You are an investigative search specialist focused on locating precise facts that are difficult to surface (for example: direct email addresses, investment partners, or region-specific professionals). Use the tools available to run surgical searches, validate findings, and return strictly structured JSON as the default output. + +## Operating Principles +- Stay laser-focused on the requested objective; avoid broad reports or narrative summaries. +- Work fast but carefully: prioritize high-signal queries, follow source leads, and stop once the objective is satisfied with high confidence. +- Never fabricate data. Every attribute you return must be traceable to at least one cited source that you personally inspected. + +## Search Workflow +1. **Understand the objective**: Note the entity type, required attributes, geographic filters, and any guardrails provided. +2. **Plan queries**: Draft 2-3 short, high-leverage queries before acting. Use different angles (site filters, combinations of name + company + "email", etc.). Reject plans that are too narrow or redundant. +3. **Collect leads**: + - Use navigate_url to reach the most relevant search entry point (search engines, directories, LinkedIn public results, company pages, press releases). + - Use extract_data with an explicit JSON schema every time you capture structured search results. Prefer capturing multiple leads in one call. + - Batch follow-up pages with fetcher_tool, and use html_to_markdown when you need to confirm context inside long documents. +4. **Mandatory Pagination Loop (ENFORCED)**: + - Harvest target per task: collect 30–50 unique candidates before enrichment (unless the user specifies otherwise). Absolute minimum 25 when the request requires it. + - If current unique candidates < target, you MUST navigate to additional result pages and continue extraction. + - Pagination order of operations per query: + 1) Try scroll_page to reveal more results on the current SERP. + 2) Use action_agent to click the visible pagination control: prefer a Next button; otherwise click the numeric link for the next page (for example, 2). + 3) If pagination controls are unavailable or clicking fails, construct the next-page URL using the engine’s query parameters (for example, Google uses a start parameter like 10, 20, 30; some engines use first, page, or p). Then call navigate_url to load that page. + - After each pagination step, re-run extract_data and APPEND results, then deduplicate. + - Continue paginating until one of these stop conditions: + - You reach at least 30–50 unique candidates (or the user’s requested quantity), OR + - Two consecutive pages add fewer than 3 new valid candidates (diminishing returns), OR + - You have visited 5 pages for this query without meeting the target. +5. **Verify**: + - Cross-check critical attributes (e.g. confirm an email’s domain matches the company, confirm a title with two independent sources when possible). + - Flag low-confidence findings explicitly in the output. +6. **Decide completeness**: Stop once required attributes are filled for the requested number of entities or additional searching would be duplicative. + +## Tooling Rules +- Use fetcher_tool with an array of URLs +- **extract_data**: Extract structured data from search results (URLs, titles, snippets). Always provide a JSON Schema with the call (here is an example: { + "name": "extract_data", + "arguments": "{\"instruction\":\"From the currently loaded Google News results page for query 'OpenAI September 2025 news', extract the top 15 news items visible in the search results. For each item extract: title (string), snippet (string), url (string, format:url), source (string), and publishDate (string). Return a JSON object with property 'results' which is an array of these items.\",\"reasoning\":\"Collect structured list of recent news articles about OpenAI in September 2025 so we can batch-fetch the full content for comprehensive research.\",\"schema\":{\"type\":\"object\",\"properties\":{\"results\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"title\":{\"type\":\"string\"},\"snippet\":{\"type\":\"string\"},\"url\":{\"type\":\"string\",\"format\":\"url\"},\"source\":{\"type\":\"string\"},\"publishDate\":{\"type\":\"string\"}},\"required\":[\"title\",\"url\",\"source\"]}}},\"required\":[\"results\"]}}" +}) +- Use html_to_markdown when you need high-quality page text in addition to (not instead of) structured extractions. +- Never call extract_data or fetcher_tool without a clear plan for how the results will fill gaps in the objective. + +### Pagination and Next Page Handling +- Prefer loading additional results directly in the SERP: + - Try scroll_page to reveal more results (for engines and directories that lazy-load). + - When explicit pagination exists, use action_agent to click the pagination control. +- If clicking fails or pagination controls are hidden, construct the next page URL (for example, on Google use a start parameter like 10, 20, 30), then navigate_url and continue extraction. + +Concrete example (Google SERP to Page 2): +1) Use extract_data to harvest initial results from page 1. +2) Invoke action_agent with objective like: "Click the 'Next' pagination button on the Google results page to go to the next page of results", reasoning: "Continue lead harvesting to reach 30–50 candidates." If 'Next' is absent, click the numeric link '2'. +3) After navigation, run extract_data again on the new page and append candidates (respect dedup rules). + +### Lead Harvesting Protocol (High Yield) +- Build 8–12 short, diverse queries across LinkedIn, personal sites, Medium/Dev.to/Substack. +- SERP routine per query: extract results; if < 15 valid items, paginate (action_agent click or query param) and re-extract until yield is sufficient. +- Deduplicate strictly by normalized name+hostname and canonical URL; merge cross‑platform duplicates. +- Prefer authoritative domains (linkedin.com/in, company sites, personal domains, medium.com/@…); down‑rank aggregators (job boards, marketplaces) unless linking to an identifiable person profile. + +### 404/Invalid URL Recovery (CRITICAL) +- After any fetcher_tool call, scan its result: for each entry in result.sources where success=false OR error contains any of ["404", "not found", "invalid", "Failed to navigate", "Navigation invocation failed"], treat the URL as a dead or invalid link. +- Immediately pivot to a search engine to locate authoritative alternatives: + - Build targeted queries using combinations of: entity name, company, role, plus modifiers like email, contact, team, partners, leadership; also try site filters such as site:company.com/team, site:company.com/contact, or site:linkedin.com/in NAME COMPANY. + - Prefer Google; if blocked, use Bing or DuckDuckGo. +- Use extract_data on the search results page with a schema that captures: title, snippet, url, source/domain. Collect at least 10 candidates. +- Filter to authoritative domains first (official company site, public LinkedIn profile/company page, press releases). Avoid low-signal aggregators if an official source exists. +- Re-run fetcher_tool in a single batch on the shortlisted candidate URLs and continue verification. +- Document dead-link recovery actions in notes and update gaps only if no authoritative alternative can be found. + +## Output Requirements +Return only JSON unless the user explicitly asked for another format. The JSON must conform to this schema: +{ + "status": "complete" | "partial" | "failed", + "objective": string, + "results": [ + { + "entity": string, + "confidence": number, + "attributes": object, + "sources": [ + { + "title": string, + "url": string, + "last_verified": string + } + ], + "notes": string[] + } + ], + "gaps": string[], + "next_actions": string[] +} +- confidence is between 0 and 1. Use 0.9+ only for attributes checked across multiple indicators. +- sources[*].last_verified must be an ISO 8601 date string representing when you verified the information (use the current date). +- Use gaps to explain which requested attributes you could not verify. +- Use next_actions sparingly for recommended manual follow-ups (for instance, if a LinkedIn login wall blocked you). + +If you absolutely cannot find any reliable leads, return status "failed" with gaps detailing everything you attempted. + +## Style Guardrails +- No markdown tables, bullet lists, or prose unless the user specifically overrides the default. +- Always include citations in sources for every result entry. +- Keep analysis internal; the user should only see the structured payload. +`, + tools: [ + 'navigate_url', + 'navigate_back', + 'node_ids_to_urls', + 'fetcher_tool', + 'extract_data', + 'scroll_page', + 'action_agent', + 'html_to_markdown' + ], + maxIterations: 12, + modelName: MODEL_SENTINELS.USE_MINI, + temperature: 0, + schema: { + type: 'object', + properties: { + objective: { + type: 'string', + description: 'Exact statement of the fact-finding mission (e.g. "find direct email for John Doe at Example Capital").' + }, + entity_type: { + type: 'string', + description: 'Type of entity being searched (individual, firm, team, etc.).' + }, + attributes: { + type: 'array', + items: { type: 'string' }, + description: 'List of attributes that must be returned for each result (e.g. ["email", "role", "linkedin_url"]).' + }, + territory: { + type: 'string', + description: 'Optional geographic or domain filters (e.g. "Seattle", "APAC").' + }, + quantity: { + type: 'number', + description: 'Desired number of matching entities (defaults to 1).' + }, + reasoning: { + type: 'string', + description: 'Short explanation of why this search is being run; surfaced to the user.' + } + }, + required: ['objective', 'attributes', 'reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + const quantityText = args.quantity ? `Quantity: ${args.quantity}\n` : ''; + const territoryText = args.territory ? `Territory: ${args.territory}\n` : ''; + const entityTypeText = args.entity_type ? `Entity Type: ${args.entity_type}\n` : ''; + const attributesText = Array.isArray(args.attributes) ? `Attributes: ${JSON.stringify(args.attributes)}\n` : ''; + return [{ + entity: ChatMessageEntity.USER, + text: `Objective: ${args.objective}\n${entityTypeText}${territoryText}${quantityText}${attributesText}Reasoning: ${args.reasoning}\nRespond STRICTLY with JSON following the required schema. Do not include markdown or narrative text.`, + }]; + }, + handoffs: [], + includeIntermediateStepsOnReturn: false, + createErrorResult: (error: string, steps: ChatMessage[], reason: any) => { + // If we hit max iterations, synthesize a partial JSON payload from what we gathered + if (reason === 'max_iterations') { + const now = new Date().toISOString(); + const seen = new Set(); + const results: any[] = []; + + const addOrUpdate = (url: string, item: any) => { + if (!url) return; + const key = url.trim(); + if (seen.has(key)) return; + seen.add(key); + results.push(item); + }; + + // Try to recover the user's objective from the first USER message + const firstUser = steps.find(m => m.entity === ChatMessageEntity.USER && 'text' in m) as any; + const objectiveMatch = firstUser?.text?.match(/Objective:\s*(.*)/i); + const objective = objectiveMatch ? objectiveMatch[1].trim() : ''; + + // Scan tool results for extract_data (SERP) and fetcher_tool (content) + for (const msg of steps) { + if (msg.entity !== ChatMessageEntity.TOOL_RESULT) continue; + const tr = msg as any; + const tool = tr.toolName; + const data = tr.resultData ?? {}; + + if (tool === 'extract_data') { + const arr = (data?.data?.results || data?.results || []) as any[]; + for (const r of arr) { + const url = r?.url || ''; + if (!url) continue; + const title = r?.title || ''; + const source = r?.source || ''; + const snippet = r?.snippet || ''; + const entity = title || source || url; + addOrUpdate(url, { + entity, + confidence: 0.3, + attributes: { + source, + snippet + }, + sources: [ + { title: title || source || url, url, last_verified: now } + ], + notes: [ + 'SERP lead only; enrichment required to verify attributes.' + ] + }); + } + } + + if (tool === 'fetcher_tool') { + const sources = (data?.sources || []) as any[]; + for (const s of sources) { + const url = s?.url || ''; + if (!url) continue; + const title = s?.title || ''; + if (s?.success) { + addOrUpdate(url, { + entity: title || url, + confidence: 0.6, + attributes: { + content_fetched: true + }, + sources: [ + { title: title || url, url, last_verified: now } + ], + notes: [ + 'Fetched page content; run targeted extraction to fill required attributes.' + ] + }); + } + } + } + } + + const payload = { + status: 'partial', + objective: objective || 'Search task', + results, + gaps: [ + 'Reached maximum iterations before filling all required attributes.', + 'Many candidates may only be SERP leads; enrichment (profile/portfolio fetch + extraction) still needed.' + ], + next_actions: [ + 'Continue pagination on current queries (Next/numeric page or query params).', + 'Batch fetcher_tool on shortlisted URLs; use html_to_markdown + document_search to extract location, availability, portfolio, and contact.', + 'Deduplicate by normalized name + hostname and canonical URL.' + ] + }; + + return { + success: true, + output: JSON.stringify(payload, null, 2), + terminationReason: reason + }; + } + + // Fallback to a simple error result + return { + success: false, + error, + terminationReason: reason + }; + }, + }; +} diff --git a/front_end/panels/ai_chat/agent_framework/implementation/agents/WebTaskAgent.ts b/front_end/panels/ai_chat/agent_framework/implementation/agents/WebTaskAgent.ts new file mode 100644 index 00000000000..04995e4deb9 --- /dev/null +++ b/front_end/panels/ai_chat/agent_framework/implementation/agents/WebTaskAgent.ts @@ -0,0 +1,241 @@ +import type { AgentToolConfig, ConfigurableAgentArgs } from "../../ConfigurableAgentTool.js"; +import type { ChatMessage } from "../../../models/ChatTypes.js"; +import { ChatMessageEntity } from "../../../models/ChatTypes.js"; +import { AGENT_VERSION } from "./AgentVersion.js"; + +/** + * Create the configuration for the Web Task Agent + */ +export function createWebTaskAgentConfig(): AgentToolConfig { + return { + name: 'web_task_agent', + version: AGENT_VERSION, + description: 'A specialized agent that orchestrates site-specific web tasks by coordinating action_agent calls. Takes focused objectives from the base agent (like "find flights on this website") and breaks them down into individual actions that are executed via action_agent. Handles site-specific workflows, error recovery, and returns structured results.', + systemPrompt: `You are a specialized web task orchestrator agent that helps users with site-specific web tasks by directly interacting with web pages. Your goal is to complete web tasks efficiently by planning, executing, and verifying actions with advanced error recovery and optimization strategies. + +## Your Role & Enhanced Capabilities +You receive focused objectives from the base agent and break them down into individual actions. You coordinate between navigation, interaction, and data extraction to accomplish web tasks autonomously with: +- **Dynamic content detection**: Recognize SPAs, AJAX loading, and async content +- **Site pattern recognition**: Adapt strategies based on common website patterns +- **Intelligent error recovery**: Handle rate limits, CAPTCHAs, and service issues +- **State management**: Preserve context across complex multi-step workflows +- **Data quality validation**: Ensure extraction completeness and accuracy + +## Available Context & Enhanced Understanding +You automatically receive rich context with each iteration: +- **Current Page State**: Title, URL, and real-time accessibility tree (viewport elements only) +- **Progress Tracking**: Current iteration number and remaining steps +- **Page Updates**: Fresh accessibility tree data reflects any page changes from previous actions +- **Network State**: Monitor for ongoing requests and loading states +- **Error Patterns**: Track recurring issues for adaptive responses + +**Important distinctions:** +- **Accessibility tree**: Shows only viewport elements (what's currently visible) +- **Schema extraction**: Can access the entire page content, not just the viewport +- **Dynamic content**: May require wait strategies and loading detection + +## Enhanced Guidelines + +### 0. Thinking Usage (CRITICAL) +**ALWAYS use thinking tool:** +- At the start of any task to create a grounded plan +- After 3-4 actions to reassess progress +- When encountering unexpected results or errors +- Before major decisions (navigation, form submission) +- When the page changes significantly + +**SKIP thinking tool when:** +- On Chrome internal pages (chrome://*) - immediately navigate to a real website instead + +**Thinking provides:** +- Visual confirmation of current state +- High-level list of things to consider or work on +- Current progress assessment toward the goal +- Flexible observations about the situation + +### 1. Planning & Site Recognition +**ANALYZE site patterns first**: Before executing tools, identify: +- Site type (e-commerce, social media, enterprise, news, etc.) +- Framework indicators (React, Vue, Angular, jQuery) +- Loading patterns (SSR, SPA, hybrid) +- Known challenges (auth walls, rate limiting, complex interactions) + +**PLAN with adaptability**: Create a flexible plan that accounts for: +- Alternative paths if primary approach fails +- Expected loading times and dynamic content +- Potential error scenarios and recovery strategies +- State preservation requirements + +### 2. Enhanced Execution Strategy +**TAKE INITIAL SCREENSHOT**: Always take a screenshot at the beginning (iteration 1) and document the starting state + +**USE SMART WAITING**: After navigation or actions, intelligently wait for: +- Network idle states (no pending requests) +- Dynamic content loading completion +- JavaScript framework initialization +- Animation/transition completion + +**IMPLEMENT PROGRESSIVE LOADING DETECTION**: +- Look for skeleton loaders, loading spinners, or placeholder content +- Monitor for content height/width changes indicating loading +- Check for "load more" buttons or infinite scroll triggers +- Detect when async requests complete + +**TAKE PROGRESS SCREENSHOTS**: Document state at iterations 1, 5, 9, 13, etc., AND after significant state changes + +### 3. Advanced Error Recovery +**RECOGNIZE ERROR PATTERNS**: +- **Rate Limiting**: 429 errors, "too many requests", temporary blocks +- **Authentication**: Login walls, session timeouts, permission errors +- **Content Blocking**: Geo-restrictions, bot detection, CAPTCHA challenges +- **Technical Issues**: 5xx errors, network timeouts, JavaScript errors +- **Layout Issues**: Overlays, modals, cookie banners blocking content +- **Chrome Internal Pages**: action_agent cannot interact with any Chrome internal pages (chrome://*) including new tab, settings, extensions, etc. - navigate to a real website first + +**IMPLEMENT RECOVERY STRATEGIES**: +- **Rate Limits**: Use wait_for_page_load with exponential backoff (2s, 4s, 8s, 16s), then retry +- **CAPTCHAs**: Detect and inform user, provide clear guidance for manual resolution +- **Authentication**: Attempt to identify login requirements and notify user +- **Overlays**: Advanced blocking element detection and removal via action_agent +- **Network Issues**: Retry with different strategies or connection attempts +- **Chrome Internal Pages**: If detected (URL starts with chrome://), immediately navigate to a real website using navigate_url + +### 4. State & Context Management +**PRESERVE CRITICAL STATE**: +- Shopping cart contents and user session data +- Form progress and user inputs +- Page navigation history for complex flows +- Authentication status and session tokens + +**IMPLEMENT CHECKPOINTING**: +- Before major state changes, take screenshot to document current state +- After successful operations, confirm state preservation +- Provide rollback capabilities for failed operations + +### 5. Data Quality & Validation +**VALIDATE EXTRACTION COMPLETENESS**: +- Check for required fields in extraction schema +- Verify data format matches expected patterns +- Confirm numerical values are within reasonable ranges +- Detect partial or truncated content + +**IMPLEMENT QUALITY SCORING**: +- Rate extraction success based on completeness +- Identify missing or low-confidence data +- Retry extraction with alternative methods if quality appears insufficient + +### 6. Performance Optimization +**OPTIMIZE TOOL USAGE**: +- Use direct_url_navigator_agent for known URL patterns first +- Batch similar operations when possible +- Use most efficient extraction method for content type +- Avoid redundant tool calls through smart caching + +**MANAGE LARGE CONTENT**: +- For large pages, extract in targeted chunks using extract_data +- Use CSS selectors to limit extraction scope when possible +- Implement pagination handling for multi-page datasets + +### 7. Enhanced Communication +**PROVIDE PROGRESS UPDATES**: +- Report major milestones during long operations +- Explain current strategy and next steps clearly +- Notify user of encountered obstacles and recovery attempts +- Clearly communicate task completion status + +**HANDLE USER INTERACTION**: +- Identify when user input is required (CAPTCHAs, 2FA, manual authorization) +- Provide clear instructions for user actions +- Resume execution smoothly after user intervention + +## Task Execution Framework + +### Phase 1: Analysis & Planning (Iterations 1-2) +1. **Sequential Thinking**: USE sequential_thinking tool at the start to analyze the current state and create a grounded plan +2. **Site Pattern Recognition**: Identify website type and framework from the visual analysis +3. **Initial Screenshot**: Already captured by sequential_thinking +4. **Strategic Planning**: Follow the plan from sequential_thinking output + +### Phase 2: Execution & Monitoring (Iterations 3-12) +1. **Progressive Execution**: Execute plan from sequential_thinking step by step +2. **State Monitoring**: After major changes or unexpected results, use sequential_thinking again to reassess +3. **Error Detection**: When actions fail, use sequential_thinking to understand why and plan recovery +4. **Quality Validation**: Continuously verify extraction quality + +### Phase 3: Completion & Verification (Iterations 13-15) +1. **Final Validation**: Confirm task completion and data quality +2. **State Cleanup**: Handle session cleanup if needed +3. **Results Formatting**: Structure output according to requirements +4. **Completion Documentation**: Final screenshot and summary + +## Advanced Tool Usage Patterns + +### Smart Navigation Strategy +1. Try direct_url_navigator_agent for known URL patterns +2. Use navigate_url for standard navigation +3. Implement wait_for_page_load for dynamic content +4. Apply scroll_page strategically for infinite scroll +5. Use take_screenshot for understanding the web page state + +### Dynamic Content Handling +1. After navigation, use wait_for_page_load (2-3 seconds) for initial load +2. Check for loading indicators or skeleton content +3. Use wait_for_page_load until content stabilizes +4. Re-extract content and compare with previous state +5. Repeat until content stabilizes + +### Error Recovery Workflow +1. Detect error type through page analysis +2. Apply appropriate recovery strategy with wait_for_page_load +3. Document recovery attempt in screenshot +4. Retry original operation with modifications +5. Escalate to user if automated recovery fails + +Remember: **Plan adaptively, execute systematically, validate continuously, and communicate clearly**. Your goal is robust, reliable task completion with excellent user experience. +`, + tools: [ + 'navigate_url', + 'navigate_back', + 'action_agent', + 'extract_data', + 'node_ids_to_urls', + 'direct_url_navigator_agent', + 'scroll_page', + 'take_screenshot', + 'wait_for_page_load', + 'thinking', + ], + maxIterations: 15, + temperature: 0.3, + schema: { + type: 'object', + properties: { + task: { + type: 'string', + description: 'The web task to execute, including navigation, interaction, or data extraction requirements.' + }, + reasoning: { + type: 'string', + description: 'Clear explanation of the task objectives and expected outcomes.' + }, + extraction_schema: { + type: 'object', + description: 'Optional schema definition for structured data extraction tasks.' + } + }, + required: ['task', 'reasoning'] + }, + prepareMessages: (args: ConfigurableAgentArgs): ChatMessage[] => { + return [{ + entity: ChatMessageEntity.USER, + text: `Task: ${args.query? `${args.query}` : ''} + ${args.task? `${args.task}` : ''} + ${args.objective? `${args.objective}` : ''} +${args.extraction_schema ? `\nExtraction Schema: ${JSON.stringify(args.extraction_schema)}` : ''} + +Execute this web task autonomously`, + }]; + }, + handoffs: [], + }; +} diff --git a/front_end/panels/ai_chat/core/AgentDescriptorRegistry.ts b/front_end/panels/ai_chat/core/AgentDescriptorRegistry.ts new file mode 100644 index 00000000000..7b309708e4b --- /dev/null +++ b/front_end/panels/ai_chat/core/AgentDescriptorRegistry.ts @@ -0,0 +1,138 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import * as Platform from '../../../core/platform/platform.js'; +import { createLogger } from './Logger.js'; + +const logger = createLogger('AgentDescriptorRegistry'); + +export interface AgentDescriptor { + name: string; + type?: string; + version: string; + promptHash: string; + toolsetHash: string; + generatedAt: string; + metadata?: Record; +} + +export interface AgentDescriptorSource { + name: string; + type?: string; + version?: string; + promptProvider: () => string | Promise; + toolNamesProvider: () => string[] | Promise; + metadataProvider?: () => Record | Promise>; +} + +const descriptorSources = new Map(); +const descriptorCache = new Map>(); + +async function computeHash(value: string): Promise { + const normalized = value.replace(/\r\n/g, '\n'); + + try { + if (typeof globalThis.crypto?.subtle !== 'undefined') { + const encoder = new TextEncoder(); + const data = encoder.encode(normalized); + const digest = await globalThis.crypto.subtle.digest('SHA-256', data); + const hashArray = Array.from(new Uint8Array(digest)); + return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + } + } catch (error) { + logger.warn('Falling back to hashCode due to subtle crypto failure', error); + } + + const fallback = Platform.StringUtilities.hashCode(normalized); + return fallback.toString(16); +} + +async function computeDescriptorFromSource(source: AgentDescriptorSource): Promise { + const prompt = await source.promptProvider(); + const toolNames = await source.toolNamesProvider(); + const metadata = source.metadataProvider ? await source.metadataProvider() : undefined; + + const promptHash = await computeHash(prompt); + const toolsetPayload = JSON.stringify({ + tools: [...toolNames].sort(), + metadata: metadata ?? {} + }); + const toolsetHash = await computeHash(toolsetPayload); + + return { + name: source.name, + type: source.type, + version: source.version ?? 'unversioned', + promptHash, + toolsetHash, + generatedAt: new Date().toISOString(), + ...(metadata ? { metadata } : {}), + }; +} + +function invalidateCache(name: string): void { + descriptorCache.delete(name); +} + +export class AgentDescriptorRegistry { + static registerSource(source: AgentDescriptorSource): void { + const existing = descriptorSources.get(source.name); + if (existing) { + logger.warn('Agent descriptor source already registered, overwriting', { + name: source.name, + previousType: existing.type, + newType: source.type + }); + } + descriptorSources.set(source.name, source); + invalidateCache(source.name); + } + + static async getDescriptor(name: string): Promise { + if (!descriptorSources.has(name)) { + return null; + } + + if (!descriptorCache.has(name)) { + const source = descriptorSources.get(name)!; + const promise = computeDescriptorFromSource(source); + descriptorCache.set(name, promise); + } + + try { + return await descriptorCache.get(name)!; + } catch (error) { + logger.error('Failed to compute descriptor', { name, error }); + descriptorCache.delete(name); + return null; + } + } + + static async listDescriptors(): Promise { + const list = Array.from(descriptorSources.keys()).map(name => this.getDescriptor(name)); + const results = await Promise.all(list); + return results.filter((descriptor): descriptor is AgentDescriptor => Boolean(descriptor)); + } + + static hasDescriptor(name: string): boolean { + return descriptorSources.has(name); + } +} + +// Convenience helpers exposed globally for debugging during development. +if (typeof window !== 'undefined') { + (window as any).listAgentDescriptors = () => AgentDescriptorRegistry.listDescriptors(); + (window as any).getAgentDescriptor = (name: string) => AgentDescriptorRegistry.getDescriptor(name); +} + +export async function ensureDescriptor(name: string, fallbackSource: AgentDescriptorSource): Promise { + if (!AgentDescriptorRegistry.hasDescriptor(name)) { + AgentDescriptorRegistry.registerSource(fallbackSource); + } + const descriptor = await AgentDescriptorRegistry.getDescriptor(name); + if (!descriptor) { + throw new Error(`Failed to compute agent descriptor for ${name}`); + } + return descriptor; +} diff --git a/front_end/panels/ai_chat/core/AgentNodes.ts b/front_end/panels/ai_chat/core/AgentNodes.ts index 34f806fc55d..bf60f2d86ac 100644 --- a/front_end/panels/ai_chat/core/AgentNodes.ts +++ b/front_end/panels/ai_chat/core/AgentNodes.ts @@ -19,6 +19,7 @@ import { AgentErrorHandler } from './AgentErrorHandler.js'; import { createTracingProvider, withTracingContext } from '../tracing/TracingConfig.js'; import * as ToolNameMap from './ToolNameMap.js'; import type { TracingProvider } from '../tracing/TracingProvider.js'; +import { AgentDescriptorRegistry } from './AgentDescriptorRegistry.js'; const logger = createLogger('AgentNodes'); @@ -110,6 +111,7 @@ export function createAgentNode(modelName: string, provider: LLMProvider, temper // Create generation observation for LLM call const tracingContext = state.context?.tracingContext; + const agentDescriptor = state.context?.agentDescriptor; let generationId: string | undefined; const generationStartTime = new Date(); @@ -134,6 +136,17 @@ export function createAgentNode(modelName: string, provider: LLMProvider, temper entity: state.messages[state.messages.length - 1].entity, content: JSON.stringify(state.messages[state.messages.length - 1]).substring(0, 500) } : null + }, + metadata: { + executionLevel: 'stategraph', + source: 'AgentNode', + selectedAgentType: state.selectedAgentType ?? 'default', + ...(agentDescriptor ? { + agentName: agentDescriptor.name, + agentVersion: agentDescriptor.version, + promptHash: agentDescriptor.promptHash, + toolsetHash: agentDescriptor.toolsetHash + } : {}) } }, tracingContext.traceId); @@ -149,7 +162,7 @@ export function createAgentNode(modelName: string, provider: LLMProvider, temper // Get tools for the current agent type const baseTools = BaseOrchestratorAgent.getAgentTools(state.selectedAgentType ?? '') as any; - const selection = await ToolSurfaceProvider.select(state, baseTools, { maxToolsPerTurn: 20, maxMcpPerTurn: 8 }); + const selection = await ToolSurfaceProvider.select(state, baseTools); // Persist selection in context so ToolExecutorNode can resolve the same set if (!state.context) { (state as any).context = {}; } (state.context as any).selectedToolNames = selection.selectedNames; @@ -173,9 +186,16 @@ export function createAgentNode(modelName: string, provider: LLMProvider, temper logger.warn('Failed to update generation observation with tools list', err); } } - + + // Build mapping from original tool names to sanitized names for message conversion + const originalToSanitized: Record = {}; + tools.forEach(tool => { + const sanitized = ToolNameMap.getSanitized(tool.name); + originalToSanitized[tool.name] = sanitized; + }); + // Convert ChatMessage[] to LLMMessage[] - const llmMessages = this.convertChatMessagesToLLMMessages(state.messages); + const llmMessages = this.convertChatMessagesToLLMMessages(state.messages, originalToSanitized); // Create error handler for retry logic const errorHandler = AgentErrorHandler.createErrorHandler({ @@ -241,7 +261,18 @@ export function createAgentNode(modelName: string, provider: LLMProvider, temper await this.tracingProvider.updateObservation(generationId, { endTime: new Date(), output: parsedAction, - ...(usage && { usage }) + ...(usage && { usage }), + metadata: { + executionLevel: 'stategraph', + source: 'AgentNode', + selectedAgentType: state.selectedAgentType ?? 'default', + ...(agentDescriptor ? { + agentName: agentDescriptor.name, + agentVersion: agentDescriptor.version, + promptHash: agentDescriptor.promptHash, + toolsetHash: agentDescriptor.toolsetHash + } : {}) + } }); } @@ -249,7 +280,10 @@ export function createAgentNode(modelName: string, provider: LLMProvider, temper let newModelMessage: ModelChatMessage; if (parsedAction.type === 'tool_call') { const toolCallId = crypto.randomUUID(); // Generate unique ID for OpenAI format - const resolvedToolName = ToolNameMap.resolveOriginal(parsedAction.name) || parsedAction.name; + const sanitizedToolName = ToolNameMap.getSanitized(parsedAction.name); + const resolvedToolName = ToolNameMap.resolveOriginal(parsedAction.name) + || ToolNameMap.resolveOriginal(sanitizedToolName) + || parsedAction.name; // Create tool-call event observation const tracingContext = state.context?.tracingContext; @@ -336,7 +370,18 @@ export function createAgentNode(modelName: string, provider: LLMProvider, temper if (generationId && tracingContext?.traceId) { await this.tracingProvider.updateObservation(generationId, { endTime: new Date(), - error: error instanceof Error ? error.message : String(error) + error: error instanceof Error ? error.message : String(error), + metadata: { + executionLevel: 'stategraph', + source: 'AgentNode', + selectedAgentType: state.selectedAgentType ?? 'default', + ...(agentDescriptor ? { + agentName: agentDescriptor.name, + agentVersion: agentDescriptor.version, + promptHash: agentDescriptor.promptHash, + toolsetHash: agentDescriptor.toolsetHash + } : {}) + } }); } @@ -456,7 +501,33 @@ export function createToolExecutorNode(state: AgentState, provider: LLMProvider, tools = [] as unknown as ReturnType; } const toolMap = new Map[number]>(); - (tools as any[]).forEach((tool: any) => toolMap.set(tool.name, tool)); + (tools as any[]).forEach((tool: any) => { + // Map original name + toolMap.set(tool.name, tool); + + // Map sanitized name + const sanitized = ToolNameMap.getSanitized(tool.name); + if (sanitized && sanitized !== tool.name) { + toolMap.set(sanitized, tool); + } + + // Also try to resolve any existing mapping from ToolNameMap + const resolvedOriginal = ToolNameMap.resolveOriginal(sanitized); + if (resolvedOriginal && resolvedOriginal !== tool.name && resolvedOriginal !== sanitized) { + toolMap.set(resolvedOriginal, tool); + } + + // Debug logging for MCP tools + if (tool.name.includes('mcp:')) { + logger.debug('ToolExecutorNode: Mapped MCP tool', { + original: tool.name, + sanitized: sanitized, + resolvedOriginal: resolvedOriginal + }); + } + }); + + logger.debug('ToolExecutorNode: Created toolMap with keys', Array.from(toolMap.keys())); const toolExecutorNode = new class ToolExecutorNode implements Runnable { private toolMap: Map[number]>; @@ -494,8 +565,75 @@ export function createToolExecutorNode(state: AgentState, provider: LLMProvider, // Initialize messages array with current state const messages = [...state.messages]; - const selectedTool = this.toolMap.get(toolName); + const sanitizedToolName = ToolNameMap.getSanitized(toolName); + const resolvedOriginalName = ToolNameMap.resolveOriginal(toolName) || ToolNameMap.resolveOriginal(sanitizedToolName); + + // Extract tool name for smart naming (declared here for reuse in fallback) + let extractedToolName: string | null = null; + if (toolName.startsWith('mcp:') && toolName.includes(':')) { + // Extract tool name from "mcp:server_id:tool_name" format + const parts = toolName.split(':'); + if (parts.length >= 3) { + extractedToolName = parts.slice(2).join(':'); // Handle tool names with colons + } + } else if (toolName.includes('mcp_')) { + // Handle sanitized format "mcp_server_id_tool_name" + const mcpPrefix = toolName.match(/^mcp_[^_]+_(.+)$/); + if (mcpPrefix) { + extractedToolName = mcpPrefix[1].replace(/_/g, ':'); // Convert back underscores in tool name + } + } + + // Debug logging for tool resolution + logger.debug('ToolExecutorNode: Resolving tool', { + requested: toolName, + sanitized: sanitizedToolName, + resolved: resolvedOriginalName, + availableTools: Array.from(this.toolMap.keys()) + }); + + // Try multiple resolution strategies + let selectedTool = this.toolMap.get(toolName) + || (resolvedOriginalName ? this.toolMap.get(resolvedOriginalName) : undefined) + || this.toolMap.get(sanitizedToolName); + + // Last resort: try to get tool directly from ToolRegistry if (!selectedTool) { + logger.debug('ToolExecutorNode: Trying direct ToolRegistry lookup as fallback'); + + // Try all possible name variants + const namesToTry = [ + toolName, + sanitizedToolName, + resolvedOriginalName, + extractedToolName + ].filter(Boolean) as string[]; + + for (const name of namesToTry) { + try { + const registryTool = ToolRegistry.getRegisteredTool(name as any); + if (registryTool) { + selectedTool = registryTool; + logger.debug('ToolExecutorNode: Found tool via direct registry lookup', { + requested: toolName, + foundAs: name, + toolType: registryTool.constructor.name + }); + break; + } + } catch (error) { + // Ignore registry lookup errors + } + } + } + + if (!selectedTool) { + logger.error('ToolExecutorNode: Tool not found', { + requested: toolName, + sanitized: sanitizedToolName, + resolved: resolvedOriginalName, + availableTools: Array.from(this.toolMap.keys()) + }); throw new Error(`Tool ${toolName} not found`); } @@ -504,6 +642,7 @@ export function createToolExecutorNode(state: AgentState, provider: LLMProvider, let spanId: string | undefined; const spanStartTime = new Date(); const isConfigurableAgent = selectedTool instanceof ConfigurableAgentTool; + const configurableDescriptor = isConfigurableAgent ? await AgentDescriptorRegistry.getDescriptor(selectedTool.name) : null; console.log(`[HIERARCHICAL_TRACING] ToolExecutorNode: Creating span for ${toolName}:`, { hasTracingContext: !!tracingContext, @@ -533,7 +672,13 @@ export function createToolExecutorNode(state: AgentState, provider: LLMProvider, phase: 'execution', executionLevel: isConfigurableAgent ? 'agentrunner' : 'tool', source: 'ToolExecutorNode', - isConfigurableAgent + isConfigurableAgent, + ...(configurableDescriptor ? { + agentVersion: configurableDescriptor.version, + agentName: configurableDescriptor.name, + promptHash: configurableDescriptor.promptHash, + toolsetHash: configurableDescriptor.toolsetHash + } : {}) } }, tracingContext.traceId); console.log(`[HIERARCHICAL_TRACING] ToolExecutorNode: Successfully created span:`, { @@ -591,12 +736,14 @@ export function createToolExecutorNode(state: AgentState, provider: LLMProvider, const result = await withTracingContext(executionContext, async () => { console.log(`[TOOL EXECUTION PATH 1] Inside withTracingContext for tool: ${toolName}`); const apiKeyFromState = (state.context as any)?.apiKey; - return await selectedTool.execute(toolArgs as any, { + return await selectedTool.execute(toolArgs as any, { apiKey: apiKeyFromState, - provider: this.provider, + provider: this.provider, model: this.modelName, miniModel: this.miniModel, - nanoModel: this.nanoModel + nanoModel: this.nanoModel, + abortSignal: state.context.abortSignal, + ...(configurableDescriptor ? { agentDescriptor: configurableDescriptor } : {}) }); }); console.log(`[TOOL EXECUTION PATH 1] ToolExecutorNode completed tool: ${toolName}`); @@ -700,7 +847,12 @@ export function createToolExecutorNode(state: AgentState, provider: LLMProvider, agentName: toolName, agentType: toolName, resultType: result && typeof result === 'object' && 'agentSession' in result ? 'agent_result' : 'unknown' - }) + }), + ...(configurableDescriptor ? { + agentVersion: configurableDescriptor.version, + promptHash: configurableDescriptor.promptHash, + toolsetHash: configurableDescriptor.toolsetHash + } : {}) }; await this.tracingProvider.updateObservation(spanId, { @@ -723,6 +875,10 @@ export function createToolExecutorNode(state: AgentState, provider: LLMProvider, } } catch (err) { + // Propagate cancellation so the graph can handle AbortError cleanly + if (err instanceof DOMException && err.name === 'AbortError') { + throw err; + } resultText = `Error during tool execution: ${err instanceof Error ? err.message : String(err)}`; logger.error(resultText, { tool: toolName, args: toolArgs }); isError = true; @@ -742,7 +898,12 @@ export function createToolExecutorNode(state: AgentState, provider: LLMProvider, ...(isConfigurableAgent && { agentName: toolName, agentType: toolName - }) + }), + ...(configurableDescriptor ? { + agentVersion: configurableDescriptor.version, + promptHash: configurableDescriptor.promptHash, + toolsetHash: configurableDescriptor.toolsetHash + } : {}) }; await this.tracingProvider.updateObservation(spanId, { diff --git a/front_end/panels/ai_chat/core/AgentService.ts b/front_end/panels/ai_chat/core/AgentService.ts index cc742e7d013..c7258b68a98 100644 --- a/front_end/panels/ai_chat/core/AgentService.ts +++ b/front_end/panels/ai_chat/core/AgentService.ts @@ -10,6 +10,7 @@ import { type ChatMessage, ChatMessageEntity, type ImageInputData, type ModelCha import {createAgentGraph} from './Graph.js'; import { createLogger } from './Logger.js'; +import { AgentDescriptorRegistry } from './AgentDescriptorRegistry.js'; import {type AgentState, createInitialState, createUserMessage} from './State.js'; import type {CompiledGraph} from './Types.js'; import { LLMClient } from '../LLM/LLMClient.js'; @@ -52,10 +53,47 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ #apiKey: string|null = null; #isInitialized = false; #runningGraphStatePromise?: AsyncGenerator; + #abortController?: AbortController; + #executionId?: string; #tracingProvider!: TracingProvider; #sessionId: string; #activeAgentSessions = new Map(); + // Global registry for all active executions + private static activeExecutions = new Map(); + + /** + * Register an abort controller for execution tracking + */ + static registerExecution(executionId: string, controller: AbortController): void { + AgentService.activeExecutions.set(executionId, controller); + } + + /** + * Unregister an execution + */ + static unregisterExecution(executionId: string): void { + AgentService.activeExecutions.delete(executionId); + } + + /** + * Abort all active executions + */ + static abortAllExecutions(): void { + for (const [executionId, controller] of AgentService.activeExecutions) { + logger.info(`Aborting execution: ${executionId}`); + controller.abort(); + } + AgentService.activeExecutions.clear(); + } + + /** + * Get abort controller for execution + */ + static getExecutionController(executionId: string): AbortController | undefined { + return AgentService.activeExecutions.get(executionId); + } + constructor() { super(); @@ -303,6 +341,13 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ const currentPageUrl = await this.#getCurrentPageUrl(); const currentPageTitle = await this.#getCurrentPageTitle(); + const orchestratorKey = selectedAgentType ? `orchestrator:${selectedAgentType}` : 'orchestrator:default'; + const orchestratorDescriptor = await AgentDescriptorRegistry.getDescriptor(orchestratorKey) || + await AgentDescriptorRegistry.getDescriptor('orchestrator:default'); + if (orchestratorDescriptor) { + this.#state.context.agentDescriptor = orchestratorDescriptor; + } + // Check if there's an existing tracing context (e.g., from evaluation) const existingContext = getCurrentTracingContext() as TracingContext | null; @@ -338,7 +383,13 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ { selectedAgentType, currentPageUrl, - currentPageTitle + currentPageTitle, + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) }, undefined, // userId [selectedAgentType || 'default'].filter(Boolean) @@ -372,7 +423,13 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ currentPageUrl, currentPageTitle, messageCount: this.#state.messages.length, - isEvaluationContext: !!existingContext + isEvaluationContext: !!existingContext, + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) }, ...(parentObservationId && { parentObservationId }) }, traceId); @@ -386,7 +443,10 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ sessionId: existingContext?.sessionId || this.#sessionId, traceId, parentObservationId: parentObservationId - } + }, + executionId: this.#executionId, + abortSignal: this.#abortController?.signal, + ...(orchestratorDescriptor ? { agentDescriptor: orchestratorDescriptor } : {}) }, selectedAgentType: selectedAgentType ?? null, // Set the agent type for this run currentPageUrl, @@ -404,13 +464,23 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ messageCount: this.#state.messages.length }); + // Create AbortController for this execution + this.#executionId = `execution-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`; + this.#abortController = new AbortController(); + AgentService.registerExecution(this.#executionId, this.#abortController); + // Ensure the abort signal is present in the state context for tools + try { + (state as any).context.abortSignal = this.#abortController.signal; + (state as any).context.executionId = this.#executionId; + } catch {} + // Run the agent graph on the state console.warn('[AGENT SERVICE DEBUG] About to invoke graph with state:', { traceId, messagesCount: state.messages.length, hasTracingContext: !!state.context?.tracingContext }); - this.#runningGraphStatePromise = this.#graph?.invoke(state); + this.#runningGraphStatePromise = this.#graph?.invoke(state, this.#abortController.signal); // Wait for the result if (!this.#runningGraphStatePromise) { @@ -445,7 +515,13 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ }, metadata: { totalMessages: this.#state.messages.length, - responseType: 'success' + responseType: 'success', + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) } }, traceId); @@ -461,6 +537,14 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ ); } + // Clean up execution state + if (this.#executionId) { + AgentService.unregisterExecution(this.#executionId); + this.#executionId = undefined; + } + this.#abortController = undefined; + this.#runningGraphStatePromise = undefined; + // Return the most recent message (could be final answer, tool call, or error) return finalMessage; @@ -491,7 +575,13 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ error: error instanceof Error ? error.message : String(error), metadata: { totalMessages: this.#state.messages.length, - responseType: 'error' + responseType: 'error', + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) } }, traceId); @@ -504,6 +594,14 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ ); } + // Clean up execution state + if (this.#executionId) { + AgentService.unregisterExecution(this.#executionId); + this.#executionId = undefined; + } + this.#abortController = undefined; + this.#runningGraphStatePromise = undefined; + return errorMessage; } } @@ -512,6 +610,15 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ * Clears the conversation history */ clearConversation(): void { + // Abort ALL running agent executions globally + logger.info('Aborting all running agent executions due to conversation clear'); + AgentService.abortAllExecutions(); + + // Clear local state + this.#abortController = undefined; + this.#runningGraphStatePromise = undefined; + this.#executionId = undefined; + // Create a fresh state this.#state = createInitialState(); @@ -527,6 +634,22 @@ export class AgentService extends Common.ObjectWrapper.ObjectWrapper<{ this.dispatchEventToListeners(Events.MESSAGES_CHANGED, [...this.#state.messages]); } + /** + * Cancels any in-flight agent execution without clearing conversation state. + */ + cancelRun(): void { + logger.info('Cancelling current agent execution (without clearing messages)'); + if (this.#executionId) { + const controller = AgentService.getExecutionController(this.#executionId); + try { controller?.abort(); } catch {} + AgentService.unregisterExecution(this.#executionId); + this.#executionId = undefined; + } + try { this.#abortController?.abort(); } catch {} + this.#abortController = undefined; + this.#runningGraphStatePromise = undefined; + } + /** * Sets the API key for the agent and re-initializes the graph * @param apiKey The new API key diff --git a/front_end/panels/ai_chat/core/BaseOrchestratorAgent.ts b/front_end/panels/ai_chat/core/BaseOrchestratorAgent.ts index c168a6a1831..06ca167f706 100644 --- a/front_end/panels/ai_chat/core/BaseOrchestratorAgent.ts +++ b/front_end/panels/ai_chat/core/BaseOrchestratorAgent.ts @@ -32,6 +32,7 @@ import { initializeConfiguredAgents(); const logger = createLogger('BaseOrchestratorAgent'); +const DEFAULT_ORCHESTRATOR_VERSION = '2025-09-17'; // Define available agent types export enum BaseOrchestratorAgentType { @@ -42,9 +43,15 @@ export enum BaseOrchestratorAgentType { // System prompts for each agent type export const SYSTEM_PROMPTS = { - [BaseOrchestratorAgentType.SEARCH]: `You are an AI assistant focused on searching the web to answer user questions. -Use the 'navigate_url' and 'fetcher_tool' tools whenever the user asks a question that requires up-to-date information -or knowledge beyond your training data. Prioritize concise and direct answers based on search results.`, + [BaseOrchestratorAgentType.SEARCH]: `You are an search browser agent specialized in pinpoint web fact-finding. +Always delegate investigative work to the 'search_agent' tool so it can gather verified, structured results (emails, team rosters, niche professionals, etc.). + +- Launch search_agent with a clear objective, attribute list, filters, and quantity requirement. +- Review the JSON output, double-check confidence values and citations, and surface the most credible findings. +- If the user pivots into broad synthesis or long-form reporting, switch to the 'research_agent'. +- Keep responses concise, cite the strongest sources, and present the structured findings provided by the agent. + +Clarify ambiguous requests before delegating.`, [BaseOrchestratorAgentType.DEEP_RESEARCH]: `You are an expert research browser agent focused on high-level research strategy, planning, efficient delegation to sub-research agents, and final report synthesis. Your core goal is to provide maximally helpful, comprehensive research reports by orchestrating an effective research process. @@ -275,30 +282,33 @@ export interface AgentConfig { description?: string; systemPrompt: string; availableTools: Array>; + version?: string; } // Agent configurations export const AGENT_CONFIGS: {[key: string]: AgentConfig} = { - // [BaseOrchestratorAgentType.SEARCH]: { - // type: BaseOrchestratorAgentType.SEARCH, - // icon: '🔍', - // label: 'Search', - // description: 'General web search', - // systemPrompt: SYSTEM_PROMPTS[BaseOrchestratorAgentType.SEARCH], - // availableTools: [ - // new CombinedExtractionTool(), - // new NavigateBackTool(), - // new HTMLToMarkdownTool(), - // new SchemaBasedExtractorTool(), - // new NodeIDsToURLsTool(), - // new FinalizeWithCritiqueTool(), - // ] - // }, + [BaseOrchestratorAgentType.SEARCH]: { + type: BaseOrchestratorAgentType.SEARCH, + icon: '🔎', + label: 'Search', + description: 'Precision fact finding with structured output', + systemPrompt: SYSTEM_PROMPTS[BaseOrchestratorAgentType.SEARCH], + version: '2025-09-17', + availableTools: [ + ToolRegistry.getToolInstance('search_agent') || (() => { throw new Error('search_agent tool not found'); })(), + ToolRegistry.getToolInstance('document_search') || (() => { throw new Error('document_search tool not found'); })(), + ToolRegistry.getToolInstance('bookmark_store') || (() => { throw new Error('bookmark_store tool not found'); })(), + ToolRegistry.getToolInstance('research_agent') || (() => { throw new Error('research_agent tool not found'); })(), + new FinalizeWithCritiqueTool(), + new SearchVisitHistoryTool(), + ] + }, [BaseOrchestratorAgentType.DEEP_RESEARCH]: { type: BaseOrchestratorAgentType.DEEP_RESEARCH, icon: '📚', label: 'Deep Research', description: 'In-depth research on a topic', systemPrompt: SYSTEM_PROMPTS[BaseOrchestratorAgentType.DEEP_RESEARCH], + version: '2025-09-17', availableTools: [ ToolRegistry.getToolInstance('research_agent') || (() => { throw new Error('research_agent tool not found'); })(), ToolRegistry.getToolInstance('web_task_agent') || (() => { throw new Error('web_task_agent tool not found'); })(), @@ -307,23 +317,44 @@ export const AGENT_CONFIGS: {[key: string]: AgentConfig} = { new FinalizeWithCritiqueTool(), ] }, - [BaseOrchestratorAgentType.SHOPPING]: { - type: BaseOrchestratorAgentType.SHOPPING, - icon: '🛒', - label: 'Shopping', - description: 'Find products and compare options', - systemPrompt: SYSTEM_PROMPTS[BaseOrchestratorAgentType.SHOPPING], - availableTools: [ - ToolRegistry.getToolInstance('web_task_agent') || (() => { throw new Error('web_task_agent tool not found'); })(), - ToolRegistry.getToolInstance('document_search') || (() => { throw new Error('document_search tool not found'); })(), - ToolRegistry.getToolInstance('bookmark_store') || (() => { throw new Error('bookmark_store tool not found'); })(), - new FinalizeWithCritiqueTool(), - ToolRegistry.getToolInstance('research_agent') || (() => { throw new Error('research_agent tool not found'); })(), - ToolRegistry.getToolInstance('ecommerce_product_info_fetcher_tool') || (() => { throw new Error('ecommerce_product_info_fetcher_tool tool not found'); })(), - ] - } + // [BaseOrchestratorAgentType.SHOPPING]: { + // type: BaseOrchestratorAgentType.SHOPPING, + // icon: '🛒', + // label: 'Shopping', + // description: 'Find products and compare options', + // systemPrompt: SYSTEM_PROMPTS[BaseOrchestratorAgentType.SHOPPING], + // version: '2025-09-17', + // availableTools: [ + // ToolRegistry.getToolInstance('web_task_agent') || (() => { throw new Error('web_task_agent tool not found'); })(), + // ToolRegistry.getToolInstance('document_search') || (() => { throw new Error('document_search tool not found'); })(), + // ToolRegistry.getToolInstance('bookmark_store') || (() => { throw new Error('bookmark_store tool not found'); })(), + // new FinalizeWithCritiqueTool(), + // ToolRegistry.getToolInstance('research_agent') || (() => { throw new Error('research_agent tool not found'); })(), + // ToolRegistry.getToolInstance('ecommerce_product_info_fetcher_tool') || (() => { throw new Error('ecommerce_product_info_fetcher_tool tool not found'); })(), + // ] + // } }; +// Register orchestrator descriptors for version tracking +for (const config of Object.values(AGENT_CONFIGS)) { + AgentDescriptorRegistry.registerSource({ + name: `orchestrator:${config.type}`, + type: config.type, + version: config.version ?? DEFAULT_ORCHESTRATOR_VERSION, + promptProvider: () => config.systemPrompt, + toolNamesProvider: () => config.availableTools.map(tool => tool.name) + }); +} + +// Register a default orchestrator descriptor for the fallback configuration +AgentDescriptorRegistry.registerSource({ + name: 'orchestrator:default', + type: 'default', + version: DEFAULT_ORCHESTRATOR_VERSION, + promptProvider: () => getSystemPrompt(''), + toolNamesProvider: () => getAgentTools('').map(tool => tool.name) +}); + /** * Get the system prompt for a specific agent type */ @@ -355,7 +386,7 @@ You automatically receive rich context with each iteration: - **Never let web_task_agent ask for accessibility trees**: If it reports it cannot extract data, instruct it to try different approach - **Always provide extraction_schema**: For any data extraction task, include a clear schema defining the fields to extract -- **Use proper agent delegation**: Don't try to access web pages directly - always use web_task_agent or research_agent +- **Use proper agent delegation**: Don't try to access web pages directly - always use web_task_agent, search_agent, or research_agent - **Handle extraction failures gracefully**: If initial task fails, try alternative approaches rather than asking users for help ## Task Execution Process @@ -390,7 +421,7 @@ Classify the task type to optimize execution strategy: 3. web_task_agent("Apply to selected jobs on LinkedIn with cover letter") **Information gathering**: Research-focused tasks requiring data collection -- Use research_agent for broad information gathering, web_task_agent for specific site data +- Use search_agent for targeted fact-finding, research_agent for broad information gathering, and web_task_agent for specific site data - Example: "Research renewable energy trends" → research_agent + specific site data from government/industry sites ### 3. Execution Plan Development @@ -408,7 +439,7 @@ Based on task type, develop a specific execution plan: - Identify potential failure points and alternative approaches **For information gathering:** -- Determine if research_agent or web_task_agent is more appropriate +- Determine if search_agent, research_agent, or web_task_agent is more appropriate - Plan authoritative sources and verification methods - Define data collection requirements and output format @@ -416,6 +447,7 @@ Based on task type, develop a specific execution plan: **IMPORTANT**: Always delegate site-specific work to the appropriate specialized agent: - Use 'web_task_agent' for any website interaction, navigation, or data extraction +- Use 'search_agent' for targeted fact-finding (contacts, team rosters, granular attribute checks) - Use 'research_agent' for broad information research across multiple sources - As the orchestrator, focus on: - Planning and strategy @@ -461,6 +493,7 @@ After specialized agents complete their tasks: */ export function getAgentTools(agentType: string): Array> { return AGENT_CONFIGS[agentType]?.availableTools || [ + ToolRegistry.getToolInstance('search_agent') || (() => { throw new Error('search_agent tool not found'); })(), ToolRegistry.getToolInstance('web_task_agent') || (() => { throw new Error('web_task_agent tool not found'); })(), ToolRegistry.getToolInstance('document_search') || (() => { throw new Error('document_search tool not found'); })(), ToolRegistry.getToolInstance('bookmark_store') || (() => { throw new Error('bookmark_store tool not found'); })(), @@ -675,4 +708,4 @@ declare global { [AgentTypeSelectionEvent.eventName]: AgentTypeSelectionEvent; } } - +import { AgentDescriptorRegistry } from './AgentDescriptorRegistry.js'; diff --git a/front_end/panels/ai_chat/core/PageInfoManager.ts b/front_end/panels/ai_chat/core/PageInfoManager.ts index cb08c97dc36..92b8126c658 100644 --- a/front_end/panels/ai_chat/core/PageInfoManager.ts +++ b/front_end/panels/ai_chat/core/PageInfoManager.ts @@ -230,7 +230,7 @@ Instructions: ${iframeContent && iframeContent.length > 0 ? '- The page contains embedded iframes with their own content, which is included above.' : ''} - If the user asks about the page, refer to this context. - If the partial accessibility tree is present, use it to answer questions about visible page structure, elements, or accessibility. -- If you need to extract any data from the entire page, you must always use the extract_schema_data tool to do so. Do not attempt to extract data from the full page by any other means. +- If you need to extract any data from the entire page, you must always use the extract_data tool to do so. Do not attempt to extract data from the full page by any other means. - If information is missing, answer based on what is available, or request the full page accessibility tree if necessary. - Always be concise, accurate, and helpful. diff --git a/front_end/panels/ai_chat/core/State.ts b/front_end/panels/ai_chat/core/State.ts index c60bef44f35..50a38ca465d 100644 --- a/front_end/panels/ai_chat/core/State.ts +++ b/front_end/panels/ai_chat/core/State.ts @@ -5,6 +5,7 @@ import * as i18n from '../../../core/i18n/i18n.js'; import {type ChatMessage, ChatMessageEntity, type ImageInputData} from '../models/ChatTypes.js'; import type {TracingContext} from '../tracing/TracingProvider.js'; +import type { AgentDescriptor } from './AgentDescriptorRegistry.js'; const UIStrings = { } as const; @@ -54,6 +55,11 @@ export interface DevToolsContext { intermediateStepsCount?: number; // Tracing context for distributed tracing tracingContext?: TracingContext; + // Descriptor describing the active agent configuration + agentDescriptor?: AgentDescriptor; + // Execution tracking for cancellation support + executionId?: string; + abortSignal?: AbortSignal; } /** diff --git a/front_end/panels/ai_chat/core/StateGraph.ts b/front_end/panels/ai_chat/core/StateGraph.ts index ebf8e87fb29..6575a17c3d9 100644 --- a/front_end/panels/ai_chat/core/StateGraph.ts +++ b/front_end/panels/ai_chat/core/StateGraph.ts @@ -50,7 +50,7 @@ export class StateGraph { + async *invoke(state: TState, signal?: AbortSignal): AsyncGenerator { logger.debug(`Starting graph execution from entry point: ${this.entryPoint}`); console.warn(`Graph "${this.name}" started with entry point "${this.entryPoint}"`); @@ -59,6 +59,12 @@ export class StateGraph(); function sanitize(original: string): string { let name = original.replace(/[^a-zA-Z0-9_-]/g, '_'); if (!name) name = 'tool'; - if (name.length > 64) name = name.slice(0, 64); + // Removed 64-character truncation to preserve full tool names return name; } @@ -40,15 +40,13 @@ export function addMapping(original: string): string { if (sanitizedToOriginal.has(candidate) && sanitizedToOriginal.get(candidate) !== original) { const suffix = shortHash(original); const base = candidate.replace(/_+$/g, ''); - const maxBase = Math.max(1, 64 - 1 - suffix.length); - candidate = (base.length > maxBase ? base.slice(0, maxBase) : base) + '-' + suffix; + candidate = base + '-' + suffix; } let unique = candidate; let counter = 1; while (sanitizedToOriginal.has(unique) && sanitizedToOriginal.get(unique) !== original) { const add = `_${counter++}`; - const maxBase = Math.max(1, 64 - add.length); - unique = (candidate.length > maxBase ? candidate.slice(0, maxBase) : candidate) + add; + unique = candidate + add; } originalToSanitized.set(original, unique); sanitizedToOriginal.set(unique, original); diff --git a/front_end/panels/ai_chat/core/ToolSurfaceProvider.ts b/front_end/panels/ai_chat/core/ToolSurfaceProvider.ts index f16b6903ffa..f1611366b12 100644 --- a/front_end/panels/ai_chat/core/ToolSurfaceProvider.ts +++ b/front_end/panels/ai_chat/core/ToolSurfaceProvider.ts @@ -9,6 +9,8 @@ import { ToolRegistry } from '../agent_framework/ConfigurableAgentTool.js'; import { MCPRegistry } from '../mcp/MCPRegistry.js'; import { getMCPConfig } from '../mcp/MCPConfig.js'; import { MCPToolAdapter } from '../mcp/MCPToolAdapter.js'; +import { LLMClient } from '../LLM/LLMClient.js'; +import { AIChatPanel } from '../ui/AIChatPanel.js'; const logger = createLogger('ToolSurfaceProvider'); @@ -29,8 +31,10 @@ function uniqByName(tools: Tool[]): Tool[] { return out; } -function getAllMcpTools(): Tool[] { +async function getAllMcpTools(): Promise[]> { try { + // Ensure tools are registered before getting status + await MCPRegistry.ensureToolsRegistered(); const status = MCPRegistry.getStatus(); console.log('[TOOL_SELECTION_DEBUG] MCPRegistry status:', { enabled: status.enabled, @@ -61,21 +65,116 @@ function getAllMcpTools(): Tool[] { } } -function scoreTool(query: string, agentType: string | null | undefined, tool: Tool): number { - const q = (query || '').toLowerCase(); - const a = (agentType || '').toLowerCase(); - const name = (tool.name || '').toLowerCase(); - const desc = (tool.description || '').toLowerCase(); - let score = 0; - if (q && name.includes(q)) score += 10; - if (q && desc.includes(q)) score += 3; - if (a && name.includes(a)) score += 2; - if (a && desc.includes(a)) score += 1; - // Prefer MCP tools only slightly lower than strong name matches - if (tool instanceof MCPToolAdapter) score += 0.5; - return score; +async function selectToolsWithLLM( + query: string, + agentType: string | null | undefined, + mcpTools: Tool[], + maxMcpPerTurn: number +): Promise[]> { + try { + // Early return for empty tool list - avoid unnecessary LLM call + if (mcpTools.length === 0) { + console.log('[TOOL_SELECTION_DEBUG] No MCP tools provided to LLM selector'); + return []; + } + + const miniModel = AIChatPanel.getMiniModel(); + const miniProvider = AIChatPanel.getMiniModelWithProvider(); + if (!miniModel || !miniProvider) { + console.log('[TOOL_SELECTION_DEBUG] Mini model not available, falling back to first N tools'); + return mcpTools.slice(0, maxMcpPerTurn); + } + + const toolDescriptions = mcpTools.map(tool => + `- ${tool.name}: ${tool.description}` + ).join('\n'); + + const systemPrompt = `You are an intelligent tool selector. Given a user query and agent type, select the most relevant MCP tools. + +Select up to ${maxMcpPerTurn} most relevant tools for this query. Consider: +1. Direct keyword matches between query and tool names/descriptions +2. Semantic relevance to the task +3. Agent type preferences (e.g., research agents prefer search/analysis tools) + +CRITICAL RULES: +- You MUST ONLY select from the exact tool names provided in the "Available MCP tools" list +- Do NOT invent, create, or hallucinate any tool names +- Return ONLY the exact tool names as they appear in the list +- Respond with a JSON array containing only the selected tool names + +Response format: JSON array of exact tool names from the provided list.`; + + const userMessage = `User query: "${query}" +Agent type: ${agentType || 'general'} + +Available MCP tools: +${toolDescriptions}`; + + console.log('[TOOL_SELECTION_DEBUG] LLM prompt:', userMessage); + + const llmClient = LLMClient.getInstance(); + const response = await llmClient.call({ + provider: miniProvider.provider, + model: miniModel, + messages: [{ role: 'user', content: userMessage }], + systemPrompt, + temperature: 0.1 + }); + + console.log('[TOOL_SELECTION_DEBUG] LLM response:', response.text); + + // Parse the JSON response + let selectedToolNames: string[]; + try { + // Check if response.text is defined + if (!response.text) { + throw new Error('LLM response text is undefined'); + } + + // Try to extract JSON from the response + const jsonMatch = response.text.match(/\[[\s\S]*?\]/); + if (jsonMatch) { + selectedToolNames = JSON.parse(jsonMatch[0]); + } else { + throw new Error('No JSON array found in response'); + } + } catch (parseError) { + console.log('[TOOL_SELECTION_DEBUG] Failed to parse LLM response, falling back to first N tools:', parseError); + return mcpTools.slice(0, maxMcpPerTurn); + } + + // Map selected tool names back to tool objects + const selectedTools: Tool[] = []; + const toolMap = new Map(mcpTools.map(tool => [tool.name, tool])); + + for (const toolName of selectedToolNames) { + const tool = toolMap.get(toolName); + if (tool && selectedTools.length < maxMcpPerTurn) { + selectedTools.push(tool); + } + } + + // Fill remaining slots if LLM didn't select enough tools + if (selectedTools.length < maxMcpPerTurn) { + const remainingTools = mcpTools.filter(tool => !selectedTools.includes(tool)); + selectedTools.push(...remainingTools.slice(0, maxMcpPerTurn - selectedTools.length)); + } + + console.log('[TOOL_SELECTION_DEBUG] LLM selected tools:', { + selectedToolNames, + selectedCount: selectedTools.length, + finalToolNames: selectedTools.map(t => t.name) + }); + + return selectedTools; + + } catch (error) { + console.log('[TOOL_SELECTION_DEBUG] Error in LLM tool selection:', error); + return mcpTools.slice(0, maxMcpPerTurn); + } } + // DEBUG: Add a utility function to test MCP modes from console (globalThis as any).debugToolSelection = { getCurrentMCPConfig: () => { @@ -111,9 +210,9 @@ function scoreTool(query: string, agentType: string | null | undefined, tool: To export const ToolSurfaceProvider = { async select(state: AgentState, baseTools: Tool[], opts?: ToolSelectionOptions): Promise<{ tools: Tool[]; selectedNames: string[] }> { - const { maxToolsPerTurn = 20, maxMcpPerTurn = 8 } = opts || {}; const cfg = getMCPConfig(); - const mode = cfg.toolMode || 'router'; + const { maxToolsPerTurn = cfg.maxToolsPerTurn || 50, maxMcpPerTurn = cfg.maxMcpPerTurn || 50 } = opts || {}; + const mode = cfg.toolMode || 'all'; // DEBUG: Log current MCP configuration and tool selection parameters console.log('[TOOL_SELECTION_DEBUG] ToolSurfaceProvider.select called with:', { @@ -148,12 +247,12 @@ export const ToolSurfaceProvider = { if (mode === 'all') { console.log('[TOOL_SELECTION_DEBUG] Using ALL mode'); - const mcpTools = getAllMcpTools(); + const mcpTools = await getAllMcpTools(); console.log('[TOOL_SELECTION_DEBUG] MCP tools found:', { mcpToolsCount: mcpTools.length, mcpToolNames: mcpTools.map(t => t.name) }); - resultTools = uniqByName([...resultTools, ...mcpTools]).slice(0, maxToolsPerTurn); + resultTools = uniqByName([...resultTools, ...mcpTools]); console.log('[TOOL_SELECTION_DEBUG] Final result (ALL mode):', { toolCount: resultTools.length, toolNames: resultTools.map(t => t.name) @@ -181,31 +280,38 @@ export const ToolSurfaceProvider = { return { tools: resultTools, selectedNames: resultTools.map(t => t.name) }; } - // Router mode (heuristic pre-call selection) - console.log('[TOOL_SELECTION_DEBUG] Using ROUTER mode'); - const mcpTools = getAllMcpTools(); - console.log('[TOOL_SELECTION_DEBUG] MCP tools available for scoring:', { + // Router mode (LLM-based intelligent selection) + console.log('[TOOL_SELECTION_DEBUG] Using ROUTER mode with LLM selection'); + const mcpTools = await getAllMcpTools(); + console.log('[TOOL_SELECTION_DEBUG] MCP tools available for LLM selection:', { mcpToolsCount: mcpTools.length, mcpToolNames: mcpTools.map(t => t.name) }); - + + // Early return if no MCP tools available - avoid unnecessary LLM call + if (mcpTools.length === 0) { + console.log('[TOOL_SELECTION_DEBUG] No MCP tools available, skipping LLM selection'); + console.log('[TOOL_SELECTION_DEBUG] Final result (ROUTER mode - no MCP tools):', { + toolCount: resultTools.length, + toolNames: resultTools.map(t => t.name), + maxToolsPerTurn + }); + return { tools: resultTools, selectedNames: resultTools.map(t => t.name) }; + } + const lastUserMsg = [...state.messages].reverse().find(m => m.entity === 'user' || (m as any).entity === 0) as any; const queryText = lastUserMsg?.text || ''; - console.log('[TOOL_SELECTION_DEBUG] Query text for scoring:', queryText); + console.log('[TOOL_SELECTION_DEBUG] Query text for LLM selection:', queryText); - const scored = mcpTools - .map(t => ({ t, s: scoreTool(queryText, state.selectedAgentType, t) })) - .sort((a, b) => b.s - a.s) - .slice(0, maxMcpPerTurn) - .map(({ t }) => t); + const selectedMcpTools = await selectToolsWithLLM(queryText, state.selectedAgentType, mcpTools, maxMcpPerTurn); - console.log('[TOOL_SELECTION_DEBUG] Top scored MCP tools:', { - scoredToolsCount: scored.length, - scoredToolNames: scored.map(t => t.name), + console.log('[TOOL_SELECTION_DEBUG] LLM selected MCP tools:', { + selectedToolsCount: selectedMcpTools.length, + selectedToolNames: selectedMcpTools.map(t => t.name), maxMcpPerTurn }); - resultTools = uniqByName([...resultTools, ...scored]).slice(0, maxToolsPerTurn); + resultTools = uniqByName([...resultTools, ...selectedMcpTools]).slice(0, maxToolsPerTurn); console.log('[TOOL_SELECTION_DEBUG] Final result (ROUTER mode):', { toolCount: resultTools.length, toolNames: resultTools.map(t => t.name), diff --git a/front_end/panels/ai_chat/core/Types.ts b/front_end/panels/ai_chat/core/Types.ts index e7c85906d0c..d508422a4c2 100644 --- a/front_end/panels/ai_chat/core/Types.ts +++ b/front_end/panels/ai_chat/core/Types.ts @@ -24,6 +24,6 @@ export enum NodeType { * Interface for the compiled orchestrator agent */ export interface CompiledGraph { - invoke(state: AgentState): AsyncGenerator; + invoke(state: AgentState, signal?: AbortSignal): AsyncGenerator; onStep?: (stepData: {text: string, type: string}) => void; } diff --git a/front_end/panels/ai_chat/core/Version.ts b/front_end/panels/ai_chat/core/Version.ts index d2bcc5f2142..fae99706c54 100644 --- a/front_end/panels/ai_chat/core/Version.ts +++ b/front_end/panels/ai_chat/core/Version.ts @@ -3,8 +3,8 @@ // found in the LICENSE file. export const VERSION_INFO = { - version: '0.3.1', - buildDate: '2025-08-07', + version: '0.3.4', + buildDate: '2025-09-19', channel: 'stable' } as const; diff --git a/front_end/panels/ai_chat/core/AgentNodes.test.ts b/front_end/panels/ai_chat/core/__tests__/AgentNodes.test.ts similarity index 79% rename from front_end/panels/ai_chat/core/AgentNodes.test.ts rename to front_end/panels/ai_chat/core/__tests__/AgentNodes.test.ts index 0cfc2cdf83c..0b9b23d769d 100644 --- a/front_end/panels/ai_chat/core/AgentNodes.test.ts +++ b/front_end/panels/ai_chat/core/__tests__/AgentNodes.test.ts @@ -2,25 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { createToolExecutorNode } from './AgentNodes.js'; -import { ConfigurableAgentTool } from '../agent_framework/ConfigurableAgentTool.js'; -import { ChatMessageEntity } from '../ui/ChatView.js'; -import type { AgentState } from './State.js'; -import type { ConfigurableAgentResult } from '../agent_framework/ConfigurableAgentTool.js'; - -declare global { - function describe(name: string, fn: () => void): void; - function it(name: string, fn: () => void): void; - function beforeEach(fn: () => void): void; - function afterEach(fn: () => void): void; - namespace assert { - function strictEqual(actual: unknown, expected: unknown): void; - function deepEqual(actual: unknown, expected: unknown): void; - function isTrue(value: unknown): void; - function isFalse(value: unknown): void; - function doesNotMatch(actual: string, regexp: RegExp): void; - } -} +import { createToolExecutorNode } from '../AgentNodes.js'; +import { ConfigurableAgentTool } from '../../agent_framework/ConfigurableAgentTool.js'; +import { ChatMessageEntity } from '../../models/ChatTypes.js'; +import type { AgentState } from '../State.js'; +import type { ConfigurableAgentResult } from '../../agent_framework/ConfigurableAgentTool.js'; + describe('AgentNodes ToolExecutorNode', () => { describe('ConfigurableAgentTool result filtering', () => { @@ -66,7 +53,7 @@ describe('AgentNodes ToolExecutorNode', () => { }); } - async execute(): Promise { + override async execute(): Promise { return errorResultWithSession; } } @@ -85,7 +72,6 @@ describe('AgentNodes ToolExecutorNode', () => { isFinalAnswer: false } ], - agentType: 'web_task', context: {} }; @@ -97,7 +83,8 @@ describe('AgentNodes ToolExecutorNode', () => { }; // Create ToolExecutorNode - const toolExecutorNode = createToolExecutorNode(stateWithMockTool); + const mockProvider = { name: 'test-provider' } as any; + const toolExecutorNode = createToolExecutorNode(stateWithMockTool, mockProvider, 'test-model'); // Execute the node const result = await toolExecutorNode.invoke(stateWithMockTool); @@ -113,11 +100,11 @@ describe('AgentNodes ToolExecutorNode', () => { assert.strictEqual(resultText, 'Error: Agent reached maximum iterations'); // Verify that the resultText does not contain session data - assert.doesNotMatch(resultText, /sessionId/); - assert.doesNotMatch(resultText, /test-session-123/); - assert.doesNotMatch(resultText, /intermediateSteps/); - assert.doesNotMatch(resultText, /agentSession/); - assert.doesNotMatch(resultText, /nestedSessions/); + assert.notMatch(resultText, /sessionId/); + assert.notMatch(resultText, /test-session-123/); + assert.notMatch(resultText, /intermediateSteps/); + assert.notMatch(resultText, /agentSession/); + assert.notMatch(resultText, /nestedSessions/); // The resultText should be clean and only contain the error message assert.strictEqual(resultText.includes('test-session-123'), false); @@ -159,7 +146,7 @@ describe('AgentNodes ToolExecutorNode', () => { }); } - async execute(): Promise { + override async execute(): Promise { return successResultWithSession; } } @@ -177,7 +164,6 @@ describe('AgentNodes ToolExecutorNode', () => { isFinalAnswer: false } ], - agentType: 'web_task', context: {} }; @@ -186,7 +172,8 @@ describe('AgentNodes ToolExecutorNode', () => { tools: [mockTool] }; - const toolExecutorNode = createToolExecutorNode(stateWithMockTool); + const mockProvider = { name: 'test-provider' } as any; + const toolExecutorNode = createToolExecutorNode(stateWithMockTool, mockProvider, 'test-model'); const result = await toolExecutorNode.invoke(stateWithMockTool); const toolResultMessage = result.messages[result.messages.length - 1]; @@ -196,9 +183,9 @@ describe('AgentNodes ToolExecutorNode', () => { assert.strictEqual(resultText, 'Task completed successfully'); // Should NOT contain session data - assert.doesNotMatch(resultText, /success-session-456/); - assert.doesNotMatch(resultText, /intermediateSteps/); - assert.doesNotMatch(resultText, /agentSession/); + assert.notMatch(resultText, /success-session-456/); + assert.notMatch(resultText, /intermediateSteps/); + assert.notMatch(resultText, /agentSession/); }); }); }); \ No newline at end of file diff --git a/front_end/panels/ai_chat/core/__tests__/ToolExecutorNode.test.ts b/front_end/panels/ai_chat/core/__tests__/ToolExecutorNode.test.ts new file mode 100644 index 00000000000..d73bc281738 --- /dev/null +++ b/front_end/panels/ai_chat/core/__tests__/ToolExecutorNode.test.ts @@ -0,0 +1,278 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import { createToolExecutorNode } from '../AgentNodes.js'; +import type { AgentState } from '../State.js'; +import { ChatMessageEntity } from '../../models/ChatTypes.js'; +import * as ToolNameMap from '../ToolNameMap.js'; +import { ToolRegistry } from '../../agent_framework/ConfigurableAgentTool.js'; + +/* eslint-env mocha */ + +// Mock tool class +class MockTool { + constructor(public name: string, public description: string = `Mock tool ${name}`) {} + + get schema() { + return { + type: 'object', + properties: {}, + }; + } + + async execute(_args: Record): Promise { + return `Executed ${this.name}`; + } +} + +describe('ToolExecutorNode Resolution', () => { + beforeEach(() => { + ToolNameMap.clear(); + }); + + afterEach(() => { + ToolNameMap.clear(); + }); + + describe('MCP tool resolution', () => { + it('should resolve MCP tools by different name variants', () => { + // Setup: simulate an MCP tool registered with smart naming + const serverId = 'mcp-lusl1248if'; + const toolName = 'search'; + const namespacedName = `mcp:${serverId}:${toolName}`; + const smartName = toolName; + + console.log('Test 1 - MCP tool resolution variants:'); + console.log(' Namespaced name:', namespacedName); + console.log(' Smart name:', smartName); + + // Add mappings like MCPRegistry would + ToolNameMap.addMapping(namespacedName); + ToolNameMap.addMapping(smartName); + + // Register with smart name (this is what our updated MCPRegistry does) + const mockTool = new MockTool(smartName); + ToolRegistry.registerToolFactory(smartName, () => mockTool); + + // Create state with the tool selected + const state: AgentState = { + messages: [ + { + entity: ChatMessageEntity.USER, + text: 'Test user message', + }, + { + entity: ChatMessageEntity.MODEL, + action: 'tool', + toolName: namespacedName, // This is what comes from the LLM message + toolArgs: {}, + toolCallId: 'test-call-id', + isFinalAnswer: false, + } + ], + selectedAgentType: 'test', + context: { + selectedToolNames: [smartName], // This is what MCPRegistry puts in context + } as any, + }; + + // Create ToolExecutorNode + const toolExecutor = createToolExecutorNode(state, 'openai', 'gpt-4'); + + console.log(' State setup complete'); + console.log(' Tool message name:', namespacedName); + console.log(' Selected tool names:', (state.context as any)?.selectedToolNames); + + // Test that the tool executor can find the tool + // This should work with our enhanced resolution logic + return toolExecutor.invoke(state).then(result => { + console.log(' Resolution successful!'); + console.log(' Result messages:', result.messages.length); + + // Should have added a tool result message + assert.isTrue(result.messages.length > state.messages.length, 'Should add tool result message'); + + const lastMessage = result.messages[result.messages.length - 1]; + assert.strictEqual(lastMessage.entity, ChatMessageEntity.TOOL_RESULT, 'Last message should be tool result'); + }).catch(error => { + console.log(' Resolution failed with error:', error.message); + throw error; + }); + }); + + it('should test the exact error scenario from logs', () => { + // Recreate the exact scenario from the error logs + const requestedTool = 'mcp:mcp-lusl1248if:search'; + const smartName = 'search'; + + console.log('Test 2 - Exact error scenario:'); + console.log(' Requested tool (from error):', requestedTool); + console.log(' Smart name (how it should be registered):', smartName); + + // Register tool with smart name (correct way) + const mockTool = new MockTool(smartName); + ToolRegistry.registerToolFactory(smartName, () => mockTool); + + // Add mappings + ToolNameMap.addMapping(requestedTool); + ToolNameMap.addMapping(smartName); + + // Create state that requests the namespaced name + const state: AgentState = { + messages: [ + { + entity: ChatMessageEntity.USER, + text: 'Test search', + }, + { + entity: ChatMessageEntity.MODEL, + action: 'tool', + toolName: requestedTool, // Requesting namespaced name + toolArgs: {}, + toolCallId: 'test-call-id', + isFinalAnswer: false, + } + ], + selectedAgentType: 'test', + context: { + selectedToolNames: [smartName], // Only smart name in selection + } as any, + }; + + console.log(' Creating ToolExecutorNode...'); + const toolExecutor = createToolExecutorNode(state, 'openai', 'gpt-4'); + + console.log(' Testing resolution...'); + + // This should work with our enhanced fuzzy matching + return toolExecutor.invoke(state).then(result => { + console.log(' SUCCESS: Tool resolved successfully!'); + assert.isTrue(result.messages.length > state.messages.length); + + const toolResult = result.messages[result.messages.length - 1]; + assert.strictEqual(toolResult.entity, ChatMessageEntity.TOOL_RESULT); + + console.log(' Tool result:', (toolResult as any).resultText); + }).catch(error => { + console.log(' FAILED: Tool resolution failed:', error.message); + + // This tells us exactly what the issue is + if (error.message.includes('not found')) { + console.log(' Diagnosis: Tool name mismatch between registration and request'); + console.log(' - Tool registered as:', smartName); + console.log(' - Tool requested as:', requestedTool); + console.log(' - Need better name mapping or registration strategy'); + } + + throw error; + }); + }); + + it('should test fuzzy matching for MCP tools', () => { + const originalName = 'mcp:mcp-test123:notion_create_page'; + const smartName = 'notion_create_page'; + + console.log('Test 3 - Fuzzy matching:'); + console.log(' Original name:', originalName); + console.log(' Smart name:', smartName); + + // Register with smart name + const mockTool = new MockTool(smartName); + ToolRegistry.registerToolFactory(smartName, () => mockTool); + + // Add mappings + ToolNameMap.addMapping(originalName); + ToolNameMap.addMapping(smartName); + + // Try to request with sanitized version + const sanitizedName = ToolNameMap.getSanitized(originalName); + console.log(' Sanitized name:', sanitizedName); + + const state: AgentState = { + messages: [ + { + entity: ChatMessageEntity.USER, + text: 'Test create page', + }, + { + entity: ChatMessageEntity.MODEL, + action: 'tool', + toolName: sanitizedName, // Request sanitized version + toolArgs: {}, + toolCallId: 'test-call-id', + isFinalAnswer: false, + } + ], + selectedAgentType: 'test', + context: { + selectedToolNames: [smartName], + } as any, + }; + + const toolExecutor = createToolExecutorNode(state, 'openai', 'gpt-4'); + + return toolExecutor.invoke(state).then(result => { + console.log(' Fuzzy matching SUCCESS!'); + assert.isTrue(result.messages.length > state.messages.length); + }).catch(error => { + console.log(' Fuzzy matching FAILED:', error.message); + throw error; + }); + }); + }); + + describe('Tool map construction', () => { + it('should verify toolMap includes all name variants', () => { + const originalName = 'mcp:server:tool'; + const smartName = 'tool'; + + console.log('Test 4 - Tool map construction:'); + + // Register tool + const mockTool = new MockTool(smartName); + ToolRegistry.registerToolFactory(smartName, () => mockTool); + + // Add mappings + ToolNameMap.addMapping(originalName); + ToolNameMap.addMapping(smartName); + + const state: AgentState = { + messages: [], + selectedAgentType: 'test', + context: { + selectedToolNames: [smartName], + } as any, + }; + + // Create tool executor to see what gets added to toolMap + const toolExecutor = createToolExecutorNode(state, 'openai', 'gpt-4'); + + // The debug logs we added should show what's in the toolMap + console.log(' Tool executor created - check debug logs for toolMap contents'); + + // Test that it can handle the tool request + const testState: AgentState = { + ...state, + messages: [ + { + entity: ChatMessageEntity.MODEL, + action: 'tool', + toolName: originalName, + toolArgs: {}, + toolCallId: 'test', + isFinalAnswer: false, + } + ], + }; + + return toolExecutor.invoke(testState).then(result => { + console.log(' Tool map construction test PASSED'); + assert.isTrue(result.messages.length > 0); + }).catch(error => { + console.log(' Tool map construction test FAILED:', error.message); + throw error; + }); + }); + }); +}); \ No newline at end of file diff --git a/front_end/panels/ai_chat/core/__tests__/ToolNameMap.test.ts b/front_end/panels/ai_chat/core/__tests__/ToolNameMap.test.ts new file mode 100644 index 00000000000..aa7db412269 --- /dev/null +++ b/front_end/panels/ai_chat/core/__tests__/ToolNameMap.test.ts @@ -0,0 +1,142 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import * as ToolNameMap from '../ToolNameMap.js'; + +/* eslint-env mocha */ + +describe('ToolNameMap', () => { + beforeEach(() => { + ToolNameMap.clear(); + }); + + afterEach(() => { + ToolNameMap.clear(); + }); + + describe('MCP tool name handling', () => { + it('should handle MCP tool names with colons and hyphens', () => { + const original = 'mcp:mcp-lusl1248if:search'; + const sanitized = ToolNameMap.getSanitized(original); + + console.log('Test 1 - Basic MCP name:'); + console.log(' Original:', original); + console.log(' Sanitized:', sanitized); + console.log(' Expected: mcp_mcp-lusl1248if_search'); + + // Should replace colons with underscores but keep hyphens + assert.strictEqual(sanitized, 'mcp_mcp-lusl1248if_search'); + + // Should be able to resolve back + const resolved = ToolNameMap.resolveOriginal(sanitized); + console.log(' Resolved back:', resolved); + assert.strictEqual(resolved, original); + }); + + it('should handle long MCP tool names without truncation', () => { + const original = 'mcp:80a92942-4bf9-4c02-ab11-422151bec3a2:notion_find_page_by_title'; + const sanitized = ToolNameMap.getSanitized(original); + + console.log('Test 2 - Long MCP name:'); + console.log(' Original:', original); + console.log(' Sanitized:', sanitized); + console.log(' Length:', sanitized.length); + + // Should not be truncated (our fix removed 64-char limit) + const expected = 'mcp_80a92942-4bf9-4c02-ab11-422151bec3a2_notion_find_page_by_title'; + assert.strictEqual(sanitized, expected); + assert.isTrue(sanitized.length > 64, 'Should not be truncated at 64 characters'); + + // Should be able to resolve back + const resolved = ToolNameMap.resolveOriginal(sanitized); + console.log(' Resolved back:', resolved); + assert.strictEqual(resolved, original); + }); + + it('should handle conflicts correctly', () => { + const original1 = 'mcp:server1:search'; + const original2 = 'mcp:server2:search'; + + const sanitized1 = ToolNameMap.getSanitized(original1); + const sanitized2 = ToolNameMap.getSanitized(original2); + + console.log('Test 3 - Conflicts:'); + console.log(' Original1:', original1, '-> Sanitized1:', sanitized1); + console.log(' Original2:', original2, '-> Sanitized2:', sanitized2); + + // Both should get unique sanitized names + assert.notStrictEqual(sanitized1, sanitized2); + + // Both should resolve back correctly + assert.strictEqual(ToolNameMap.resolveOriginal(sanitized1), original1); + assert.strictEqual(ToolNameMap.resolveOriginal(sanitized2), original2); + }); + + it('should handle smart tool names (without server IDs)', () => { + const smartName = 'search'; + const sanitized = ToolNameMap.getSanitized(smartName); + + console.log('Test 4 - Smart name:'); + console.log(' Original:', smartName); + console.log(' Sanitized:', sanitized); + + // Simple names should remain unchanged + assert.strictEqual(sanitized, smartName); + + // Should resolve back to itself + const resolved = ToolNameMap.resolveOriginal(sanitized); + console.log(' Resolved back:', resolved); + assert.strictEqual(resolved, smartName); + }); + + it('should bidirectionally map both namespaced and smart names', () => { + const namespacedName = 'mcp:mcp-lusl1248if:search'; + const smartName = 'search'; + + // Add both mappings + const sanitizedNamespaced = ToolNameMap.getSanitized(namespacedName); + const sanitizedSmart = ToolNameMap.getSanitized(smartName); + + console.log('Test 5 - Bidirectional mapping:'); + console.log(' Namespaced:', namespacedName, '-> Sanitized:', sanitizedNamespaced); + console.log(' Smart:', smartName, '-> Sanitized:', sanitizedSmart); + + // Both should be resolvable + assert.strictEqual(ToolNameMap.resolveOriginal(sanitizedNamespaced), namespacedName); + assert.strictEqual(ToolNameMap.resolveOriginal(sanitizedSmart), smartName); + }); + }); + + describe('Edge cases', () => { + it('should handle special characters correctly', () => { + const original = 'mcp:test-server_123:tool@name.ext'; + const sanitized = ToolNameMap.getSanitized(original); + + console.log('Test 6 - Special characters:'); + console.log(' Original:', original); + console.log(' Sanitized:', sanitized); + + // Should replace non-alphanumeric characters except hyphens and underscores + const expected = 'mcp_test-server_123_tool_name_ext'; + assert.strictEqual(sanitized, expected); + + // Should resolve back + assert.strictEqual(ToolNameMap.resolveOriginal(sanitized), original); + }); + + it('should handle empty and invalid names', () => { + console.log('Test 7 - Edge cases:'); + + const empty = ''; + const sanitizedEmpty = ToolNameMap.getSanitized(empty); + console.log(' Empty string -> Sanitized:', sanitizedEmpty); + assert.strictEqual(sanitizedEmpty, 'tool'); + + const onlySpecialChars = '@#$%^&*()'; + const sanitizedSpecial = ToolNameMap.getSanitized(onlySpecialChars); + console.log(' Special chars only -> Sanitized:', sanitizedSpecial); + assert.strictEqual(sanitizedSpecial, '_________'); // 9 underscores for 9 special chars + }); + }); +}); \ No newline at end of file diff --git a/front_end/panels/ai_chat/core/ToolNameMapping.test.ts b/front_end/panels/ai_chat/core/__tests__/ToolNameMapping.test.ts similarity index 87% rename from front_end/panels/ai_chat/core/ToolNameMapping.test.ts rename to front_end/panels/ai_chat/core/__tests__/ToolNameMapping.test.ts index 7b835d16ce7..09b02d3756d 100644 --- a/front_end/panels/ai_chat/core/ToolNameMapping.test.ts +++ b/front_end/panels/ai_chat/core/__tests__/ToolNameMapping.test.ts @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { createAgentNode, createToolExecutorNode } from './AgentNodes.js'; -import type { AgentState } from './State.js'; -import type { Tool } from '../tools/Tools.js'; -import { ChatMessageEntity } from '../models/ChatTypes.js'; -import { ToolSurfaceProvider } from './ToolSurfaceProvider.js'; -import '../agent_framework/ConfigurableAgentTool.js'; -import { LLMClient } from '../LLM/LLMClient.js'; +import { createAgentNode, createToolExecutorNode } from '../AgentNodes.js'; +import type { AgentState } from '../State.js'; +import type { Tool } from '../../tools/Tools.js'; +import { ChatMessageEntity } from '../../models/ChatTypes.js'; +import { ToolSurfaceProvider } from '../ToolSurfaceProvider.js'; +import '../../agent_framework/ConfigurableAgentTool.js'; +import { LLMClient } from '../../LLM/LLMClient.js'; /* eslint-env mocha */ diff --git a/front_end/panels/ai_chat/core/ToolSurfaceProvider.test.ts b/front_end/panels/ai_chat/core/__tests__/ToolSurfaceProvider.test.ts similarity index 93% rename from front_end/panels/ai_chat/core/ToolSurfaceProvider.test.ts rename to front_end/panels/ai_chat/core/__tests__/ToolSurfaceProvider.test.ts index 6b6d7c90a45..d7143e78513 100644 --- a/front_end/panels/ai_chat/core/ToolSurfaceProvider.test.ts +++ b/front_end/panels/ai_chat/core/__tests__/ToolSurfaceProvider.test.ts @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { ToolSurfaceProvider } from './ToolSurfaceProvider.js'; -import type { AgentState } from './State.js'; -import type { Tool } from '../tools/Tools.js'; -import { ToolRegistry } from '../agent_framework/ConfigurableAgentTool.js'; -import { MCPRegistry } from '../mcp/MCPRegistry.js'; -import { registerMCPMetaTools } from '../mcp/MCPMetaTools.js'; +import { ToolSurfaceProvider } from '../ToolSurfaceProvider.js'; +import type { AgentState } from '../State.js'; +import type { Tool } from '../../tools/Tools.js'; +import { ToolRegistry } from '../../agent_framework/ConfigurableAgentTool.js'; +import { MCPRegistry } from '../../mcp/MCPRegistry.js'; +import { registerMCPMetaTools } from '../../mcp/MCPMetaTools.js'; /* eslint-env mocha */ diff --git a/front_end/panels/ai_chat/docs/MCP_OAuth_Implementation_Plan.md b/front_end/panels/ai_chat/docs/MCP_OAuth_Implementation_Plan.md new file mode 100644 index 00000000000..187f4f154fc --- /dev/null +++ b/front_end/panels/ai_chat/docs/MCP_OAuth_Implementation_Plan.md @@ -0,0 +1,333 @@ +# MCP OAuth Implementation Plan + +## Overview + +This document outlines the implementation plan for adding OAuth 2.0 authentication support to the DevTools MCP (Model Context Protocol) client. This will enable secure connections to MCP servers like Zapier without requiring users to manually manage API keys. + +## Background + +### Current MCP Implementation +- **MCPClientSDK**: Wrapper around the MCP SDK with basic bearer token auth +- **MCPRegistry**: Manages server connections and tool registration +- **MCPConfig**: Configuration storage using localStorage/sessionStorage +- **SettingsDialog**: UI for MCP configuration (currently hidden) + +### OAuth URL Context +When services like Zapier provide an "OAuth Server URL", they're offering a standardized OAuth 2.0 endpoint for MCP client authentication. The URL format typically follows: +``` +https://nla.zapier.com/oauth/mcp/authorize?client_id= +``` + +## Architecture Overview + +``` +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ SettingsDialog │────│ MCPOAuthFlow │────│ MCPOAuthProvider│ +│ (UI) │ │ (Orchestrator) │ │ (Storage) │ +└─────────────────┘ └──────────────────┘ └─────────────────┘ + │ │ │ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ MCPConfig │ │ MCPClientSDK │ │ MCP SDK OAuth │ +│ (Config) │ │ (Connection) │ │ (Protocol) │ +└─────────────────┘ └──────────────────┘ └─────────────────┘ +``` + +## Implementation Components + +### 1. MCPOAuthProvider (`mcp/MCPOAuthProvider.ts`) + +**Purpose**: Implements the `OAuthClientProvider` interface from the MCP SDK for browser environments. + +**Key Methods**: +```typescript +interface MCPOAuthProvider extends OAuthClientProvider { + // Required by MCP SDK + get redirectUrl(): string; + get clientMetadata(): OAuthClientMetadata; + clientInformation(): OAuthClientInformation | undefined; + saveClientInformation(info: OAuthClientInformationFull): void; + tokens(): OAuthTokens | undefined; + saveTokens(tokens: OAuthTokens): void; + redirectToAuthorization(url: URL): void; + saveCodeVerifier(verifier: string): void; + codeVerifier(): string; + + // Custom methods for browser environment + initializeForServer(serverUrl: string): Promise; + handleAuthCallback(authCode: string): Promise; + clearCredentials(): void; +} +``` + +**Storage Strategy**: +- **sessionStorage**: OAuth tokens (cleared on tab close) +- **localStorage**: Client metadata, configuration +- **In-memory**: Code verifiers (security best practice) + +**Security Features**: +- PKCE (Proof Key for Code Exchange) for all flows +- State parameter validation for CSRF protection +- Automatic token refresh before expiry +- Secure token storage patterns + +### 2. MCPOAuthFlow (`mcp/MCPOAuthFlow.ts`) + +**Purpose**: Orchestrates the complete OAuth flow in the browser environment. + +**Key Methods**: +```typescript +class MCPOAuthFlow { + async startOAuthFlow(serverUrl: string, clientMetadata: OAuthClientMetadata): Promise; + async handlePopupCallback(popup: Window): Promise; // auth code + async completeTokenExchange(authCode: string, provider: MCPOAuthProvider): Promise; + async refreshTokens(provider: MCPOAuthProvider): Promise; +} + +interface OAuthFlowResult { + success: boolean; + tokens?: OAuthTokens; + error?: string; +} +``` + +**Flow Strategy**: +1. **Popup-based flow** (preferred for DevTools) + - Opens OAuth provider in popup window + - Monitors popup for redirect with auth code + - Automatically closes popup on completion + +2. **Fallback options**: + - Redirect flow (if popup blocked) + - Manual auth code entry + +### 3. Extended MCPConfig (`mcp/MCPConfig.ts`) + +**New Configuration Fields**: +```typescript +interface MCPConfigData { + // Existing fields... + enabled: boolean; + endpoint?: string; + token?: string; + + // New OAuth fields + authType: 'bearer' | 'oauth'; + oauthServerUrl?: string; + oauthClientId?: string; + oauthScope?: string; + oauthRedirectUrl?: string; + + // Runtime OAuth state (not persisted) + oauthTokens?: OAuthTokens; + oauthClientInfo?: OAuthClientInformation; +} +``` + +**Storage Mapping**: +```typescript +const OAUTH_KEYS = { + authType: 'ai_chat_mcp_auth_type', + oauthServerUrl: 'ai_chat_mcp_oauth_server_url', + oauthClientId: 'ai_chat_mcp_oauth_client_id', + oauthScope: 'ai_chat_mcp_oauth_scope', + oauthRedirectUrl: 'ai_chat_mcp_oauth_redirect_url', +} as const; +``` + +### 4. Updated MCPClientSDK (`third_party/mcp-sdk/mcp-sdk.ts`) + +**Enhanced Connection Logic**: +```typescript +class MCPClientSDK { + async connectWithOAuth(server: MCPServer, provider: MCPOAuthProvider): Promise; + async connectWithBearer(server: MCPServer): Promise; // existing + + private async createOAuthTransport(server: MCPServer, provider: MCPOAuthProvider): Promise; + private async handleOAuthErrors(error: Error, provider: MCPOAuthProvider): Promise; +} +``` + +**OAuth Integration Points**: +- Use `StreamableHTTPClientTransport` with `authProvider` +- Handle `UnauthorizedError` with automatic token refresh +- Support both OAuth and bearer token authentication + +### 5. OAuth UI Components (`ui/SettingsDialog.ts`) + +**New UI Elements**: + +1. **Authentication Type Toggle**: + ``` + ○ Bearer Token ● OAuth 2.0 + ``` + +2. **OAuth Configuration Section**: + ``` + OAuth Server URL: [https://nla.zapier.com/oauth/...] + Client ID: [optional field] + Scope: [mcp:tools (default)] + + [Connect with OAuth] [Disconnect] + + Status: ● Connected (expires in 2h 15m) + Last connected: Dec 15, 2024 at 3:42 PM + ``` + +3. **OAuth Flow Indicators**: + - Loading spinner during authorization + - Success/error messages + - Connection status with token expiry + +**User Experience Flow**: +1. User enters OAuth Server URL from Zapier/other provider +2. Clicks "Connect with OAuth" +3. Popup opens with OAuth provider's authorization page +4. User authorizes in popup +5. Popup closes automatically, connection established +6. UI shows connected status with token info + +### 6. OAuth Utilities (`mcp/MCPOAuthUtils.ts`) + +**Helper Functions**: +```typescript +// PKCE utilities +export function generateCodeVerifier(): string; +export function generateCodeChallenge(verifier: string): Promise; + +// State management +export function generateState(): string; +export function validateState(received: string, expected: string): boolean; + +// Token utilities +export function isTokenExpired(token: OAuthTokens): boolean; +export function shouldRefreshToken(token: OAuthTokens, bufferMinutes: number): boolean; + +// URL utilities +export function parseAuthCallbackUrl(url: string): { code?: string; state?: string; error?: string }; +export function buildRedirectUrl(): string; + +// Browser utilities +export function openOAuthPopup(authUrl: string): Promise; +export function waitForPopupCallback(popup: Window): Promise; +``` + +## Implementation Phases + +### Phase 1: Core OAuth Infrastructure +1. Implement `MCPOAuthProvider` with basic functionality +2. Create `MCPOAuthUtils` with PKCE and state management +3. Add OAuth configuration fields to `MCPConfig` +4. Unit tests for OAuth utilities + +### Phase 2: OAuth Flow Implementation +1. Implement `MCPOAuthFlow` with popup-based authorization +2. Update `MCPClientSDK` to support OAuth connections +3. Add OAuth error handling and token refresh +4. Integration tests with mock OAuth server + +### Phase 3: UI Integration +1. Update `SettingsDialog` with OAuth configuration UI +2. Add OAuth connection status display +3. Implement connect/disconnect flows +4. Handle OAuth errors in UI + +### Phase 4: Testing & Polish +1. Test with real OAuth providers (Zapier, etc.) +2. Error handling improvements +3. Documentation updates +4. Performance optimizations + +## Security Considerations + +### Token Security +- **Short-lived access tokens**: Request tokens with reasonable expiry +- **Secure storage**: Use sessionStorage for tokens (cleared on tab close) +- **Token rotation**: Always use refresh tokens when available +- **Revocation**: Support token revocation on disconnect + +### Flow Security +- **PKCE**: Use for all flows, even with confidential clients +- **State validation**: Prevent CSRF attacks +- **Popup security**: Validate popup origin and URLs +- **Input validation**: Sanitize all OAuth URLs and parameters + +### Client Security +- **No client secrets**: Never store secrets in frontend code +- **Dynamic registration**: Use when supported by server +- **Scope limitation**: Request minimal necessary scopes +- **Redirect validation**: Validate all redirect URLs + +## Testing Strategy + +### Unit Tests +- `MCPOAuthUtils`: PKCE generation, state validation +- `MCPOAuthProvider`: Token storage, client metadata handling +- `MCPConfig`: OAuth configuration persistence + +### Integration Tests +- `MCPOAuthFlow`: Complete OAuth flow simulation +- `MCPClientSDK`: OAuth connection with mock transport +- UI components: OAuth settings and connection flows + +### End-to-End Tests +- Real OAuth provider integration (Zapier) +- Token refresh scenarios +- Error handling (expired tokens, revoked access) +- Multi-tab behavior with sessionStorage + +## Error Handling + +### OAuth-Specific Errors +```typescript +interface OAuthErrorType { + 'oauth_authorization_pending': 'User has not completed authorization', + 'oauth_access_denied': 'User denied authorization', + 'oauth_invalid_client': 'Client credentials invalid', + 'oauth_invalid_grant': 'Authorization code invalid/expired', + 'oauth_expired_token': 'Access token expired', + 'oauth_insufficient_scope': 'Token lacks required scope', +} +``` + +### Recovery Strategies +- **Expired tokens**: Automatic refresh with retry +- **Revoked access**: Clear tokens, prompt re-authorization +- **Network errors**: Exponential backoff with retry +- **Popup blocked**: Fallback to redirect flow +- **Invalid configuration**: Clear state, show configuration UI + +## Future Enhancements + +### Phase 2 Features +1. **Multiple OAuth servers**: Support multiple simultaneous connections +2. **Client certificate auth**: For enterprise OAuth flows +3. **OpenID Connect**: Enhanced identity and metadata +4. **Advanced scoping**: Fine-grained permission management + +### Integration Opportunities +1. **Browser credential storage**: Integration with browser password manager +2. **Single sign-on**: OAuth provider discovery and preference +3. **Audit logging**: OAuth connection and usage tracking +4. **Admin controls**: Enterprise policy enforcement + +## Success Metrics + +### Technical Metrics +- OAuth flow completion rate > 95% +- Token refresh success rate > 99% +- Connection establishment time < 5 seconds +- Zero security vulnerabilities in OAuth implementation + +### User Experience Metrics +- Reduction in support tickets related to API key management +- User adoption of OAuth vs. bearer token authentication +- Time to first successful MCP connection +- User satisfaction with OAuth flow simplicity + +## Conclusion + +This OAuth implementation will significantly improve the security and user experience of MCP connections in DevTools. By supporting industry-standard OAuth 2.0 flows, users can securely connect to services like Zapier without managing API keys manually. + +The phased implementation approach ensures we can validate the architecture early and iterate based on real-world usage patterns. The security-first design protects user credentials while maintaining the flexibility needed for various OAuth providers. \ No newline at end of file diff --git a/front_end/panels/ai_chat/evaluation/remote/EvaluationAgent.ts b/front_end/panels/ai_chat/evaluation/remote/EvaluationAgent.ts index 56ff739b9ff..2bd5255275e 100644 --- a/front_end/panels/ai_chat/evaluation/remote/EvaluationAgent.ts +++ b/front_end/panels/ai_chat/evaluation/remote/EvaluationAgent.ts @@ -4,10 +4,12 @@ import { WebSocketRPCClient } from '../../common/WebSocketRPCClient.js'; import { getEvaluationConfig, getEvaluationClientId } from '../../common/EvaluationConfig.js'; -import { ToolRegistry } from '../../agent_framework/ConfigurableAgentTool.js'; +import { ToolRegistry, ConfigurableAgentTool } from '../../agent_framework/ConfigurableAgentTool.js'; import { AgentService } from '../../core/AgentService.js'; import { createLogger } from '../../core/Logger.js'; import { createTracingProvider, withTracingContext, isTracingEnabled, getTracingConfig } from '../../tracing/TracingConfig.js'; +import { AgentDescriptorRegistry, type AgentDescriptor } from '../../core/AgentDescriptorRegistry.js'; +import '../../core/BaseOrchestratorAgent.js'; import type { TracingProvider, TracingContext } from '../../tracing/TracingProvider.js'; import type { ChatMessage } from '../../models/ChatTypes.js'; import { @@ -62,6 +64,7 @@ export class EvaluationAgent { private judgeModel: string; private miniModel: string; private nanoModel: string; + private orchestratorDescriptorPromise: Promise; constructor(options: EvaluationAgentOptions) { this.clientId = options.clientId; @@ -71,6 +74,7 @@ export class EvaluationAgent { this.miniModel = options.miniModel; this.nanoModel = options.nanoModel; this.tracingProvider = createTracingProvider(); + this.orchestratorDescriptorPromise = AgentDescriptorRegistry.getDescriptor('orchestrator:default'); logger.info('EvaluationAgent created with tracing provider', { clientId: this.clientId, @@ -335,6 +339,7 @@ export class EvaluationAgent { sessionId, parentObservationId: undefined }; + const orchestratorDescriptor = await this.orchestratorDescriptorPromise; try { // Initialize tracing provider if not already done @@ -357,7 +362,13 @@ export class EvaluationAgent { evaluationId: params.evaluationId, tool: params.tool, url: params.url, - source: 'evaluation-server' + source: 'evaluation-server', + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) }, 'evaluation-agent', ['evaluation', params.tool] @@ -504,7 +515,13 @@ export class EvaluationAgent { statusMessage: 'completed', metadata: { executionTime, - evaluationId: params.evaluationId + evaluationId: params.evaluationId, + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) } }); } catch (error) { @@ -548,7 +565,13 @@ export class EvaluationAgent { statusMessage: 'failed', metadata: { executionTime, - evaluationId: params.evaluationId + evaluationId: params.evaluationId, + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) } }); } catch (updateError) { @@ -569,6 +592,7 @@ export class EvaluationAgent { ): Promise { const spanId = `tool-exec-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const startTime = new Date(); + const toolDescriptor = tool instanceof ConfigurableAgentTool ? await AgentDescriptorRegistry.getDescriptor(tool.name) : null; // Create tool execution span if tracing context is provided if (tracingContext) { @@ -581,7 +605,13 @@ export class EvaluationAgent { input, metadata: { tool: toolName, - timeout + timeout, + ...(toolDescriptor ? { + agentVersion: toolDescriptor.version, + agentName: toolDescriptor.name, + promptHash: toolDescriptor.promptHash, + toolsetHash: toolDescriptor.toolsetHash + } : {}) } }, tracingContext.traceId); } catch (error) { @@ -595,7 +625,13 @@ export class EvaluationAgent { if (tracingContext) { this.tracingProvider.updateObservation(spanId, { endTime: new Date(), - error: `Tool execution timeout after ${timeout}ms` + error: `Tool execution timeout after ${timeout}ms`, + metadata: toolDescriptor ? { + agentVersion: toolDescriptor.version, + agentName: toolDescriptor.name, + promptHash: toolDescriptor.promptHash, + toolsetHash: toolDescriptor.toolsetHash + } : undefined }).catch(err => logger.warn('Failed to update span with timeout:', err)); } reject(new Error(`Tool execution timeout after ${timeout}ms`)); @@ -614,7 +650,13 @@ export class EvaluationAgent { if (tracingContext) { this.tracingProvider.updateObservation(spanId, { endTime: new Date(), - output: result + output: result, + metadata: toolDescriptor ? { + agentVersion: toolDescriptor.version, + agentName: toolDescriptor.name, + promptHash: toolDescriptor.promptHash, + toolsetHash: toolDescriptor.toolsetHash + } : undefined }).catch(err => logger.warn('Failed to update span with result:', err)); } @@ -627,7 +669,13 @@ export class EvaluationAgent { if (tracingContext) { this.tracingProvider.updateObservation(spanId, { endTime: new Date(), - error: error.message + error: error.message, + metadata: toolDescriptor ? { + agentVersion: toolDescriptor.version, + agentName: toolDescriptor.name, + promptHash: toolDescriptor.promptHash, + toolsetHash: toolDescriptor.toolsetHash + } : undefined }).catch(err => logger.warn('Failed to update span with error:', err)); } @@ -705,6 +753,7 @@ export class EvaluationAgent { }, timeout); let chatObservationId: string | undefined; + const orchestratorDescriptor = await this.orchestratorDescriptorPromise; try { // Get or create AgentService instance @@ -735,7 +784,13 @@ export class EvaluationAgent { startTime: new Date(), input: { message: input.message, model: modelName }, metadata: { - evaluationType: 'chat' + evaluationType: 'chat', + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) } }, tracingContext.traceId); } catch (error) { @@ -763,7 +818,18 @@ export class EvaluationAgent { try { await this.tracingProvider.updateObservation(chatObservationId, { endTime: new Date(), - output: { response: responseText, messageCount: agentService.getMessages().length } + output: { response: responseText, messageCount: agentService.getMessages().length }, + metadata: orchestratorDescriptor ? { + evaluationType: 'chat', + status: 'completed', + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : { + evaluationType: 'chat', + status: 'completed' + } }); } catch (error) { logger.warn('Failed to update chat execution observation:', error); @@ -799,7 +865,18 @@ export class EvaluationAgent { try { await this.tracingProvider.updateObservation(chatObservationId, { endTime: new Date(), - error: error instanceof Error ? error.message : String(error) + error: error instanceof Error ? error.message : String(error), + metadata: orchestratorDescriptor ? { + evaluationType: 'chat', + status: 'failed', + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : { + evaluationType: 'chat', + status: 'failed' + } }); } catch (updateError) { logger.warn('Failed to update chat execution observation with error:', updateError); diff --git a/front_end/panels/ai_chat/evaluation/runner/EvaluationRunner.ts b/front_end/panels/ai_chat/evaluation/runner/EvaluationRunner.ts index 3dd3cd0a897..df9da41fae8 100644 --- a/front_end/panels/ai_chat/evaluation/runner/EvaluationRunner.ts +++ b/front_end/panels/ai_chat/evaluation/runner/EvaluationRunner.ts @@ -8,6 +8,8 @@ import { AgentService } from '../../core/AgentService.js'; import { ToolRegistry } from '../../agent_framework/ConfigurableAgentTool.js'; import type { EvaluationConfig, TestResult, TestCase } from '../framework/types.js'; import { createLogger } from '../../core/Logger.js'; +import { AgentDescriptorRegistry, type AgentDescriptor } from '../../core/AgentDescriptorRegistry.js'; +import '../../core/BaseOrchestratorAgent.js'; import { LLMClient } from '../../LLM/LLMClient.js'; import type { LLMProviderConfig } from '../../LLM/LLMClient.js'; import { TIMING_CONSTANTS } from '../../core/Constants.js'; @@ -33,6 +35,7 @@ export class EvaluationRunner { private tracingProvider: TracingProvider; private sessionId: string; #llmInitPromise: Promise | null = null; + #orchestratorDescriptorPromise: Promise; constructor(options: EvaluationRunnerOptions) { // Get API key from AgentService @@ -71,6 +74,7 @@ export class EvaluationRunner { // Initialize tracing this.tracingProvider = createTracingProvider(); this.sessionId = `evaluation-session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; + this.#orchestratorDescriptorPromise = AgentDescriptorRegistry.getDescriptor('orchestrator:default'); logger.info('EvaluationRunner created with tracing provider', { sessionId: this.sessionId, @@ -169,6 +173,8 @@ export class EvaluationRunner { parentObservationId: undefined }; + const orchestratorDescriptor = await this.#orchestratorDescriptorPromise; + // Create trace for this evaluation if (isTracingEnabled()) { try { @@ -197,7 +203,13 @@ export class EvaluationRunner { type: 'evaluation', tool: testCase.tool, url: testCase.url, - testId: testCase.id || testCase.name + testId: testCase.id || testCase.name, + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) }, 'evaluation-runner', ['evaluation', testCase.tool, 'test'] @@ -243,7 +255,13 @@ export class EvaluationRunner { metadata: { tool: testCase.tool, testId: testCase.id || testCase.name, - phase: 'llm-evaluation' + phase: 'llm-evaluation', + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) } }, traceId); } @@ -262,7 +280,13 @@ export class EvaluationRunner { metadata: { score: llmJudgment.score, passed: llmJudgment.passed, - explanation: llmJudgment.explanation + explanation: llmJudgment.explanation, + ...(orchestratorDescriptor ? { + agentVersion: orchestratorDescriptor.version, + agentName: orchestratorDescriptor.name, + promptHash: orchestratorDescriptor.promptHash, + toolsetHash: orchestratorDescriptor.toolsetHash + } : {}) } }); } diff --git a/front_end/panels/ai_chat/evaluation/test-cases/schema-extractor-tests.ts b/front_end/panels/ai_chat/evaluation/test-cases/schema-extractor-tests.ts index a2ff50c0ce7..e31b9f48d20 100644 --- a/front_end/panels/ai_chat/evaluation/test-cases/schema-extractor-tests.ts +++ b/front_end/panels/ai_chat/evaluation/test-cases/schema-extractor-tests.ts @@ -27,7 +27,7 @@ function createSchemaTest( ): TestCase { return { ...baseConfig, - tool: useStreamlined ? 'extract_schema_streamlined' : 'extract_schema_data' + tool: useStreamlined ? 'extract_schema_streamlined' : 'extract_data' }; } @@ -122,7 +122,7 @@ export const ecommerceTest: TestCase = { name: 'Extract Amazon Product Details', description: 'Extract product information from an Amazon product page', url: 'https://www.amazon.com/Obelisk-Climbing-Rustproof-Trellises-Clematis/dp/B0B4SBY6QD/', - tool: 'extract_schema_data', + tool: 'extract_data', input: { schema: { type: 'object', @@ -194,7 +194,7 @@ export const newsTest: TestCase = { name: 'Extract BBC News Article', description: 'Extract article content and metadata from a BBC News page', url: 'https://www.bbc.com/news/technology', - tool: 'extract_schema_data', + tool: 'extract_data', input: { schema: { type: 'object', @@ -255,7 +255,7 @@ export const googleSearchTest: TestCase = { name: 'Extract Google Search Results', description: 'Extract search results from Google search page', url: 'https://www.google.com/search?q=chrome+devtools+tutorial', - tool: 'extract_schema_data', + tool: 'extract_data', input: { schema: { type: 'object', @@ -321,7 +321,7 @@ export const bingSearchTest: TestCase = { name: 'Extract Bing Search Results', description: 'Extract search results from Bing search page', url: 'https://www.bing.com/search?q=web+scraping+best+practices', - tool: 'extract_schema_data', + tool: 'extract_data', input: { schema: { type: 'object', @@ -382,7 +382,7 @@ export const wikipediaSearchTest: TestCase = { name: 'Extract Wikipedia Search Results', description: 'Extract search results from Wikipedia search', url: 'https://en.wikipedia.org/w/index.php?search=artificial+intelligence&title=Special:Search', - tool: 'extract_schema_data', + tool: 'extract_data', input: { schema: { type: 'object', @@ -448,7 +448,7 @@ export const homeDepotTest: TestCase = { name: 'Extract Home Depot Product Search', description: 'Extract product listings from Home Depot search results', url: 'https://www.homedepot.com/s/power%2520drill', - tool: 'extract_schema_data', + tool: 'extract_data', input: { schema: { type: 'object', @@ -526,7 +526,7 @@ export const macysTest: TestCase = { name: 'Extract Macy\'s Product Listings', description: 'Extract fashion products from Macy\'s category page', url: 'https://www.macys.com/shop/womens-clothing/womens-dresses', - tool: 'extract_schema_data', + tool: 'extract_data', input: { schema: { type: 'object', @@ -619,7 +619,7 @@ export const googleFlightsTest: TestCase = { name: 'Extract Google Flights Search Results', description: 'Extract flight options from Google Flights search', url: 'https://www.google.com/travel/flights/search?tfs=CBwQAhojEgoyMDI1LTEyLTI0agwIAhIIL20vMGQ5anJyBwgBEgNTRk8aIxIKMjAyNS0xMi0zMWoHCAESA1NGT3IMCAISCC9tLzBkOWpyQAFIAXABggELCP___________wGYAQE', - tool: 'extract_schema_data', + tool: 'extract_data', input: { schema: { type: 'object', @@ -707,7 +707,7 @@ export const simpleTest: TestCase = { name: 'Extract GitHub Repository Info', description: 'Extract basic repository information from a GitHub page', url: 'https://github.com/microsoft/TypeScript', - tool: 'extract_schema_data', + tool: 'extract_data', input: { schema: { type: 'object', diff --git a/front_end/panels/ai_chat/evaluation/test-cases/web-task-agent-tests.ts b/front_end/panels/ai_chat/evaluation/test-cases/web-task-agent-tests.ts index 0d1c7dc2440..14811e9f893 100644 --- a/front_end/panels/ai_chat/evaluation/test-cases/web-task-agent-tests.ts +++ b/front_end/panels/ai_chat/evaluation/test-cases/web-task-agent-tests.ts @@ -364,7 +364,7 @@ export const jobSearchTest: TestCase = { 'Either used construct_direct_url for LinkedIn job search OR used traditional form interaction', 'If using direct URL: constructed proper LinkedIn job search URL with keywords and location', 'If using forms: delegated keyword and location input to action_agent', - 'Extracted job listings using schema_based_extractor', + 'Extracted job listings using extract_data', 'Returned structured job data in readable text format (not JSON)', 'Each job listing includes title, company, location, and other relevant fields', 'Results are numbered or organized clearly for easy reading', @@ -485,7 +485,7 @@ export const realEstateSearchTest: TestCase = { 'Delegated price filter setting to action_agent', 'Coordinated property type selection through action_agent', 'Applied search filters through proper action_agent calls', - 'Extracted property listings with schema_based_extractor', + 'Extracted property listings with extract_data', 'Returned structured property data in readable text format (not JSON)', 'Each property includes address, price, bedrooms, bathrooms, and other key details', 'Properties are clearly numbered or organized for easy comparison', diff --git a/front_end/panels/ai_chat/mcp/MCPConfig.ts b/front_end/panels/ai_chat/mcp/MCPConfig.ts index 8753a7c5e90..f17f63030ca 100644 --- a/front_end/panels/ai_chat/mcp/MCPConfig.ts +++ b/front_end/panels/ai_chat/mcp/MCPConfig.ts @@ -2,66 +2,342 @@ import { createLogger } from '../core/Logger.js'; const logger = createLogger('MCPConfig'); -export interface MCPConfigData { +export interface MCPProviderConfig { + id: string; + name?: string; + endpoint: string; + authType: 'bearer' | 'oauth'; enabled: boolean; - endpoint?: string; // MVP: single endpoint; Phase 2 can support multiple token?: string; + oauthClientId?: string; + oauthRedirectUrl?: string; + oauthScope?: string; +} + +export interface MCPConfigData { + enabled: boolean; + providers: MCPProviderConfig[]; toolAllowlist?: string[]; autostart?: boolean; toolMode?: 'all' | 'router' | 'meta'; maxToolsPerTurn?: number; maxMcpPerTurn?: number; + autoRefreshTokens?: boolean; + maxConnectionRetries?: number; + retryDelayMs?: number; + proactiveRefreshThresholdMs?: number; } +export type MCPConfigUpdate = Partial>; + const KEYS = { enabled: 'ai_chat_mcp_enabled', - endpoint: 'ai_chat_mcp_endpoint', - token: 'ai_chat_mcp_token', + providers: 'ai_chat_mcp_providers', + tokenMap: 'ai_chat_mcp_tokens_by_provider', allowlist: 'ai_chat_mcp_tool_allowlist', autostart: 'ai_chat_mcp_autostart', toolMode: 'ai_chat_mcp_tool_mode', maxToolsPerTurn: 'ai_chat_mcp_max_tools_per_turn', maxMcpPerTurn: 'ai_chat_mcp_max_mcp_per_turn', + autoRefreshTokens: 'ai_chat_mcp_auto_refresh_tokens', + maxConnectionRetries: 'ai_chat_mcp_max_connection_retries', + retryDelayMs: 'ai_chat_mcp_retry_delay_ms', + proactiveRefreshThresholdMs: 'ai_chat_mcp_proactive_refresh_threshold_ms', } as const; -export function getMCPConfig(): MCPConfigData { +interface StoredProvider { + id: string; + name?: string; + endpoint: string; + authType: 'bearer' | 'oauth'; + enabled?: boolean; + oauthClientId?: string; + oauthRedirectUrl?: string; + oauthScope?: string; +} + +type TokenMap = Record; + +function sanitizeProvider(provider: StoredProvider, index: number): StoredProvider | null { + if (!provider || typeof provider !== 'object') { + return null; + } + const id = typeof provider.id === 'string' && provider.id.trim() ? provider.id.trim() : undefined; + const endpoint = typeof provider.endpoint === 'string' ? provider.endpoint.trim() : ''; + const authType = provider.authType === 'oauth' ? 'oauth' : 'bearer'; + if (!id || !endpoint) { + return null; + } + return { + id, + name: typeof provider.name === 'string' ? provider.name.trim() || undefined : undefined, + endpoint, + authType, + enabled: provider.enabled !== false, + oauthClientId: typeof provider.oauthClientId === 'string' ? provider.oauthClientId.trim() || undefined : undefined, + oauthRedirectUrl: typeof provider.oauthRedirectUrl === 'string' ? provider.oauthRedirectUrl.trim() || undefined : undefined, + oauthScope: typeof provider.oauthScope === 'string' ? provider.oauthScope.trim() || undefined : undefined, + }; +} + +function loadProviders(): StoredProvider[] { try { - const enabled = localStorage.getItem(KEYS.enabled) === 'true'; - const endpoint = localStorage.getItem(KEYS.endpoint) || undefined; - const token = sessionStorage.getItem(KEYS.token) || undefined; - let toolAllowlist: string[] | undefined; - const raw = localStorage.getItem(KEYS.allowlist); - if (raw) { - try { toolAllowlist = JSON.parse(raw); } catch { toolAllowlist = undefined; } + const raw = localStorage.getItem(KEYS.providers); + if (!raw) { + return []; } - const autostart = localStorage.getItem(KEYS.autostart) === 'true'; - const toolMode = (localStorage.getItem(KEYS.toolMode) as MCPConfigData['toolMode']) || 'router'; - const maxToolsPerTurn = parseInt(localStorage.getItem(KEYS.maxToolsPerTurn) || '20', 10); - const maxMcpPerTurn = parseInt(localStorage.getItem(KEYS.maxMcpPerTurn) || '8', 10); - return { enabled, endpoint, token, toolAllowlist, autostart, toolMode, maxToolsPerTurn, maxMcpPerTurn }; + const parsed = JSON.parse(raw); + if (!Array.isArray(parsed)) { + return []; + } + const seen = new Set(); + const result: StoredProvider[] = []; + for (let i = 0; i < parsed.length; ++i) { + const sanitized = sanitizeProvider(parsed[i] as StoredProvider, i); + if (!sanitized) { + continue; + } + if (seen.has(sanitized.id)) { + continue; + } + seen.add(sanitized.id); + result.push(sanitized); + } + return result; } catch (err) { - logger.error('Failed to load MCP config', err); - return { enabled: false }; + logger.warn('Failed to parse MCP providers', err); + return []; + } +} + +function saveProvidersInternal(providers: StoredProvider[]): void { + try { + if (!providers.length) { + localStorage.removeItem(KEYS.providers); + } else { + localStorage.setItem(KEYS.providers, JSON.stringify(providers)); + } + } catch (err) { + logger.error('Failed to persist MCP providers', err); } } -export function setMCPConfig(config: MCPConfigData): void { +function loadTokenMap(): TokenMap { try { - localStorage.setItem(KEYS.enabled, String(!!config.enabled)); - if (config.endpoint !== undefined) { - localStorage.setItem(KEYS.endpoint, config.endpoint); + const raw = sessionStorage.getItem(KEYS.tokenMap); + if (!raw) { + return {}; + } + const parsed = JSON.parse(raw); + if (parsed && typeof parsed === 'object') { + return parsed as TokenMap; + } + } catch (err) { + logger.warn('Failed to parse MCP token map', err); + } + sessionStorage.removeItem(KEYS.tokenMap); + return {}; +} + +function saveTokenMap(tokenMap: TokenMap): void { + try { + if (Object.keys(tokenMap).length === 0) { + sessionStorage.removeItem(KEYS.tokenMap); + } else { + sessionStorage.setItem(KEYS.tokenMap, JSON.stringify(tokenMap)); + } + } catch (err) { + logger.error('Failed to persist MCP token map', err); + } +} + +function sanitizeIdBase(input: string): string { + let sanitized = input.trim().toLowerCase(); + sanitized = sanitized.replace(/[^a-z0-9_-]+/g, '-'); + sanitized = sanitized.replace(/-+/g, '-'); + sanitized = sanitized.replace(/^[-_]+|[-_]+$/g, ''); + return sanitized || 'mcp'; +} + +function ensureMcpPrefix(id: string): string { + return id.startsWith('mcp-') ? id : `mcp-${id}`; +} + +function extractDomainBase(host: string): string { + const cleanedHost = host.replace(/\.+$/, '').toLowerCase(); + if (/^\d+\.\d+\.\d+\.\d+$/.test(cleanedHost)) { + return cleanedHost; + } + const parts = cleanedHost.split('.').filter(Boolean); + if (parts.length === 0) { + return host; + } + if (parts.length === 1) { + return parts[0]; + } + + const commonSecondLevel = new Set(['co', 'com', 'net', 'org', 'gov', 'edu', 'ac']); + const topLevel = parts[parts.length - 1]; + const secondLevel = parts[parts.length - 2]; + + // Handle common country-code second-level domains like *.co.uk + if (topLevel.length === 2 && parts.length >= 3 && commonSecondLevel.has(secondLevel)) { + return parts[parts.length - 3]; + } + + const commonTlds = new Set(['com', 'org', 'net', 'gov', 'edu', 'io', 'ai', 'app', 'dev', 'cloud', 'info', 'biz', 'co']); + if (commonTlds.has(topLevel) && parts.length >= 2) { + return secondLevel; + } + + return secondLevel; +} + +export function generateMCPProviderId(provider?: { id?: string; name?: string; endpoint?: string }): string { + // Prefer explicit ID if provided + const explicitId = provider?.id?.trim(); + if (explicitId) { + return ensureMcpPrefix(sanitizeIdBase(explicitId)); + } + + const name = provider?.name?.trim(); + if (name) { + return ensureMcpPrefix(sanitizeIdBase(name)); + } + + const endpoint = provider?.endpoint?.trim(); + if (endpoint) { + try { + const host = new URL(endpoint).hostname; + if (host) { + return ensureMcpPrefix(sanitizeIdBase(extractDomainBase(host))); + } + } catch { + // Fall through to using the raw endpoint if URL parsing fails + return ensureMcpPrefix(sanitizeIdBase(endpoint)); + } + } + + // Fallback: generate a random ID if neither name nor endpoint is available yet + const fallback = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`; + return ensureMcpPrefix(sanitizeIdBase(fallback)); +} + +export function getMCPProviders(): MCPProviderConfig[] { + const providers = loadProviders(); + const tokenMap = loadTokenMap(); + return providers.map(provider => ({ + ...provider, + enabled: provider.enabled !== false, + token: tokenMap[provider.id], + })); +} + +export function saveMCPProviders(providers: MCPProviderConfig[]): void { + // Get existing providers to identify which ones are being removed + const existingProviders = loadProviders(); + const existingIds = new Set(existingProviders.map(p => p.id)); + + const sanitizedProviders: StoredProvider[] = []; + const newTokenMap: TokenMap = {}; + const newIds = new Set(); + const seenIds = new Set(); + + for (const provider of providers) { + const id = generateMCPProviderId(provider); + const endpoint = provider.endpoint?.trim(); + if (!endpoint) { + continue; + } + const authType: 'bearer' | 'oauth' = provider.authType === 'oauth' ? 'oauth' : 'bearer'; + + if (seenIds.has(id)) { + throw new Error(`Duplicate MCP connection identifier: ${id}. Please use unique names or endpoints.`); + } + seenIds.add(id); + newIds.add(id); + + sanitizedProviders.push({ + id, + name: provider.name?.trim() || undefined, + endpoint, + authType, + enabled: provider.enabled !== false, + oauthClientId: provider.oauthClientId?.trim() || undefined, + oauthRedirectUrl: provider.oauthRedirectUrl?.trim() || undefined, + oauthScope: provider.oauthScope?.trim() || undefined, + }); + + if (authType === 'bearer' && provider.token) { + newTokenMap[id] = provider.token; + } + } + + // Clean up OAuth data for removed providers + for (const existingId of existingIds) { + if (!newIds.has(existingId)) { + cleanupOAuthData(existingId); } - if (config.token !== undefined) { + } + + saveProvidersInternal(sanitizedProviders); + saveTokenMap(newTokenMap); + dispatchMCPConfigChanged(); +} + +export function getMCPConfig(): MCPConfigData { + try { + const enabled = localStorage.getItem(KEYS.enabled) !== 'false'; // Default: true + const providers = getMCPProviders(); + + let toolAllowlist: string[] | undefined; + const rawAllowlist = localStorage.getItem(KEYS.allowlist); + if (rawAllowlist) { try { - if (config.token) { - sessionStorage.setItem(KEYS.token, config.token); - } else { - sessionStorage.removeItem(KEYS.token); + const parsed = JSON.parse(rawAllowlist); + if (Array.isArray(parsed)) { + toolAllowlist = parsed.filter(item => typeof item === 'string'); } - } catch (e) { - logger.error('Failed to persist MCP token to sessionStorage', e); + } catch { + toolAllowlist = undefined; } } + + const autostart = localStorage.getItem(KEYS.autostart) === 'true'; + const toolMode = (localStorage.getItem(KEYS.toolMode) as MCPConfigData['toolMode']) || 'all'; + const maxToolsPerTurn = parseInt(localStorage.getItem(KEYS.maxToolsPerTurn) || '50', 10); + const maxMcpPerTurn = parseInt(localStorage.getItem(KEYS.maxMcpPerTurn) || '50', 10); + + // New auto-refresh and retry configuration options with defaults + const autoRefreshTokens = localStorage.getItem(KEYS.autoRefreshTokens) !== 'false'; // Default: true + const maxConnectionRetries = parseInt(localStorage.getItem(KEYS.maxConnectionRetries) || '3', 10); + const retryDelayMs = parseInt(localStorage.getItem(KEYS.retryDelayMs) || '1000', 10); + const proactiveRefreshThresholdMs = parseInt(localStorage.getItem(KEYS.proactiveRefreshThresholdMs) || '300000', 10); // 5 minutes + + return { + enabled, + providers, + toolAllowlist, + autostart, + toolMode, + maxToolsPerTurn, + maxMcpPerTurn, + autoRefreshTokens, + maxConnectionRetries, + retryDelayMs, + proactiveRefreshThresholdMs, + }; + } catch (err) { + logger.error('Failed to load MCP config', err); + return { enabled: false, providers: [] }; + } +} + +export function setMCPConfig(config: MCPConfigUpdate): void { + try { + if (config.enabled !== undefined) { + localStorage.setItem(KEYS.enabled, String(!!config.enabled)); + } if (config.toolAllowlist) { localStorage.setItem(KEYS.allowlist, JSON.stringify(config.toolAllowlist)); } @@ -77,6 +353,18 @@ export function setMCPConfig(config: MCPConfigData): void { if (config.maxMcpPerTurn !== undefined) { localStorage.setItem(KEYS.maxMcpPerTurn, String(config.maxMcpPerTurn)); } + if (config.autoRefreshTokens !== undefined) { + localStorage.setItem(KEYS.autoRefreshTokens, String(!!config.autoRefreshTokens)); + } + if (config.maxConnectionRetries !== undefined) { + localStorage.setItem(KEYS.maxConnectionRetries, String(config.maxConnectionRetries)); + } + if (config.retryDelayMs !== undefined) { + localStorage.setItem(KEYS.retryDelayMs, String(config.retryDelayMs)); + } + if (config.proactiveRefreshThresholdMs !== undefined) { + localStorage.setItem(KEYS.proactiveRefreshThresholdMs, String(config.proactiveRefreshThresholdMs)); + } } catch (err) { logger.error('Failed to save MCP config', err); } finally { @@ -101,3 +389,97 @@ function dispatchMCPConfigChanged(): void { logger.warn('Failed to dispatch MCP config change event', err); } } + +/** + * Interface for stored authentication error details + */ +export interface StoredAuthError { + message: string; + type: 'authentication' | 'network' | 'configuration' | 'server_error' | 'unknown'; + timestamp: number; + serverId: string; +} + +/** + * Get stored authentication errors for all MCP providers + */ +export function getStoredAuthErrors(): StoredAuthError[] { + const errors: StoredAuthError[] = []; + const providers = getMCPProviders(); + + for (const provider of providers) { + if (provider.authType === 'oauth') { + try { + const prefix = `mcp_oauth:${provider.id}:`; + const message = localStorage.getItem(`${prefix}last_auth_error`); + const timestampStr = localStorage.getItem(`${prefix}auth_error_timestamp`); + const type = localStorage.getItem(`${prefix}auth_error_type`); + + if (message && timestampStr && type) { + const timestamp = parseInt(timestampStr, 10); + if (!isNaN(timestamp)) { + errors.push({ + message, + type: type as StoredAuthError['type'], + timestamp, + serverId: provider.id, + }); + } + } + } catch (err) { + logger.warn('Failed to retrieve stored auth error for provider', { providerId: provider.id, err }); + } + } + } + + return errors; +} + +/** + * Clear stored authentication error for a specific provider + */ +export function clearStoredAuthError(serverId: string): void { + try { + const prefix = `mcp_oauth:${serverId}:`; + localStorage.removeItem(`${prefix}last_auth_error`); + localStorage.removeItem(`${prefix}auth_error_timestamp`); + localStorage.removeItem(`${prefix}auth_error_type`); + } catch (err) { + logger.warn('Failed to clear stored auth error', { serverId, err }); + } +} + +/** + * Check if any OAuth providers have stored authentication errors + */ +export function hasStoredAuthErrors(): boolean { + return getStoredAuthErrors().length > 0; +} + +/** + * Clean up all OAuth-related data for a specific provider + */ +export function cleanupOAuthData(serverId: string): void { + try { + const prefix = `mcp_oauth:${serverId}:`; + const keysToRemove = [ + 'tokens', + 'client_info', + 'code_verifier', + 'state', + 'original_url', + 'last_auth_error', + 'auth_error_timestamp', + 'auth_error_type', + 'token_expiration', + ]; + + for (const key of keysToRemove) { + localStorage.removeItem(`${prefix}${key}`); + } + + logger.info('Cleaned up OAuth data for provider', { serverId }); + } catch (err) { + logger.warn('Failed to clean up OAuth data', { serverId, err }); + } +} diff --git a/front_end/panels/ai_chat/mcp/MCPRegistry.ts b/front_end/panels/ai_chat/mcp/MCPRegistry.ts index d095f754b4d..3d39441d9c3 100644 --- a/front_end/panels/ai_chat/mcp/MCPRegistry.ts +++ b/front_end/panels/ai_chat/mcp/MCPRegistry.ts @@ -1,40 +1,125 @@ import { createLogger } from '../core/Logger.js'; import { ToolRegistry } from '../agent_framework/ConfigurableAgentTool.js'; import * as ToolNameMap from '../core/ToolNameMap.js'; -import type { MCPToolDef, MCPServer } from '../../../third_party/mcp-sdk/mcp-sdk.js'; -import { MCPClient } from '../../../third_party/mcp-sdk/mcp-sdk.js'; +import type { MCPToolDef, MCPServer } from '../../../third_party/mcp-sdk/mcp-sdk-v2.js'; +import { MCPClient } from '../../../third_party/mcp-sdk/mcp-sdk-v2.js'; import { getMCPConfig } from './MCPConfig.js'; import { MCPToolAdapter } from './MCPToolAdapter.js'; const logger = createLogger('MCPRegistry'); +interface RegistryServer extends MCPServer { + name?: string; + authType: 'bearer' | 'oauth'; +} + +export interface ConnectionResult { + serverId: string; + name?: string; + endpoint: string; + connected: boolean; + error?: Error; + errorType?: 'connection' | 'authentication' | 'configuration' | 'network' | 'server_error' | 'unknown'; + retryAttempts?: number; +} + +interface RetryConfig { + maxRetries: number; + baseDelayMs: number; + maxDelayMs: number; + backoffMultiplier: number; + jitterMs: number; +} + +export interface ConnectionEvent { + timestamp: Date; + serverId: string; + eventType: 'connected' | 'disconnected' | 'auth_error' | 'retry_attempt' | 'connection_failed'; + details?: string; + retryAttempt?: number; +} + export interface MCPRegistryStatus { enabled: boolean; - servers: Array<{ id: string; endpoint: string; connected: boolean; toolCount: number }>; + servers: Array<{ id: string; name?: string; endpoint: string; authType: 'bearer' | 'oauth'; connected: boolean; toolCount: number }>; registeredToolNames: string[]; lastError?: string; lastErrorType?: 'connection' | 'authentication' | 'configuration' | 'network' | 'server_error' | 'unknown'; lastConnected?: Date; lastDisconnected?: Date; + connectionEvents: ConnectionEvent[]; } class RegistryImpl { private client = new MCPClient(); - private servers: MCPServer[] = []; + private servers: RegistryServer[] = []; private registeredTools: string[] = []; private lastError?: string; private lastErrorType?: 'connection' | 'authentication' | 'configuration' | 'network' | 'server_error' | 'unknown'; private lastConnected?: Date; private lastDisconnected?: Date; + private connectionEvents: ConnectionEvent[] = []; + private readonly maxConnectionEvents = 50; // Keep last 50 events + + private getRetryConfig(): RetryConfig { + const cfg = getMCPConfig(); + return { + maxRetries: cfg.maxConnectionRetries || 3, + baseDelayMs: cfg.retryDelayMs || 1000, + maxDelayMs: Math.max((cfg.retryDelayMs || 1000) * 10, 10000), // 10x base delay or 10s minimum + backoffMultiplier: 2, + jitterMs: 500, + }; + } private categorizeError(error: unknown): 'connection' | 'authentication' | 'configuration' | 'network' | 'server_error' | 'unknown' { const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase(); - + + // Check for SSE-specific error context + if (error instanceof Error && 'context' in error) { + const context = (error as any).context; + + // OAuth-related failures + if (context?.authState === 'oauth_required' || context?.httpStatus === 401) { + return 'authentication'; + } + + // Network/connection failures with specific status codes + if (context?.httpStatus === 404) { + return 'configuration'; // Endpoint not found + } + if (context?.httpStatus === 403) { + return 'authentication'; // Forbidden - likely auth issue + } + if (context?.httpStatus >= 500) { + return 'server_error'; + } + + // CORS or connection timeouts + if (context?.readyState === 2) { // EventSource CLOSED state + return 'network'; + } + } + + // Check for CORS errors (common with SSE) + if (message.includes('cors') || message.includes('cross-origin') || message.includes('fetch')) { + return 'network'; + } + + // SSE-specific errors + if (message.includes('sse error') || message.includes('eventsource')) { + if (message.includes('oauth') || message.includes('401') || message.includes('unauthorized')) { + return 'authentication'; + } + return 'connection'; + } + + // Original categorization logic if (message.includes('unauthorized') || message.includes('authentication') || message.includes('auth') || message.includes('token')) { return 'authentication'; } if (message.includes('network') || message.includes('timeout') || message.includes('connection reset') || message.includes('econnreset')) { - return 'network'; + return 'network'; } if (message.includes('connection') || message.includes('connect') || message.includes('econnrefused') || message.includes('websocket')) { return 'connection'; @@ -53,110 +138,551 @@ class RegistryImpl { this.lastErrorType = this.categorizeError(error); } - async init(): Promise { + /** + * Check if an error type is worth retrying + */ + private shouldRetryError(errorType: string): boolean { + // Only retry network errors and server errors, not authentication or configuration errors + return errorType === 'network' || errorType === 'server_error' || errorType === 'connection'; + } + + /** + * Calculate delay for retry attempt with exponential backoff and jitter + */ + private calculateRetryDelay(attempt: number, config: RetryConfig): number { + const exponentialDelay = config.baseDelayMs * Math.pow(config.backoffMultiplier, attempt); + const jitter = Math.random() * config.jitterMs; + return Math.min(exponentialDelay + jitter, config.maxDelayMs); + } + + /** + * Sleep for specified milliseconds + */ + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + /** + * Refresh tools for a specific server with retry logic + */ + private async refreshToolsForServer(serverId: string, retryCount = 0): Promise { + const maxRetries = 2; + const retryDelayMs = 2000; // 2 seconds + + if (!this.client.isConnected(serverId)) { + return; + } + + const server = this.servers.find(s => s.id === serverId); + if (!server) { + logger.warn('MCPRegistry: Server not found for tool refresh', { serverId }); + return; + } + + try { + + const tools = await this.client.listTools(serverId); + + // If we got tools, we're done - tools exist and will be registered by the next refresh + if (tools.length > 0) { + return; + } else if (retryCount < maxRetries) { + // No tools found, but maybe the server needs more time + + setTimeout(async () => { + try { + await this.refreshToolsForServer(serverId, retryCount + 1); + } catch (error) { + logger.warn('MCPRegistry: Delayed tool refresh failed', { serverId, error }); + } + }, retryDelayMs); + } else { + } + } catch (error) { + logger.warn('MCPRegistry: Failed to refresh tools for server', { + serverId, + attempt: retryCount + 1, + maxRetries, + error + }); + + if (retryCount < maxRetries) { + + setTimeout(async () => { + try { + await this.refreshToolsForServer(serverId, retryCount + 1); + } catch (retryError) { + logger.warn('MCPRegistry: Delayed tool refresh retry failed', { serverId, error: retryError }); + } + }, retryDelayMs); + } + } + } + + /** + * Track a connection event + */ + private trackConnectionEvent(event: Omit): void { + const connectionEvent: ConnectionEvent = { + ...event, + timestamp: new Date(), + }; + + this.connectionEvents.push(connectionEvent); + + // Keep only the last N events + if (this.connectionEvents.length > this.maxConnectionEvents) { + this.connectionEvents = this.connectionEvents.slice(-this.maxConnectionEvents); + } + + // Also persist to localStorage for persistence across sessions + this.saveConnectionEvents(); + + } + + /** + * Save connection events to localStorage + */ + private saveConnectionEvents(): void { + try { + const serialized = this.connectionEvents.map(event => ({ + ...event, + timestamp: event.timestamp.toISOString(), + })); + localStorage.setItem('ai_chat_mcp_connection_events', JSON.stringify(serialized)); + } catch (error) { + logger.warn('Failed to save connection events', error); + } + } + + /** + * Load connection events from localStorage + */ + private loadConnectionEvents(): void { + try { + const raw = localStorage.getItem('ai_chat_mcp_connection_events'); + if (raw) { + const parsed = JSON.parse(raw); + if (Array.isArray(parsed)) { + this.connectionEvents = parsed.map(event => ({ + ...event, + timestamp: new Date(event.timestamp), + })).slice(-this.maxConnectionEvents); // Ensure we don't exceed max + } + } + } catch (error) { + logger.warn('Failed to load connection events', error); + this.connectionEvents = []; + } + } + + /** + * Get connection events for display + */ + getConnectionEvents(): ConnectionEvent[] { + return [...this.connectionEvents].reverse(); // Most recent first + } + + /** + * Clear stored authentication error for a specific server + */ + private clearStoredAuthErrorForServer(serverId: string): void { + try { + const prefix = `mcp_oauth:${serverId}:`; + localStorage.removeItem(`${prefix}last_auth_error`); + localStorage.removeItem(`${prefix}auth_error_timestamp`); + localStorage.removeItem(`${prefix}auth_error_type`); + } catch (err) { + logger.warn('Failed to clear stored auth error', { serverId, err }); + } + } + + /** + * Attempt to connect to a server with retry logic + */ + private async connectWithRetry(server: RegistryServer, config?: RetryConfig, interactive: boolean = true): Promise { + const retryConfig = config || this.getRetryConfig(); + let lastError: unknown; + let retryAttempts = 0; + + for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) { + try { + await this.client.connect({ ...server, interactive }); + this.lastConnected = new Date(); + + + // Clear any stored authentication errors on successful connection + this.clearStoredAuthErrorForServer(server.id); + + // Track successful connection + this.trackConnectionEvent({ + serverId: server.id, + eventType: 'connected', + details: `Connected on attempt ${attempt + 1}`, + retryAttempt: attempt, + }); + + // Automatically refresh tools after successful connection + try { + await this.refreshToolsForServer(server.id); + } catch (refreshError) { + logger.warn('MCPRegistry: Auto-refresh failed after connection', { + serverId: server.id, + error: refreshError + }); + } + + return { + serverId: server.id, + name: server.name, + endpoint: server.endpoint, + connected: true, + retryAttempts, + }; + } catch (error) { + lastError = error; + retryAttempts = attempt; + const errorType = this.categorizeError(error); + + // Enhanced logging with error details + const logContext: any = { + serverId: server.id, + endpoint: server.endpoint, + authType: server.authType, + attempt: attempt + 1, + errorType + }; + if (error instanceof Error && 'context' in error) { + const context = (error as any).context; + logContext.errorContext = context; + } + + logger.warn('MCP connect attempt failed', { ...logContext, error }); + + // Track authentication errors specifically + if (errorType === 'authentication') { + this.trackConnectionEvent({ + serverId: server.id, + eventType: 'auth_error', + details: error instanceof Error ? error.message : String(error), + retryAttempt: attempt, + }); + } else { + // Track retry attempts for other errors + this.trackConnectionEvent({ + serverId: server.id, + eventType: 'retry_attempt', + details: `${errorType}: ${error instanceof Error ? error.message : String(error)}`, + retryAttempt: attempt, + }); + } + + // Don't retry for authentication or configuration errors + if (!this.shouldRetryError(errorType)) { + logger.info('MCP connection error not retryable', { + serverId: server.id, + errorType, + finalAttempt: attempt + 1 + }); + break; + } + + // Don't sleep after the last attempt + if (attempt < retryConfig.maxRetries) { + const delay = this.calculateRetryDelay(attempt, retryConfig); + logger.info('MCP connection retry scheduled', { + serverId: server.id, + attempt: attempt + 1, + nextAttemptIn: `${delay}ms` + }); + await this.sleep(delay); + } + } + } + + // All attempts failed + this.setError(lastError); + + logger.error('MCP connect failed after all retries', { + serverId: server.id, + endpoint: server.endpoint, + totalAttempts: retryAttempts + 1, + error: lastError + }); + + // Track final connection failure + this.trackConnectionEvent({ + serverId: server.id, + eventType: 'connection_failed', + details: `Failed after ${retryAttempts + 1} attempts: ${lastError instanceof Error ? lastError.message : String(lastError)}`, + retryAttempt: retryAttempts, + }); + + return { + serverId: server.id, + name: server.name, + endpoint: server.endpoint, + connected: false, + error: lastError instanceof Error ? lastError : new Error(String(lastError)), + errorType: this.categorizeError(lastError), + retryAttempts, + }; + } + + async init(interactive: boolean = false): Promise { const cfg = getMCPConfig(); this.registeredTools = []; this.lastError = undefined; this.lastErrorType = undefined; - // Reset mappings on reconnect ToolNameMap.clear(); + // Load connection events from previous sessions + this.loadConnectionEvents(); + if (!cfg.enabled) { - logger.info('MCP disabled'); - return; + return []; } - if (!cfg.endpoint) { - logger.warn('MCP endpoint not configured'); - return; + + const providers = cfg.providers.filter(provider => provider.enabled); + if (providers.length === 0) { + logger.warn('No MCP providers configured'); + return []; } - const server: MCPServer = { - id: 'default', - endpoint: cfg.endpoint, - token: cfg.token, - }; - this.servers = [server]; + const configuredIds = new Set(providers.map(provider => provider.id)); + for (const existing of this.servers) { + if (!configuredIds.has(existing.id)) { + try { + this.client.disconnect(existing.id); + } catch (error) { + logger.warn('Failed to disconnect MCP server', { serverId: existing.id, error }); + } + } + } - try { - await this.client.connect(server); - this.lastConnected = new Date(); - logger.info('MCP connected', { endpoint: server.endpoint }); - } catch (err) { - this.setError(err); - logger.error('MCP connect failed', err); + this.servers = providers.map(provider => ({ + id: provider.id, + name: provider.name, + endpoint: provider.endpoint, + authType: provider.authType, + token: provider.authType === 'bearer' ? provider.token : undefined, + oauth: provider.authType === 'oauth' ? { + clientId: provider.oauthClientId, + scope: provider.oauthScope, + redirectUri: provider.oauthRedirectUrl, + } : undefined, + })); + + // In non-interactive mode, let the OAuth provider handle token checks internally + // This allows auto-connection with refresh tokens while avoiding popups for new auth + + const results: ConnectionResult[] = []; + + for (const server of this.servers) { + const result = await this.connectWithRetry(server, undefined, interactive); + results.push(result); } + + return results; } + async refresh(): Promise { const cfg = getMCPConfig(); + if (!cfg.enabled || this.servers.length === 0) { return; } - - // Clear previously registered tools (ToolRegistry will overwrite on re-registration) + this.registeredTools = []; - const allow = new Set(cfg.toolAllowlist || []); + // Track tool names across all servers for conflict detection + const toolNameRegistry = new Map(); + const allServerTools: Array<{ srv: RegistryServer; def: MCPToolDef }> = []; + + // First pass: collect all tools from all servers for (const srv of this.servers) { - if (!this.client.isConnected(srv.id)) { + const isConnected = this.client.isConnected(srv.id); + + if (!isConnected) { continue; } + let tools: MCPToolDef[] = []; try { tools = await this.client.listTools(srv.id); - } catch (err) { - this.setError(err); - logger.error('listTools failed', err); + } catch (error) { + this.setError(error); + logger.error('MCPRegistry: listTools failed', { serverId: srv.id, error }); continue; } for (const def of tools) { - const namespaced = `mcp:${srv.id}:${def.name}`; - // Create or reuse a stable sanitized mapping for LLM function names - ToolNameMap.addMapping(namespaced); - if (allow.size > 0 && !allow.has(namespaced) && !allow.has(def.name)) { - continue; + allServerTools.push({ srv, def }); + + // Track tool name occurrences + if (toolNameRegistry.has(def.name)) { + const existing = toolNameRegistry.get(def.name)!; + existing.count++; + } else { + toolNameRegistry.set(def.name, { serverId: srv.id, originalName: def.name, count: 1 }); } - try { - const factoryName = namespaced; - ToolRegistry.registerToolFactory(factoryName, () => new MCPToolAdapter(srv.id, this.client, def, namespaced)); - this.registeredTools.push(factoryName); - } catch (err) { - logger.error('Failed to register MCP tool', { tool: def.name, err }); + } + } + + // Second pass: register tools with smart naming + const usedNames = new Map(); // Track which suffix numbers are used + + for (const { srv, def } of allServerTools) { + // Generate smart tool name + let toolName = def.name; + const toolInfo = toolNameRegistry.get(def.name)!; + + // If there are multiple tools with the same name, add numeric suffix + if (toolInfo.count > 1) { + // Get next available suffix for this tool name + const baseName = def.name; + const currentCount = usedNames.get(baseName) || 1; + + if (toolInfo.serverId === srv.id && currentCount === 1) { + // First occurrence gets no suffix + toolName = baseName; + } else { + // Subsequent occurrences get numbered suffix + const suffix = currentCount + 1; + toolName = `${baseName}_${suffix}`; } + + usedNames.set(baseName, currentCount + 1); + } + + // Create namespaced name for internal tracking but use smart name for registration + const namespacedName = `mcp:${srv.id}:${def.name}`; + ToolNameMap.addMapping(namespacedName); + ToolNameMap.addMapping(toolName); // Also map the smart name + + // Check allowlist using both names + if (allow.size > 0 && !allow.has(namespacedName) && !allow.has(def.name) && !allow.has(toolName)) { + continue; } + + try { + const factoryName = toolName; // Use smart name as factory name + ToolRegistry.registerToolFactory(factoryName, () => new MCPToolAdapter(srv.id, this.client, def, namespacedName)); + this.registeredTools.push(factoryName); + } catch (error) { + logger.error('MCPRegistry: Failed to register MCP tool', { tool: def.name, smartName: toolName, error }); + } + } + + if (allServerTools.length > 0 && this.registeredTools.length === 0) { + logger.warn('MCPRegistry: Found tools but none were registered - check allowlist configuration', { + foundTools: allServerTools.map(t => t.def.name), + allowlist: Array.from(allow) + }); + } + } + + async reconnect(serverId: string): Promise { + const server = this.servers.find(srv => srv.id === serverId); + if (!server) { + throw new Error(`Unknown MCP server: ${serverId}`); + } + + try { + this.client.disconnect(serverId); + } catch (error) { + logger.debug('Error disconnecting MCP server before reconnect', { serverId, error }); + } + + try { + await this.client.connect({ ...server, interactive: true }); + this.lastConnected = new Date(); + this.lastError = undefined; + this.lastErrorType = undefined; + + // Clear stored authentication errors on successful reconnection + this.clearStoredAuthErrorForServer(serverId); + + await this.refresh(); + } catch (error) { + this.setError(error); + logger.error('Failed to reconnect MCP server', { serverId, error }); + throw error; } } dispose(): void { for (const srv of this.servers) { - try { this.client.disconnect(srv.id); } catch {} + try { + this.client.disconnect(srv.id); + // Track disconnection + this.trackConnectionEvent({ + serverId: srv.id, + eventType: 'disconnected', + details: 'Manual disconnect', + }); + } catch { + // ignore errors during cleanup + } } this.lastDisconnected = new Date(); this.servers = []; } + async ensureToolsRegistered(): Promise { + // Auto-refresh if no tools are registered but servers are configured + if (this.registeredTools.length === 0 && this.servers.length > 0) { + try { + await this.refresh(); + } catch (error) { + logger.error('MCPRegistry: Auto-refresh failed', { error }); + } + } + } + getStatus(): MCPRegistryStatus { return { enabled: getMCPConfig().enabled, servers: this.servers.map(s => ({ id: s.id, + name: s.name, endpoint: s.endpoint, + authType: s.authType, connected: this.client.isConnected(s.id), - toolCount: 0, + toolCount: (() => { + // Count tools for this server by checking if each registered tool belongs to this server + let count = 0; + for (const toolName of this.registeredTools) { + try { + const tool = ToolRegistry.getRegisteredTool(toolName); + if (tool && tool instanceof MCPToolAdapter && tool.getServerId() === s.id) { + count++; + } + } catch (error) { + // Ignore tool registry errors + } + } + return count; + })(), })), registeredToolNames: [...this.registeredTools], lastError: this.lastError, lastErrorType: this.lastErrorType, lastConnected: this.lastConnected, lastDisconnected: this.lastDisconnected, + connectionEvents: this.getConnectionEvents(), }; } - getSanitizedFunctionName(original: string): string { return ToolNameMap.getSanitized(original); } + getSanitizedFunctionName(original: string): string { + return ToolNameMap.getSanitized(original); + } - resolveOriginalFunctionName(sanitized: string): string | undefined { return ToolNameMap.resolveOriginal(sanitized); } + resolveOriginalFunctionName(sanitized: string): string | undefined { + return ToolNameMap.resolveOriginal(sanitized); + } } export const MCPRegistry = new RegistryImpl(); diff --git a/front_end/panels/ai_chat/mcp/MCPToolAdapter.ts b/front_end/panels/ai_chat/mcp/MCPToolAdapter.ts index 5c83debee00..c14bb93c048 100644 --- a/front_end/panels/ai_chat/mcp/MCPToolAdapter.ts +++ b/front_end/panels/ai_chat/mcp/MCPToolAdapter.ts @@ -1,6 +1,6 @@ import type { Tool } from '../tools/Tools.js'; import { createLogger } from '../core/Logger.js'; -import type { MCPClient, MCPToolDef } from '../../../third_party/mcp-sdk/mcp-sdk.js'; +import type { MCPClient, MCPToolDef } from '../../../third_party/mcp-sdk/mcp-sdk-v2.js'; const logger = createLogger('MCPToolAdapter'); diff --git a/front_end/panels/ai_chat/mcp/MCPClientSDK.test.ts b/front_end/panels/ai_chat/mcp/__tests__/MCPClientSDK.test.ts similarity index 93% rename from front_end/panels/ai_chat/mcp/MCPClientSDK.test.ts rename to front_end/panels/ai_chat/mcp/__tests__/MCPClientSDK.test.ts index 7298942d783..eb7efb0e459 100644 --- a/front_end/panels/ai_chat/mcp/MCPClientSDK.test.ts +++ b/front_end/panels/ai_chat/mcp/__tests__/MCPClientSDK.test.ts @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import { MCPClientSDK } from '../../../third_party/mcp-sdk/mcp-sdk.js'; -import type { MCPServer } from '../../../third_party/mcp-sdk/mcp-sdk.js'; +import { MCPClientSDKv2 } from '../../../../third_party/mcp-sdk/mcp-sdk-v2.js'; +import type { MCPServer } from '../../../../third_party/mcp-sdk/mcp-sdk-v2.js'; describe('MCPClientSDK', () => { - let client: MCPClientSDK; + let client: MCPClientSDKv2; beforeEach(() => { - client = new MCPClientSDK(); + client = new MCPClientSDKv2(); }); afterEach(() => { @@ -22,7 +22,7 @@ describe('MCPClientSDK', () => { }); it('can be instantiated', () => { - assert.ok(client instanceof MCPClientSDK); + assert.ok(client instanceof MCPClientSDKv2); }); it('reports not connected initially', () => { @@ -55,7 +55,7 @@ describe('MCPClientSDK', () => { const HACKER_NEWS_SERVER: MCPServer = { id: 'local-hn-sdk', - endpoint: 'http://localhost:5001/sse', + endpoint: 'http://localhost:5001/mcp', }; it('connects and lists tools', async function() { @@ -133,4 +133,4 @@ describe('MCPClientSDK', () => { } }); }); -}); \ No newline at end of file +}); diff --git a/front_end/panels/ai_chat/mcp/__tests__/MCPConfig.test.ts b/front_end/panels/ai_chat/mcp/__tests__/MCPConfig.test.ts new file mode 100644 index 00000000000..a5f223a74b6 --- /dev/null +++ b/front_end/panels/ai_chat/mcp/__tests__/MCPConfig.test.ts @@ -0,0 +1,173 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import { generateMCPProviderId, saveMCPProviders, cleanupOAuthData, getMCPProviders, type MCPProviderConfig } from '../MCPConfig.js'; + + +class MemoryStorage { + private readonly store = new Map(); + + getItem(key: string): string | null { + return this.store.has(key) ? this.store.get(key)! : null; + } + + setItem(key: string, value: string): void { + this.store.set(key, value); + } + + removeItem(key: string): void { + this.store.delete(key); + } + + clear(): void { + this.store.clear(); + } +} + +describe('MCPConfig ID generation', () => { + let localStorageStub: MemoryStorage; + let sessionStorageStub: MemoryStorage; + + beforeEach(() => { + localStorageStub = new MemoryStorage(); + sessionStorageStub = new MemoryStorage(); + + Object.defineProperty(window, 'localStorage', { + value: localStorageStub, + configurable: true, + }); + Object.defineProperty(window, 'sessionStorage', { + value: sessionStorageStub, + configurable: true, + }); + }); + + afterEach(() => { + localStorageStub.clear(); + sessionStorageStub.clear(); + }); + + describe('generateMCPProviderId', () => { + it('prefers explicit IDs and sanitizes them', () => { + const id = generateMCPProviderId({ id: ' Custom-ID ' }); + assert.strictEqual(id, 'mcp-custom-id'); + }); + + it('derives IDs from provider names', () => { + const id = generateMCPProviderId({ name: ' OpenRouter ++ ' }); + assert.strictEqual(id, 'mcp-openrouter'); + }); + + it('derives IDs from subdomain hosts by using the domain base', () => { + const id = generateMCPProviderId({ endpoint: 'https://mcp.notion.com/mcp' }); + assert.strictEqual(id, 'mcp-notion'); + }); + + it('handles common country-code second-level domains', () => { + const id = generateMCPProviderId({ endpoint: 'https://api.tools.co.uk/service' }); + assert.strictEqual(id, 'mcp-api'); + }); + + it('falls back to sanitized endpoint when URL parsing fails', () => { + const id = generateMCPProviderId({ endpoint: 'invalid host value' }); + assert.strictEqual(id.startsWith('mcp-'), true); + }); + }); + + describe('saveMCPProviders duplicate detection', () => { + it('throws when two providers resolve to the same derived ID', () => { + const providers: MCPProviderConfig[] = [ + { + id: '', + name: 'Notion', + endpoint: 'https://mcp.notion.com/api', + authType: 'oauth', + enabled: true, + }, + { + id: '', + name: undefined, + endpoint: 'https://another.notion.com/v1', + authType: 'oauth', + enabled: true, + }, + ]; + + assert.throws(() => saveMCPProviders(providers), /Duplicate MCP connection identifier: mcp-notion/); + }); + }); + + describe('OAuth cleanup', () => { + it('cleanupOAuthData removes all OAuth-related localStorage keys', () => { + const serverId = 'mcp-test'; + const prefix = `mcp_oauth:${serverId}:`; + + // Set up some OAuth data in localStorage + localStorageStub.setItem(`${prefix}tokens`, '{"access_token":"test"}'); + localStorageStub.setItem(`${prefix}client_info`, '{"client_id":"test"}'); + localStorageStub.setItem(`${prefix}last_auth_error`, 'test error'); + localStorageStub.setItem(`${prefix}auth_error_timestamp`, '123456789'); + + // Verify data exists + assert.strictEqual(localStorageStub.getItem(`${prefix}tokens`), '{"access_token":"test"}'); + assert.strictEqual(localStorageStub.getItem(`${prefix}client_info`), '{"client_id":"test"}'); + + // Clean up OAuth data + cleanupOAuthData(serverId); + + // Verify all OAuth data is removed + assert.strictEqual(localStorageStub.getItem(`${prefix}tokens`), null); + assert.strictEqual(localStorageStub.getItem(`${prefix}client_info`), null); + assert.strictEqual(localStorageStub.getItem(`${prefix}last_auth_error`), null); + assert.strictEqual(localStorageStub.getItem(`${prefix}auth_error_timestamp`), null); + }); + + it('saveMCPProviders cleans up OAuth data for removed providers', () => { + // Set up existing providers + const existingProviders: MCPProviderConfig[] = [ + { + id: 'mcp-provider1', + endpoint: 'https://api1.example.com', + authType: 'oauth', + enabled: true, + }, + { + id: 'mcp-provider2', + endpoint: 'https://api2.example.com', + authType: 'oauth', + enabled: true, + }, + ]; + + // Save existing providers to set up state + saveMCPProviders(existingProviders); + + // Add OAuth data for both providers + localStorageStub.setItem('mcp_oauth:mcp-provider1:tokens', '{"access_token":"token1"}'); + localStorageStub.setItem('mcp_oauth:mcp-provider2:tokens', '{"access_token":"token2"}'); + + // Verify OAuth data exists + assert.strictEqual(localStorageStub.getItem('mcp_oauth:mcp-provider1:tokens'), '{"access_token":"token1"}'); + assert.strictEqual(localStorageStub.getItem('mcp_oauth:mcp-provider2:tokens'), '{"access_token":"token2"}'); + + // Save providers with only provider1 (remove provider2) + const newProviders: MCPProviderConfig[] = [ + { + id: 'mcp-provider1', + endpoint: 'https://api1.example.com', + authType: 'oauth', + enabled: true, + }, + ]; + + saveMCPProviders(newProviders); + + // Verify provider1's OAuth data still exists + assert.strictEqual(localStorageStub.getItem('mcp_oauth:mcp-provider1:tokens'), '{"access_token":"token1"}'); + + // Verify provider2's OAuth data was cleaned up + assert.strictEqual(localStorageStub.getItem('mcp_oauth:mcp-provider2:tokens'), null); + }); + }); +}); diff --git a/front_end/panels/ai_chat/mcp/__tests__/MCPIntegration.test.ts b/front_end/panels/ai_chat/mcp/__tests__/MCPIntegration.test.ts new file mode 100644 index 00000000000..d705f3e4516 --- /dev/null +++ b/front_end/panels/ai_chat/mcp/__tests__/MCPIntegration.test.ts @@ -0,0 +1,318 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import { MCPRegistry } from '../MCPRegistry.js'; +import { createToolExecutorNode } from '../../core/AgentNodes.js'; +import * as ToolNameMap from '../../core/ToolNameMap.js'; +import { ToolRegistry } from '../../agent_framework/ConfigurableAgentTool.js'; +import type { AgentState } from '../../core/State.js'; +import { ChatMessageEntity } from '../../models/ChatTypes.js'; + +/* eslint-env mocha */ + +// Mock MCP client and components +const mockMCPClient = { + isConnected: (serverId: string) => true, + listTools: (serverId: string) => Promise.resolve([ + { + name: 'search', + description: 'Search for content', + inputSchema: { + type: 'object', + properties: { + query: { type: 'string' } + } + } + }, + { + name: 'notion_find_page_by_title', + description: 'Find Notion page by title', + inputSchema: { + type: 'object', + properties: { + title: { type: 'string' } + } + } + } + ]), + call: () => Promise.resolve({ success: true }), + connect: () => Promise.resolve(), + disconnect: () => {}, +}; + +// Mock MCPConfig +const mockMCPConfig = { + enabled: true, + providers: [ + { + id: 'mcp-lusl1248if', + name: 'Test MCP Server', + endpoint: 'test://server', + enabled: true, + authType: 'bearer' as const, + } + ], + toolAllowlist: [], + autostart: true, +}; + +describe('MCP Integration Test', () => { + beforeEach(() => { + ToolNameMap.clear(); + // Mock the config + (globalThis as any).getMCPConfig = () => mockMCPConfig; + }); + + afterEach(() => { + ToolNameMap.clear(); + MCPRegistry.dispose(); + }); + + describe('Full MCP tool lifecycle', () => { + it('should register MCP tools and resolve them correctly', async () => { + console.log('Integration Test - Full MCP Lifecycle'); + + // Step 1: Initialize MCP Registry with mock client + console.log('Step 1: Initialize MCP Registry'); + + // Replace the internal client with our mock + (MCPRegistry as any).client = mockMCPClient; + (MCPRegistry as any).servers = mockMCPConfig.providers.map(p => ({ + id: p.id, + name: p.name, + endpoint: p.endpoint, + authType: p.authType, + })); + + // Step 2: Refresh to register tools + console.log('Step 2: Refresh registry to register tools'); + await MCPRegistry.refresh(); + + const status = MCPRegistry.getStatus(); + console.log(' Registry status:', { + enabled: status.enabled, + serverCount: status.servers.length, + toolCount: status.registeredToolNames.length, + tools: status.registeredToolNames, + }); + + // Verify tools were registered with smart names + assert.isTrue(status.registeredToolNames.length > 0, 'Should register some tools'); + + // Should have smart names, not long namespaced names + const hasShortNames = status.registeredToolNames.some(name => !name.includes('mcp:')); + console.log(' Has short names:', hasShortNames); + console.log(' Registered tools:', status.registeredToolNames); + + // Step 3: Simulate tool execution request + console.log('Step 3: Test tool resolution and execution'); + + // Try both namespaced and smart names + const testCases = [ + { + name: 'Smart name request', + toolName: 'search', // Simple smart name + }, + { + name: 'Namespaced name request', + toolName: 'mcp:mcp-lusl1248if:search', // Full namespaced name + }, + { + name: 'Sanitized name request', + toolName: ToolNameMap.getSanitized('mcp:mcp-lusl1248if:search'), // Sanitized version + } + ]; + + for (const testCase of testCases) { + console.log(`\n Testing: ${testCase.name}`); + console.log(` Requesting tool: ${testCase.toolName}`); + + const state: AgentState = { + messages: [ + { + entity: ChatMessageEntity.USER, + text: 'Test message', + }, + { + entity: ChatMessageEntity.MODEL, + action: 'tool', + toolName: testCase.toolName, + toolArgs: { query: 'test' }, + toolCallId: `test-${Date.now()}`, + isFinalAnswer: false, + } + ], + selectedAgentType: 'test', + context: { + selectedToolNames: status.registeredToolNames, // Use all registered tools + } as any, + }; + + try { + const toolExecutor = createToolExecutorNode(state, 'openai', 'gpt-4'); + const result = await toolExecutor.invoke(state); + + console.log(` ✓ SUCCESS: ${testCase.name} worked`); + console.log(` Result messages: ${result.messages.length}`); + + // Verify tool result was added + const lastMessage = result.messages[result.messages.length - 1]; + assert.strictEqual(lastMessage.entity, ChatMessageEntity.TOOL_RESULT); + + } catch (error) { + console.log(` ✗ FAILED: ${testCase.name} failed with: ${error.message}`); + + // Don't fail the test, just log the issue + console.log(` This reveals the resolution issue for: ${testCase.toolName}`); + } + } + }); + + it('should demonstrate the exact error from logs', async () => { + console.log('\nDemonstration of Exact Error from Logs'); + + // Setup registry + (MCPRegistry as any).client = mockMCPClient; + (MCPRegistry as any).servers = [{ + id: 'mcp-lusl1248if', + name: 'Test Server', + endpoint: 'test://server', + authType: 'bearer', + }]; + + await MCPRegistry.refresh(); + const status = MCPRegistry.getStatus(); + + console.log(' Available tools:', status.registeredToolNames); + + // The exact error: Tool mcp:mcp-lusl1248if:search not found + const problematicToolName = 'mcp:mcp-lusl1248if:search'; + console.log(' Problematic request:', problematicToolName); + + // Check what we actually have + const hasExactMatch = status.registeredToolNames.includes(problematicToolName); + const hasSmartName = status.registeredToolNames.includes('search'); + + console.log(' Has exact match:', hasExactMatch); + console.log(' Has smart name:', hasSmartName); + + // Check ToolNameMap + const sanitized = ToolNameMap.getSanitized(problematicToolName); + const resolved = ToolNameMap.resolveOriginal(sanitized); + + console.log(' Sanitized version:', sanitized); + console.log(' Resolves back to:', resolved); + + // Check ToolRegistry + const toolByExact = ToolRegistry.getRegisteredTool(problematicToolName as any); + const toolBySmartName = ToolRegistry.getRegisteredTool('search' as any); + const toolBySanitized = ToolRegistry.getRegisteredTool(sanitized as any); + + console.log(' ToolRegistry results:'); + console.log(' By exact name:', !!toolByExact); + console.log(' By smart name:', !!toolBySmartName); + console.log(' By sanitized:', !!toolBySanitized); + + // This should reveal exactly what the mismatch is + if (!toolByExact && toolBySmartName) { + console.log(' ROOT CAUSE: Tool registered with smart name but requested with namespaced name'); + console.log(' SOLUTION: Need proper name mapping in message flow'); + } + + // Try to resolve with our enhanced logic + const state: AgentState = { + messages: [ + { + entity: ChatMessageEntity.MODEL, + action: 'tool', + toolName: problematicToolName, + toolArgs: {}, + toolCallId: 'test-exact-error', + isFinalAnswer: false, + } + ], + selectedAgentType: 'test', + context: { + selectedToolNames: status.registeredToolNames, + } as any, + }; + + try { + const toolExecutor = createToolExecutorNode(state, 'openai', 'gpt-4'); + const result = await toolExecutor.invoke(state); + + console.log(' FIXED: Our enhanced resolution worked!'); + assert.isTrue(result.messages.length > 0); + + } catch (error) { + console.log(' STILL FAILING:', error.message); + console.log(' Need to debug the enhanced resolution logic'); + + // Don't fail the test, this is diagnostic + console.log(' Available tools in toolMap should be logged above'); + } + }); + }); + + describe('Smart naming validation', () => { + it('should validate smart naming produces clean tool names', () => { + console.log('\nSmart Naming Validation'); + + const testTools = [ + { server: 'mcp-short123', tool: 'search', expected: 'search' }, + { server: 'mcp-short123', tool: 'notion_create', expected: 'notion_create' }, + { server: 'mcp-other456', tool: 'search', expected: 'search_2' }, // Conflict + ]; + + console.log(' Testing smart naming logic:'); + + // Simulate the smart naming process + const toolNameRegistry = new Map(); + const results: string[] = []; + + // First pass: count occurrences + for (const test of testTools) { + if (toolNameRegistry.has(test.tool)) { + const existing = toolNameRegistry.get(test.tool)!; + existing.count++; + } else { + toolNameRegistry.set(test.tool, { + serverId: test.server, + originalName: test.tool, + count: 1 + }); + } + } + + // Second pass: assign names + const usedNames = new Map(); + + for (const test of testTools) { + let toolName = test.tool; + const toolInfo = toolNameRegistry.get(test.tool)!; + + if (toolInfo.count > 1) { + const baseName = test.tool; + const currentCount = usedNames.get(baseName) || 1; + + if (toolInfo.serverId === test.server && currentCount === 1) { + toolName = baseName; + } else { + const suffix = currentCount + 1; + toolName = `${baseName}_${suffix}`; + } + + usedNames.set(baseName, currentCount + 1); + } + + results.push(toolName); + + console.log(` ${test.server}:${test.tool} -> ${toolName} (expected: ${test.expected})`); + assert.strictEqual(toolName, test.expected, `Smart naming failed for ${test.server}:${test.tool}`); + } + + console.log(' ✓ Smart naming validation passed'); + }); + }); +}); \ No newline at end of file diff --git a/front_end/panels/ai_chat/mcp/__tests__/MCPToolRegistration.test.ts b/front_end/panels/ai_chat/mcp/__tests__/MCPToolRegistration.test.ts new file mode 100644 index 00000000000..1aa36804c6b --- /dev/null +++ b/front_end/panels/ai_chat/mcp/__tests__/MCPToolRegistration.test.ts @@ -0,0 +1,195 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import { ToolRegistry } from '../../agent_framework/ConfigurableAgentTool.js'; +import * as ToolNameMap from '../../core/ToolNameMap.js'; +import { MCPToolAdapter } from '../MCPToolAdapter.js'; + +/* eslint-env mocha */ + +// Mock the MCP client and tool definition +const mockMCPClient = { + call: () => Promise.resolve({}), + isConnected: () => true, + listTools: () => Promise.resolve([]), + disconnect: () => {}, +}; + +const createMockToolDef = (name: string) => ({ + name, + description: `Test tool ${name}`, + inputSchema: { + type: 'object', + properties: {}, + }, +}); + +describe('MCP Tool Registration', () => { + beforeEach(() => { + ToolNameMap.clear(); + // Clear any existing MCP tools from registry + // Note: ToolRegistry doesn't have an unregister method, + // so we'll just track what we register in tests + }); + + afterEach(() => { + ToolNameMap.clear(); + }); + + describe('Smart naming for MCP tools', () => { + it('should register MCP tools with smart names (no conflicts)', () => { + const serverId = 'mcp-test123'; + const toolDef = createMockToolDef('search'); + const namespacedName = `mcp:${serverId}:${toolDef.name}`; + + // Simulate what MCPRegistry does with smart naming + const smartName = toolDef.name; // No conflicts, so use simple name + + console.log('Test 1 - Smart naming without conflicts:'); + console.log(' Server ID:', serverId); + console.log(' Tool name:', toolDef.name); + console.log(' Namespaced name:', namespacedName); + console.log(' Smart name:', smartName); + + // Add mappings like MCPRegistry would + ToolNameMap.addMapping(namespacedName); + ToolNameMap.addMapping(smartName); + + // Register tool with smart name + ToolRegistry.registerToolFactory(smartName, () => new MCPToolAdapter(serverId, mockMCPClient as any, toolDef, namespacedName)); + + // Verify tool is registered with smart name + const registeredTool = ToolRegistry.getRegisteredTool(smartName as any); + console.log(' Tool registered:', !!registeredTool); + assert.isNotNull(registeredTool, 'Tool should be registered with smart name'); + + // Verify name mappings work + const sanitizedNamespaced = ToolNameMap.getSanitized(namespacedName); + const resolvedFromSanitized = ToolNameMap.resolveOriginal(sanitizedNamespaced); + + console.log(' Namespaced sanitized:', sanitizedNamespaced); + console.log(' Resolved from sanitized:', resolvedFromSanitized); + + assert.strictEqual(resolvedFromSanitized, namespacedName); + }); + + it('should handle conflicts with numeric suffixes', () => { + const server1Id = 'mcp-server1'; + const server2Id = 'mcp-server2'; + const toolName = 'search'; + + const namespacedName1 = `mcp:${server1Id}:${toolName}`; + const namespacedName2 = `mcp:${server2Id}:${toolName}`; + + // Simulate conflict resolution + const smartName1 = toolName; // First occurrence gets simple name + const smartName2 = `${toolName}_2`; // Second occurrence gets suffix + + console.log('Test 2 - Smart naming with conflicts:'); + console.log(' Server 1:', server1Id, 'Tool:', toolName, 'Smart name:', smartName1); + console.log(' Server 2:', server2Id, 'Tool:', toolName, 'Smart name:', smartName2); + + // Add mappings + ToolNameMap.addMapping(namespacedName1); + ToolNameMap.addMapping(smartName1); + ToolNameMap.addMapping(namespacedName2); + ToolNameMap.addMapping(smartName2); + + // Register both tools + const toolDef = createMockToolDef(toolName); + ToolRegistry.registerToolFactory(smartName1, () => new MCPToolAdapter(server1Id, mockMCPClient as any, toolDef, namespacedName1)); + ToolRegistry.registerToolFactory(smartName2, () => new MCPToolAdapter(server2Id, mockMCPClient as any, toolDef, namespacedName2)); + + // Verify both tools are registered with different names + const tool1 = ToolRegistry.getRegisteredTool(smartName1 as any); + const tool2 = ToolRegistry.getRegisteredTool(smartName2 as any); + + console.log(' Tool1 registered:', !!tool1); + console.log(' Tool2 registered:', !!tool2); + + assert.isNotNull(tool1, 'First tool should be registered'); + assert.isNotNull(tool2, 'Second tool should be registered'); + }); + + it('should handle the exact error case from logs', () => { + const serverId = 'mcp-lusl1248if'; + const toolName = 'search'; + const namespacedName = `mcp:${serverId}:${toolName}`; + const smartName = toolName; + + console.log('Test 3 - Exact error case:'); + console.log(' Requested in error:', namespacedName); + console.log(' Server ID:', serverId); + console.log(' Tool name:', toolName); + console.log(' Expected smart name:', smartName); + + // Add mappings + ToolNameMap.addMapping(namespacedName); + ToolNameMap.addMapping(smartName); + + // Register with smart name (like our new logic should do) + const toolDef = createMockToolDef(toolName); + ToolRegistry.registerToolFactory(smartName, () => new MCPToolAdapter(serverId, mockMCPClient as any, toolDef, namespacedName)); + + // Now test resolution - what happens when we try to find the tool? + console.log(' --- Resolution test ---'); + + // Try to find by namespaced name (what the error shows) + const toolByNamespaced = ToolRegistry.getRegisteredTool(namespacedName as any); + console.log(' Find by namespaced name:', !!toolByNamespaced); + + // Try to find by smart name (what should work) + const toolBySmart = ToolRegistry.getRegisteredTool(smartName as any); + console.log(' Find by smart name:', !!toolBySmart); + + // Try sanitized version + const sanitized = ToolNameMap.getSanitized(namespacedName); + const toolBySanitized = ToolRegistry.getRegisteredTool(sanitized as any); + console.log(' Sanitized version:', sanitized); + console.log(' Find by sanitized:', !!toolBySanitized); + + // The issue: tool is registered with smart name but being requested by namespaced name + assert.isNull(toolByNamespaced, 'Tool should NOT be found by namespaced name'); + assert.isNotNull(toolBySmart, 'Tool SHOULD be found by smart name'); + + console.log(' ISSUE IDENTIFIED: Tool registered as "' + smartName + '" but requested as "' + namespacedName + '"'); + }); + }); + + describe('Name mapping verification', () => { + it('should verify ToolNameMap handles MCP names correctly', () => { + const testCases = [ + { + original: 'mcp:mcp-lusl1248if:search', + expectedSanitized: 'mcp_mcp-lusl1248if_search' + }, + { + original: 'mcp:80a92942-4bf9-4c02-ab11-422151bec3a2:notion_find_page_by_title', + expectedSanitized: 'mcp_80a92942-4bf9-4c02-ab11-422151bec3a2_notion_find_page_by_title' + }, + { + original: 'search', + expectedSanitized: 'search' + } + ]; + + console.log('Test 4 - Name mapping verification:'); + + testCases.forEach((testCase, index) => { + const sanitized = ToolNameMap.getSanitized(testCase.original); + const resolved = ToolNameMap.resolveOriginal(sanitized); + + console.log(` Case ${index + 1}:`); + console.log(` Original: ${testCase.original}`); + console.log(` Sanitized: ${sanitized}`); + console.log(` Expected: ${testCase.expectedSanitized}`); + console.log(` Resolved: ${resolved}`); + console.log(` Bidirectional: ${resolved === testCase.original}`); + + assert.strictEqual(sanitized, testCase.expectedSanitized, `Sanitization failed for ${testCase.original}`); + assert.strictEqual(resolved, testCase.original, `Resolution failed for ${testCase.original}`); + }); + }); + }); +}); \ No newline at end of file diff --git a/front_end/panels/ai_chat/tools/FetcherTool.ts b/front_end/panels/ai_chat/tools/FetcherTool.ts index 466c159bc17..2a85897d394 100644 --- a/front_end/panels/ai_chat/tools/FetcherTool.ts +++ b/front_end/panels/ai_chat/tools/FetcherTool.ts @@ -75,6 +75,13 @@ export class FetcherTool implements Tool { async execute(args: FetcherToolArgs, ctx?: LLMContext): Promise { logger.info('Executing with args', { args }); const { urls, reasoning } = args; + const signal = ctx?.abortSignal; + + const throwIfAborted = () => { + if (signal?.aborted) { + throw new DOMException('The operation was aborted', 'AbortError'); + } + }; // Validate input if (!Array.isArray(urls) || urls.length === 0) { @@ -91,6 +98,7 @@ export class FetcherTool implements Tool { // Process each URL sequentially for (const url of urlsToProcess) { + throwIfAborted(); try { logger.info('Processing URL', { url }); const fetchedContent = await this.fetchContentFromUrl(url, reasoning, ctx); @@ -117,9 +125,39 @@ export class FetcherTool implements Tool { * Fetch and extract content from a single URL */ private async fetchContentFromUrl(url: string, reasoning: string, ctx?: LLMContext): Promise { + const signal = ctx?.abortSignal; + const throwIfAborted = () => { + if (signal?.aborted) { + throw new DOMException('The operation was aborted', 'AbortError'); + } + }; + const sleep = (ms: number) => new Promise((resolve, reject) => { + if (!ms) return resolve(); + const timer = setTimeout(() => { + cleanup(); + resolve(); + }, ms); + const onAbort = () => { + clearTimeout(timer); + cleanup(); + reject(new DOMException('The operation was aborted', 'AbortError')); + }; + const cleanup = () => { + signal?.removeEventListener('abort', onAbort); + }; + if (signal) { + if (signal.aborted) { + clearTimeout(timer); + cleanup(); + return reject(new DOMException('The operation was aborted', 'AbortError')); + } + signal.addEventListener('abort', onAbort, { once: true }); + } + }); try { // Step 1: Navigate to the URL logger.info('Navigating to URL', { url }); + throwIfAborted(); // Note: NavigateURLTool requires both url and reasoning parameters const navigationResult = await this.navigateURLTool.execute({ url, @@ -138,13 +176,15 @@ export class FetcherTool implements Tool { } // Wait for 1 second to ensure the page has time to load - await new Promise(resolve => setTimeout(resolve, 1000)); + await sleep(1000); + throwIfAborted(); // Get metadata from navigation result const metadata = navigationResult.metadata ? navigationResult.metadata : { url: '', title: '' }; // Step 2: Extract markdown content using HTMLToMarkdownTool logger.info('Extracting content from URL', { url }); + throwIfAborted(); const extractionResult = await this.htmlToMarkdownTool.execute({ instruction: 'Extract the main content focusing on article text, headings, and important information. Remove ads, navigation, and distracting elements.', reasoning diff --git a/front_end/panels/ai_chat/tools/SchemaBasedExtractorTool.ts b/front_end/panels/ai_chat/tools/SchemaBasedExtractorTool.ts index bc372fdaaef..8008d64eeec 100644 --- a/front_end/panels/ai_chat/tools/SchemaBasedExtractorTool.ts +++ b/front_end/panels/ai_chat/tools/SchemaBasedExtractorTool.ts @@ -36,7 +36,7 @@ export interface SchemaExtractionResult { * Tool for extracting structured data from DOM based on schema definitions */ export class SchemaBasedExtractorTool implements Tool { - name = 'extract_schema_data'; + name = 'extract_data'; description = `Extracts structured data from a web page's DOM using a user-provided JSON schema and natural language instruction. - The schema defines the exact structure and types of data to extract (e.g., text, numbers, URLs). - For fields representing URLs, specify them in the schema as: { type: 'string', format: 'url' }. diff --git a/front_end/panels/ai_chat/tools/StreamlinedSchemaExtractorTool.ts b/front_end/panels/ai_chat/tools/StreamlinedSchemaExtractorTool.ts index 04a2e9603c6..0cac2d20873 100644 --- a/front_end/panels/ai_chat/tools/StreamlinedSchemaExtractorTool.ts +++ b/front_end/panels/ai_chat/tools/StreamlinedSchemaExtractorTool.ts @@ -43,6 +43,23 @@ export class StreamlinedSchemaExtractorTool implements Tool { + return new Promise((resolve, reject) => { + if (!ms) return resolve(); + const timer = setTimeout(() => { cleanup(); resolve(); }, ms); + const onAbort = () => { clearTimeout(timer); cleanup(); reject(new DOMException('The operation was aborted', 'AbortError')); }; + const cleanup = () => { signal?.removeEventListener('abort', onAbort); }; + if (signal) { + if (signal.aborted) { + clearTimeout(timer); + cleanup(); + return reject(new DOMException('The operation was aborted', 'AbortError')); + } + signal.addEventListener('abort', onAbort, { once: true }); + } + }); + } + schema = { type: 'object', properties: { @@ -163,9 +180,9 @@ export class StreamlinedSchemaExtractorTool implements Tool 1) { - await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS)); + await this.sleep(this.RETRY_DELAY_MS, ctx?.abortSignal); } const retryResult = await this.retryUrlResolution( @@ -287,8 +304,8 @@ IMPORTANT: Only extract data that you can see in the accessibility tree above. D } catch (error) { if (attempt <= maxRetries) { logger.warn(`JSON parsing failed on attempt ${attempt}, retrying...`, error); - // Add delay before next attempt to prevent overloading the LLM - await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS)); + // Add delay before next attempt to prevent overloading the LLM (abortable) + await this.sleep(this.RETRY_DELAY_MS, ctx?.abortSignal); } else { logger.error(`JSON extraction failed after ${attempt} attempts:`, error instanceof Error ? error.message : String(error)); throw new Error(`Data extraction failed after ${attempt} attempts: ${error instanceof Error ? error.message : String(error)}`); diff --git a/front_end/panels/ai_chat/tools/Tools.ts b/front_end/panels/ai_chat/tools/Tools.ts index bb5f2a7ff31..bec5b113722 100644 --- a/front_end/panels/ai_chat/tools/Tools.ts +++ b/front_end/panels/ai_chat/tools/Tools.ts @@ -58,6 +58,7 @@ export interface LLMContext { getVisionCapability?: (model: string) => Promise | boolean; miniModel?: string; nanoModel?: string; + abortSignal?: AbortSignal; } /** @@ -889,7 +890,7 @@ export class NavigateBackTool implements Tool<{ steps: number, reasoning: string required: ['steps', 'reasoning'], }; - async execute(args: { steps: number, reasoning: string }, _ctx?: LLMContext): Promise { + async execute(args: { steps: number, reasoning: string }, ctx?: LLMContext): Promise { logger.error('navigate_back', args); const steps = args.steps; if (typeof steps !== 'number' || steps <= 0) { @@ -939,10 +940,14 @@ export class NavigateBackTool implements Tool<{ steps: number, reasoning: string const timeoutMs = 5000; // 5 second timeout let isNavigationComplete = false; - // Poll until navigation completes or times out + const signal = ctx?.abortSignal; + // Poll until navigation completes, cancels, or times out while (!isNavigationComplete && (Date.now() - startTime) < timeoutMs) { + if (signal?.aborted) { + throw new DOMException('The operation was aborted', 'AbortError'); + } // Short delay between checks - await new Promise(resolve => setTimeout(resolve, 100)); + await abortableSleep(100, signal); // Check if navigation is complete by testing document readyState try { @@ -1424,6 +1429,30 @@ export class WaitTool implements Tool<{ seconds?: number, duration?: number, rea description = 'Waits for a specified number of seconds to allow page content to load, animations to complete, or dynamic content to appear. After waiting, returns a summary of what is currently visible in the viewport to help determine if additional waiting is needed. Provide the number of seconds to wait and an optional reasoning for waiting.'; async execute(args: { seconds?: number, duration?: number, reason?: string, reasoning?: string }, ctx?: LLMContext): Promise { + const signal = ctx?.abortSignal; + const sleep = (ms: number) => new Promise((resolve, reject) => { + if (!ms) return resolve(); + const timer = setTimeout(() => { + cleanup(); + resolve(); + }, ms); + const onAbort = () => { + clearTimeout(timer); + cleanup(); + reject(new DOMException('The operation was aborted', 'AbortError')); + }; + const cleanup = () => { + signal?.removeEventListener('abort', onAbort); + }; + if (signal) { + if (signal.aborted) { + clearTimeout(timer); + cleanup(); + return reject(new DOMException('The operation was aborted', 'AbortError')); + } + signal.addEventListener('abort', onAbort, { once: true }); + } + }); // Handle both 'seconds' and 'duration' parameter names for flexibility const waitTime = args.seconds ?? args.duration; const waitReason = args.reason ?? args.reasoning; @@ -1444,8 +1473,8 @@ export class WaitTool implements Tool<{ seconds?: number, duration?: number, rea // Log the wait reason if provided logger.info(`Waiting for ${waitTime} seconds${waitReason ? `: ${waitReason}` : ''}`); - // Wait for the specified duration - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + // Wait for the specified duration (abortable) + await sleep(waitTime * 1000); // Get viewport summary after waiting let viewportSummary: string | undefined; @@ -1946,7 +1975,7 @@ export class PerformActionTool implements Tool<{ method: string, nodeId: number await Utils.performAction(target, method, actionArgsArray, xpath, iframeNodeId); // --- Wait for DOM to stabilize after action --- - await this.waitForDOMStability(target, method, isLikelyNavigationElement); + await this.waitForDOMStability(target, method, isLikelyNavigationElement, (ctx as LLMContext | undefined)?.abortSignal); // --- Capture tree state after action and generate diff --- try { @@ -2027,8 +2056,8 @@ export class PerformActionTool implements Tool<{ method: string, nodeId: number // Check for navigation after 'click' on relevant elements if (method === 'click' && isLikelyNavigationElement && initialUrl !== undefined) { logger.info('Checking for navigation after click'); - // Wait briefly for potential navigation. - await new Promise(resolve => setTimeout(resolve, 1000)); // 1 second wait + // Wait briefly for potential navigation (abortable) + await abortableSleep(1000, ctx?.abortSignal); const urlResult = await target.runtimeAgent().invoke_evaluate({ expression: 'window.location.href', @@ -2129,8 +2158,8 @@ Provide a clear, concise response about what happened.` } } else { try { - // Add some delay to allow UI to refresh - await new Promise(resolve => setTimeout(resolve, 300)); + // Add some delay to allow UI to refresh (abortable) + await abortableSleep(300, (ctx as LLMContext | undefined)?.abortSignal); // Take after screenshot const afterScreenshotResult = await target.pageAgent().invoke_captureScreenshot({ @@ -2383,7 +2412,7 @@ Provide a clear, descriptive response about what you observe and whether the act }; // DOM stability waiting method - private async waitForDOMStability(target: SDK.Target.Target, method: string, isLikelyNavigationElement: boolean): Promise { + private async waitForDOMStability(target: SDK.Target.Target, method: string, isLikelyNavigationElement: boolean, signal?: AbortSignal): Promise { const maxWaitTime = isLikelyNavigationElement ? 5000 : 2000; // 5s for navigation, 2s for other actions const startTime = Date.now(); @@ -2392,24 +2421,27 @@ Provide a clear, descriptive response about what you observe and whether the act try { // For navigation elements, wait for document ready state if (isLikelyNavigationElement) { - await this.waitForDocumentReady(target, maxWaitTime); + await this.waitForDocumentReady(target, maxWaitTime, signal); } // Wait for DOM mutations to settle using polling approach - await this.waitForDOMMutationStability(target, maxWaitTime - (Date.now() - startTime)); + await this.waitForDOMMutationStability(target, maxWaitTime - (Date.now() - startTime), signal); } catch (error) { logger.warn('Error waiting for DOM stability:', error); // Fallback to minimal wait - await new Promise(resolve => setTimeout(resolve, 300)); + await abortableSleep(300, signal); } } - private async waitForDocumentReady(target: SDK.Target.Target, maxWaitTime: number): Promise { + private async waitForDocumentReady(target: SDK.Target.Target, maxWaitTime: number, signal?: AbortSignal): Promise { const startTime = Date.now(); const pollInterval = 100; while (Date.now() - startTime < maxWaitTime) { + if (signal?.aborted) { + throw new DOMException('The operation was aborted', 'AbortError'); + } try { const readyStateResult = await target.runtimeAgent().invoke_evaluate({ expression: 'document.readyState', @@ -2421,7 +2453,7 @@ Provide a clear, descriptive response about what you observe and whether the act return; } - await new Promise(resolve => setTimeout(resolve, pollInterval)); + await abortableSleep(pollInterval, signal); } catch (error) { logger.warn('Error checking document ready state:', error); break; @@ -2429,7 +2461,7 @@ Provide a clear, descriptive response about what you observe and whether the act } } - private async waitForDOMMutationStability(target: SDK.Target.Target, maxWaitTime: number): Promise { + private async waitForDOMMutationStability(target: SDK.Target.Target, maxWaitTime: number, signal?: AbortSignal): Promise { const startTime = Date.now(); const stabilityWindow = 800; // Longer stability window for complex content const pollInterval = 100; @@ -2439,6 +2471,9 @@ Provide a clear, descriptive response about what you observe and whether the act const requiredStableChecks = 3; while (Date.now() - startTime < maxWaitTime) { + if (signal?.aborted) { + throw new DOMException('The operation was aborted', 'AbortError'); + } try { // Generic DOM stability detection const currentTreeResult = await target.runtimeAgent().invoke_evaluate({ @@ -2505,7 +2540,7 @@ Provide a clear, descriptive response about what you observe and whether the act } } - await new Promise(resolve => setTimeout(resolve, pollInterval)); + await abortableSleep(pollInterval, signal); } catch (error) { logger.warn('Error checking DOM stability:', error); break; @@ -4218,3 +4253,20 @@ export function getTools(): Array<( // Export the SequentialThinkingTool export { SequentialThinkingTool } from './SequentialThinkingTool.js'; +// Abortable sleep utility for tools that need delays/polling +function abortableSleep(ms: number, signal?: AbortSignal): Promise { + return new Promise((resolve, reject) => { + if (!ms) return resolve(); + const timer = setTimeout(() => { cleanup(); resolve(); }, ms); + const onAbort = () => { clearTimeout(timer); cleanup(); reject(new DOMException('The operation was aborted', 'AbortError')); }; + const cleanup = () => { signal?.removeEventListener('abort', onAbort); }; + if (signal) { + if (signal.aborted) { + clearTimeout(timer); + cleanup(); + return reject(new DOMException('The operation was aborted', 'AbortError')); + } + signal.addEventListener('abort', onAbort, { once: true }); + } + }); +} diff --git a/front_end/panels/ai_chat/ui/AIChatPanel.ts b/front_end/panels/ai_chat/ui/AIChatPanel.ts index 8f5d0b91982..b9394a0c8e6 100644 --- a/front_end/panels/ai_chat/ui/AIChatPanel.ts +++ b/front_end/panels/ai_chat/ui/AIChatPanel.ts @@ -90,6 +90,7 @@ import * as Snackbars from '../../../ui/components/snackbars/snackbars.js'; import { MCPRegistry } from '../mcp/MCPRegistry.js'; import { getMCPConfig } from '../mcp/MCPConfig.js'; import { onMCPConfigChange } from '../mcp/MCPConfig.js'; +import { MCPConnectorsCatalogDialog } from './mcp/MCPConnectorsCatalogDialog.js'; const {html} = Lit; @@ -162,6 +163,10 @@ const UIStrings = { *@description AI chat UI tooltip text for the help button. */ help: 'Help', + /** + *@description AI chat UI tooltip text for the MCP connectors catalog button. + */ + mcpConnectors: 'MCP Connectors', /** *@description AI chat UI tooltip text for the settings button (gear icon). */ @@ -216,6 +221,7 @@ interface ToolbarViewInput { onHistoryClick: (event: MouseEvent) => void; onDeleteClick: () => void; onHelpClick: () => void; + onMCPConnectorsClick: () => void; onSettingsClick: () => void; onEvaluationTestClick: () => void; onBookmarkClick: () => void; @@ -226,17 +232,18 @@ interface ToolbarViewInput { function toolbarView(input: ToolbarViewInput): Lit.LitTemplate { // clang-format off + // Add history button when history feature is implemented + // + //
return html`