apex-code 0.0.1-alpha.8 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +231 -0
- package/README.md +9 -8
- package/dist/cli/args.d.ts +26 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +72 -10
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/mcp-command.d.ts +9 -0
- package/dist/cli/mcp-command.d.ts.map +1 -0
- package/dist/cli/mcp-command.js +53 -0
- package/dist/cli/mcp-command.js.map +1 -0
- package/dist/cli/project-trust.d.ts.map +1 -1
- package/dist/cli/project-trust.js +3 -1
- package/dist/cli/project-trust.js.map +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +2 -1
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +94 -5
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +45 -18
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +3 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +183 -15
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +728 -117
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +6 -7
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +7 -0
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/checkpoints/git-checkpoints.d.ts +39 -0
- package/dist/core/checkpoints/git-checkpoints.d.ts.map +1 -0
- package/dist/core/checkpoints/git-checkpoints.js +251 -0
- package/dist/core/checkpoints/git-checkpoints.js.map +1 -0
- package/dist/core/checkpoints/session-checkpoints.d.ts +23 -0
- package/dist/core/checkpoints/session-checkpoints.d.ts.map +1 -0
- package/dist/core/checkpoints/session-checkpoints.js +36 -0
- package/dist/core/checkpoints/session-checkpoints.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +7 -3
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +9 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +59 -36
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js +9 -0
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/delegation/runtime.d.ts +6 -5
- package/dist/core/delegation/runtime.d.ts.map +1 -1
- package/dist/core/delegation/runtime.js +4 -3
- package/dist/core/delegation/runtime.js.map +1 -1
- package/dist/core/durable-state/sqlite.d.ts.map +1 -1
- package/dist/core/durable-state/sqlite.js +26 -2
- package/dist/core/durable-state/sqlite.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +5 -2
- package/dist/core/exec.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +123 -50
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +45 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/source-runtime.d.ts +14 -0
- package/dist/core/extensions/source-runtime.d.ts.map +1 -0
- package/dist/core/extensions/source-runtime.js +56 -0
- package/dist/core/extensions/source-runtime.js.map +1 -0
- package/dist/core/extensions/types.d.ts +60 -11
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +3 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/formatter-lifecycle.d.ts +69 -0
- package/dist/core/formatter-lifecycle.d.ts.map +1 -0
- package/dist/core/formatter-lifecycle.js +360 -0
- package/dist/core/formatter-lifecycle.js.map +1 -0
- package/dist/core/hooks/command-handler.d.ts +19 -0
- package/dist/core/hooks/command-handler.d.ts.map +1 -0
- package/dist/core/hooks/command-handler.js +112 -0
- package/dist/core/hooks/command-handler.js.map +1 -0
- package/dist/core/hooks/http-handler.d.ts +10 -0
- package/dist/core/hooks/http-handler.d.ts.map +1 -0
- package/dist/core/hooks/http-handler.js +36 -0
- package/dist/core/hooks/http-handler.js.map +1 -0
- package/dist/core/hooks/loader.d.ts +29 -0
- package/dist/core/hooks/loader.d.ts.map +1 -0
- package/dist/core/hooks/loader.js +139 -0
- package/dist/core/hooks/loader.js.map +1 -0
- package/dist/core/hooks/runtime.d.ts +13 -0
- package/dist/core/hooks/runtime.d.ts.map +1 -0
- package/dist/core/hooks/runtime.js +63 -0
- package/dist/core/hooks/runtime.js.map +1 -0
- package/dist/core/hooks/types.d.ts +83 -0
- package/dist/core/hooks/types.d.ts.map +1 -0
- package/dist/core/hooks/types.js +24 -0
- package/dist/core/hooks/types.js.map +1 -0
- package/dist/core/keybindings.d.ts +20 -19
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +26 -5
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/mcp/config.d.ts +16 -0
- package/dist/core/mcp/config.d.ts.map +1 -0
- package/dist/core/mcp/config.js +166 -0
- package/dist/core/mcp/config.js.map +1 -0
- package/dist/core/mcp/connector.d.ts +35 -0
- package/dist/core/mcp/connector.d.ts.map +1 -0
- package/dist/core/mcp/connector.js +105 -0
- package/dist/core/mcp/connector.js.map +1 -0
- package/dist/core/mcp/contract.d.ts +19 -0
- package/dist/core/mcp/contract.d.ts.map +1 -0
- package/dist/core/mcp/contract.js +89 -0
- package/dist/core/mcp/contract.js.map +1 -0
- package/dist/core/mcp/mcp-tool.d.ts +32 -0
- package/dist/core/mcp/mcp-tool.d.ts.map +1 -0
- package/dist/core/mcp/mcp-tool.js +171 -0
- package/dist/core/mcp/mcp-tool.js.map +1 -0
- package/dist/core/mcp/metadata-cache.d.ts +29 -0
- package/dist/core/mcp/metadata-cache.d.ts.map +1 -0
- package/dist/core/mcp/metadata-cache.js +99 -0
- package/dist/core/mcp/metadata-cache.js.map +1 -0
- package/dist/core/mcp/oauth/authorize.d.ts +21 -0
- package/dist/core/mcp/oauth/authorize.d.ts.map +1 -0
- package/dist/core/mcp/oauth/authorize.js +32 -0
- package/dist/core/mcp/oauth/authorize.js.map +1 -0
- package/dist/core/mcp/oauth/discover.d.ts +27 -0
- package/dist/core/mcp/oauth/discover.d.ts.map +1 -0
- package/dist/core/mcp/oauth/discover.js +116 -0
- package/dist/core/mcp/oauth/discover.js.map +1 -0
- package/dist/core/mcp/oauth/flow.d.ts +31 -0
- package/dist/core/mcp/oauth/flow.d.ts.map +1 -0
- package/dist/core/mcp/oauth/flow.js +175 -0
- package/dist/core/mcp/oauth/flow.js.map +1 -0
- package/dist/core/mcp/oauth/mcp-token.d.ts +51 -0
- package/dist/core/mcp/oauth/mcp-token.d.ts.map +1 -0
- package/dist/core/mcp/oauth/mcp-token.js +129 -0
- package/dist/core/mcp/oauth/mcp-token.js.map +1 -0
- package/dist/core/mcp/oauth/pkce.d.ts +12 -0
- package/dist/core/mcp/oauth/pkce.d.ts.map +1 -0
- package/dist/core/mcp/oauth/pkce.js +11 -0
- package/dist/core/mcp/oauth/pkce.js.map +1 -0
- package/dist/core/mcp/oauth/register.d.ts +16 -0
- package/dist/core/mcp/oauth/register.d.ts.map +1 -0
- package/dist/core/mcp/oauth/register.js +42 -0
- package/dist/core/mcp/oauth/register.js.map +1 -0
- package/dist/core/mcp/oauth/token-client.d.ts +33 -0
- package/dist/core/mcp/oauth/token-client.d.ts.map +1 -0
- package/dist/core/mcp/oauth/token-client.js +65 -0
- package/dist/core/mcp/oauth/token-client.js.map +1 -0
- package/dist/core/mcp/runtime.d.ts +20 -0
- package/dist/core/mcp/runtime.d.ts.map +1 -0
- package/dist/core/mcp/runtime.js +37 -0
- package/dist/core/mcp/runtime.js.map +1 -0
- package/dist/core/mcp/schema.d.ts +22 -0
- package/dist/core/mcp/schema.d.ts.map +1 -0
- package/dist/core/mcp/schema.js +20 -0
- package/dist/core/mcp/schema.js.map +1 -0
- package/dist/core/mcp/server-manager.d.ts +43 -0
- package/dist/core/mcp/server-manager.d.ts.map +1 -0
- package/dist/core/mcp/server-manager.js +142 -0
- package/dist/core/mcp/server-manager.js.map +1 -0
- package/dist/core/mcp/types.d.ts +100 -0
- package/dist/core/mcp/types.d.ts.map +1 -0
- package/dist/core/mcp/types.js +8 -0
- package/dist/core/mcp/types.js.map +1 -0
- package/dist/core/model-config.d.ts +5 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +3 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +14 -8
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +2 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +22 -14
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions/gate.d.ts.map +1 -1
- package/dist/core/permissions/gate.js +45 -4
- package/dist/core/permissions/gate.js.map +1 -1
- package/dist/core/permissions/operations.d.ts +20 -0
- package/dist/core/permissions/operations.d.ts.map +1 -0
- package/dist/core/permissions/operations.js +9 -0
- package/dist/core/permissions/operations.js.map +1 -0
- package/dist/core/permissions/policy-command.d.ts +42 -0
- package/dist/core/permissions/policy-command.d.ts.map +1 -0
- package/dist/core/permissions/policy-command.js +44 -0
- package/dist/core/permissions/policy-command.js.map +1 -0
- package/dist/core/permissions/responder.d.ts +49 -2
- package/dist/core/permissions/responder.d.ts.map +1 -1
- package/dist/core/permissions/responder.js +20 -8
- package/dist/core/permissions/responder.js.map +1 -1
- package/dist/core/permissions/rules.d.ts.map +1 -1
- package/dist/core/permissions/rules.js +11 -1
- package/dist/core/permissions/rules.js.map +1 -1
- package/dist/core/permissions/store.d.ts +18 -0
- package/dist/core/permissions/store.d.ts.map +1 -1
- package/dist/core/permissions/store.js +89 -8
- package/dist/core/permissions/store.js.map +1 -1
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js +2 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/policy-executor.d.ts +47 -0
- package/dist/core/policy-executor.d.ts.map +1 -0
- package/dist/core/policy-executor.js +228 -0
- package/dist/core/policy-executor.js.map +1 -0
- package/dist/core/policy-loader.d.ts +72 -0
- package/dist/core/policy-loader.d.ts.map +1 -0
- package/dist/core/policy-loader.js +195 -0
- package/dist/core/policy-loader.js.map +1 -0
- package/dist/core/project-trust.d.ts +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +2 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +3 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sandbox/bwrap-arguments.d.ts +41 -0
- package/dist/core/sandbox/bwrap-arguments.d.ts.map +1 -0
- package/dist/core/sandbox/bwrap-arguments.js +136 -0
- package/dist/core/sandbox/bwrap-arguments.js.map +1 -0
- package/dist/core/sandbox/child-entry.d.ts.map +1 -1
- package/dist/core/sandbox/child-entry.js +3 -1
- package/dist/core/sandbox/child-entry.js.map +1 -1
- package/dist/core/sandbox/cli-launch.d.ts +84 -4
- package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
- package/dist/core/sandbox/cli-launch.js +115 -16
- package/dist/core/sandbox/cli-launch.js.map +1 -1
- package/dist/core/sandbox/cli-supervisor.d.ts +2 -0
- package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/cli-supervisor.js +50 -2
- package/dist/core/sandbox/cli-supervisor.js.map +1 -1
- package/dist/core/sandbox/full-access.d.ts +28 -0
- package/dist/core/sandbox/full-access.d.ts.map +1 -0
- package/dist/core/sandbox/full-access.js +63 -0
- package/dist/core/sandbox/full-access.js.map +1 -0
- package/dist/core/sandbox/git-identity.d.ts +57 -0
- package/dist/core/sandbox/git-identity.d.ts.map +1 -0
- package/dist/core/sandbox/git-identity.js +78 -0
- package/dist/core/sandbox/git-identity.js.map +1 -0
- package/dist/core/sandbox/host-approval.d.ts +42 -0
- package/dist/core/sandbox/host-approval.d.ts.map +1 -0
- package/dist/core/sandbox/host-approval.js +99 -0
- package/dist/core/sandbox/host-approval.js.map +1 -0
- package/dist/core/sandbox/linux-backend.d.ts +14 -0
- package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
- package/dist/core/sandbox/linux-backend.js +158 -80
- package/dist/core/sandbox/linux-backend.js.map +1 -1
- package/dist/core/sandbox/macos-backend.d.ts +5 -0
- package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
- package/dist/core/sandbox/macos-backend.js +164 -5
- package/dist/core/sandbox/macos-backend.js.map +1 -1
- package/dist/core/sandbox/network-proxy.d.ts +19 -0
- package/dist/core/sandbox/network-proxy.d.ts.map +1 -1
- package/dist/core/sandbox/network-proxy.js +93 -19
- package/dist/core/sandbox/network-proxy.js.map +1 -1
- package/dist/core/sandbox/policy.d.ts +8 -0
- package/dist/core/sandbox/policy.d.ts.map +1 -1
- package/dist/core/sandbox/policy.js +29 -12
- package/dist/core/sandbox/policy.js.map +1 -1
- package/dist/core/sandbox/profiles.d.ts +25 -0
- package/dist/core/sandbox/profiles.d.ts.map +1 -0
- package/dist/core/sandbox/profiles.js +23 -0
- package/dist/core/sandbox/profiles.js.map +1 -0
- package/dist/core/sandbox/rpc/command-client.d.ts +28 -0
- package/dist/core/sandbox/rpc/command-client.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/command-client.js +86 -0
- package/dist/core/sandbox/rpc/command-client.js.map +1 -0
- package/dist/core/sandbox/rpc/command-proxy.d.ts +48 -0
- package/dist/core/sandbox/rpc/command-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/command-proxy.js +120 -0
- package/dist/core/sandbox/rpc/command-proxy.js.map +1 -0
- package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -1
- package/dist/core/sandbox/rpc/credential-proxy.js +2 -96
- package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -1
- package/dist/core/sandbox/rpc/framing.d.ts +35 -0
- package/dist/core/sandbox/rpc/framing.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/framing.js +114 -0
- package/dist/core/sandbox/rpc/framing.js.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-helper.d.ts +47 -0
- package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-helper.js +222 -0
- package/dist/core/sandbox/rpc/git-credential-helper.js.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +66 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.js +209 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.js.map +1 -0
- package/dist/core/sandbox/supervisor-temp.d.ts +18 -0
- package/dist/core/sandbox/supervisor-temp.d.ts.map +1 -0
- package/dist/core/sandbox/supervisor-temp.js +21 -0
- package/dist/core/sandbox/supervisor-temp.js.map +1 -0
- package/dist/core/sandbox/supervisor.d.ts +12 -1
- package/dist/core/sandbox/supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/supervisor.js +15 -1
- package/dist/core/sandbox/supervisor.js.map +1 -1
- package/dist/core/sandbox/terminal-handoff.d.ts +68 -0
- package/dist/core/sandbox/terminal-handoff.d.ts.map +1 -0
- package/dist/core/sandbox/terminal-handoff.js +221 -0
- package/dist/core/sandbox/terminal-handoff.js.map +1 -0
- package/dist/core/sandbox/terminal-size.d.ts +13 -0
- package/dist/core/sandbox/terminal-size.d.ts.map +1 -1
- package/dist/core/sandbox/terminal-size.js +23 -2
- package/dist/core/sandbox/terminal-size.js.map +1 -1
- package/dist/core/sdk.d.ts +24 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +63 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-export.d.ts +4 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +32 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-manager.d.ts +13 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +34 -6
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +172 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +160 -11
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +51 -33
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +4 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +8 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/background-shell.d.ts +39 -0
- package/dist/core/tools/background-shell.d.ts.map +1 -0
- package/dist/core/tools/background-shell.js +81 -0
- package/dist/core/tools/background-shell.js.map +1 -0
- package/dist/core/tools/bash-command-segments.d.ts.map +1 -1
- package/dist/core/tools/bash-command-segments.js +2 -0
- package/dist/core/tools/bash-command-segments.js.map +1 -1
- package/dist/core/tools/bash.d.ts +44 -16
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +264 -32
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/contract-snapshot.d.ts +58 -0
- package/dist/core/tools/contract-snapshot.d.ts.map +1 -0
- package/dist/core/tools/contract-snapshot.js +51 -0
- package/dist/core/tools/contract-snapshot.js.map +1 -0
- package/dist/core/tools/contract.d.ts +53 -5
- package/dist/core/tools/contract.d.ts.map +1 -1
- package/dist/core/tools/contract.js +22 -2
- package/dist/core/tools/contract.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +6 -4
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +21 -11
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +48 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +217 -8
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts +8 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +72 -10
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +5 -2
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +5 -2
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +14 -2
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +12 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +5 -2
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/lsp.d.ts +19 -1
- package/dist/core/tools/lsp.d.ts.map +1 -1
- package/dist/core/tools/lsp.js +163 -10
- package/dist/core/tools/lsp.js.map +1 -1
- package/dist/core/tools/path-permission.d.ts +3 -0
- package/dist/core/tools/path-permission.d.ts.map +1 -1
- package/dist/core/tools/path-permission.js +16 -4
- package/dist/core/tools/path-permission.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts +27 -2
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js +221 -4
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/powershell.d.ts +16 -0
- package/dist/core/tools/powershell.d.ts.map +1 -0
- package/dist/core/tools/powershell.js +39 -0
- package/dist/core/tools/powershell.js.map +1 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +20 -11
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/test.d.ts +54 -9
- package/dist/core/tools/test.d.ts.map +1 -1
- package/dist/core/tools/test.js +239 -19
- package/dist/core/tools/test.js.map +1 -1
- package/dist/core/tools/write.d.ts +7 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +64 -9
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/verification-lifecycle.d.ts +77 -0
- package/dist/core/verification-lifecycle.d.ts.map +1 -0
- package/dist/core/verification-lifecycle.js +146 -0
- package/dist/core/verification-lifecycle.js.map +1 -0
- package/dist/core/workspace/artifacts.d.ts +45 -0
- package/dist/core/workspace/artifacts.d.ts.map +1 -0
- package/dist/core/workspace/artifacts.js +165 -0
- package/dist/core/workspace/artifacts.js.map +1 -0
- package/dist/core/workspace/comparison.d.ts +27 -0
- package/dist/core/workspace/comparison.d.ts.map +1 -0
- package/dist/core/workspace/comparison.js +94 -0
- package/dist/core/workspace/comparison.js.map +1 -0
- package/dist/core/workspace/git-observer.d.ts +48 -0
- package/dist/core/workspace/git-observer.d.ts.map +1 -0
- package/dist/core/workspace/git-observer.js +346 -0
- package/dist/core/workspace/git-observer.js.map +1 -0
- package/dist/core/workspace/projection.d.ts +3 -0
- package/dist/core/workspace/projection.d.ts.map +1 -0
- package/dist/core/workspace/projection.js +56 -0
- package/dist/core/workspace/projection.js.map +1 -0
- package/dist/core/workspace/state.d.ts +133 -0
- package/dist/core/workspace/state.d.ts.map +1 -0
- package/dist/core/workspace/state.js +96 -0
- package/dist/core/workspace/state.js.map +1 -0
- package/dist/extensions/llama/client.d.ts +6 -0
- package/dist/extensions/llama/client.d.ts.map +1 -1
- package/dist/extensions/llama/client.js +7 -0
- package/dist/extensions/llama/client.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +2 -0
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts +3 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +29 -4
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +6 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +58 -23
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +4 -3
- package/dist/migrations.js.map +1 -1
- package/dist/modes/acp/server.d.ts +55 -0
- package/dist/modes/acp/server.d.ts.map +1 -0
- package/dist/modes/acp/server.js +244 -0
- package/dist/modes/acp/server.js.map +1 -0
- package/dist/modes/acp/translate.d.ts +37 -0
- package/dist/modes/acp/translate.d.ts.map +1 -0
- package/dist/modes/acp/translate.js +50 -0
- package/dist/modes/acp/translate.js.map +1 -0
- package/dist/modes/index.d.ts +1 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +1 -0
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +29 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +182 -23
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +7 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +20 -0
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +9 -4
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +20 -9
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +7 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +7 -3
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +19 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +70 -10
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +10 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +56 -14
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/permission-preview.d.ts +15 -0
- package/dist/modes/interactive/components/permission-preview.d.ts.map +1 -0
- package/dist/modes/interactive/components/permission-preview.js +33 -0
- package/dist/modes/interactive/components/permission-preview.js.map +1 -0
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +2 -2
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +9 -2
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +128 -56
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +74 -17
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +23 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +64 -10
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +2 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +45 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +395 -43
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +12 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +20 -9
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +16 -13
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +20 -6
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +27 -12
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +7 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +7 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +12 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts +1 -0
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +196 -10
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +2 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/management-http.d.ts +5 -3
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js +14 -10
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/shell.d.ts +3 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +23 -9
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +60 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +6 -4
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +19 -0
- package/docs/custom-provider.md +12 -9
- package/docs/environment-variables.md +1 -1
- package/docs/extensions.md +35 -7
- package/docs/json.md +7 -2
- package/docs/keybindings.md +9 -9
- package/docs/llama-cpp.md +2 -0
- package/docs/models.md +8 -2
- package/docs/packages.md +2 -2
- package/docs/quickstart.md +1 -1
- package/docs/rpc.md +32 -3
- package/docs/sdk.md +32 -2
- package/docs/settings.md +23 -7
- package/docs/skills.md +4 -3
- package/docs/terminal-setup.md +23 -7
- package/docs/usage.md +10 -5
- package/docs/windows.md +24 -2
- package/examples/extensions/border-status-editor.ts +1 -1
- package/examples/extensions/built-in-tool-renderer.ts +8 -4
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/git-checkpoint.ts +24 -36
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/minimal-mode.ts +2 -2
- package/examples/extensions/notify.ts +3 -1
- package/examples/extensions/permission-gate.ts +1 -1
- package/examples/extensions/plan-mode/index.ts +1 -1
- package/examples/extensions/rpc-demo.ts +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +1 -1
- package/examples/extensions/subagent/index.ts +6 -1
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +439 -405
- package/package.json +17 -17
- package/dist/server/create-harness.d.ts +0 -26
- package/dist/server/create-harness.d.ts.map +0 -1
- package/dist/server/create-harness.js +0 -107
- package/dist/server/create-harness.js.map +0 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `mcp` tool's parameter schema, and the single source of its param type.
|
|
3
|
+
*
|
|
4
|
+
* It lives apart from the tool itself so the contract (`contract.ts`) can type its
|
|
5
|
+
* permission grammar against the same shape without importing the tool, which would
|
|
6
|
+
* be a cycle: the tool needs the contract.
|
|
7
|
+
*/
|
|
8
|
+
import { Type } from "typebox";
|
|
9
|
+
export const mcpToolSchema = Type.Object({
|
|
10
|
+
search: Type.Optional(Type.String({
|
|
11
|
+
description: "Find tools across configured MCP servers by keyword. Reads a local cache, so it works without starting a server.",
|
|
12
|
+
})),
|
|
13
|
+
server: Type.Optional(Type.String({ description: "Restrict a search to one server, or list that server's tools when used alone." })),
|
|
14
|
+
describe: Type.Optional(Type.String({ description: 'Show one tool\'s full input schema. Qualified as "<server>:<tool>".' })),
|
|
15
|
+
tool: Type.Optional(Type.String({
|
|
16
|
+
description: 'The tool to call, qualified as "<server>:<tool>". Requires the server to be reachable.',
|
|
17
|
+
})),
|
|
18
|
+
args: Type.Optional(Type.Unknown({ description: "Arguments for the called tool, matching its input schema." })),
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/core/mcp/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC;IACxC,MAAM,EAAE,IAAI,CAAC,QAAQ,CACpB,IAAI,CAAC,MAAM,CAAC;QACX,WAAW,EACV,kHAAkH;KACnH,CAAC,CACF;IACD,MAAM,EAAE,IAAI,CAAC,QAAQ,CACpB,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,+EAA+E,EAAE,CAAC,CAC7G;IACD,QAAQ,EAAE,IAAI,CAAC,QAAQ,CACtB,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,qEAAqE,EAAE,CAAC,CACnG;IACD,IAAI,EAAE,IAAI,CAAC,QAAQ,CAClB,IAAI,CAAC,MAAM,CAAC;QACX,WAAW,EAAE,wFAAwF;KACrG,CAAC,CACF;IACD,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,2DAA2D,EAAE,CAAC,CAAC;CAC/G,CAAC,CAAC","sourcesContent":["/**\n * The `mcp` tool's parameter schema, and the single source of its param type.\n *\n * It lives apart from the tool itself so the contract (`contract.ts`) can type its\n * permission grammar against the same shape without importing the tool, which would\n * be a cycle: the tool needs the contract.\n */\n\nimport { type Static, Type } from \"typebox\";\n\nexport const mcpToolSchema = Type.Object({\n\tsearch: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Find tools across configured MCP servers by keyword. Reads a local cache, so it works without starting a server.\",\n\t\t}),\n\t),\n\tserver: Type.Optional(\n\t\tType.String({ description: \"Restrict a search to one server, or list that server's tools when used alone.\" }),\n\t),\n\tdescribe: Type.Optional(\n\t\tType.String({ description: 'Show one tool\\'s full input schema. Qualified as \"<server>:<tool>\".' }),\n\t),\n\ttool: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: 'The tool to call, qualified as \"<server>:<tool>\". Requires the server to be reachable.',\n\t\t}),\n\t),\n\targs: Type.Optional(Type.Unknown({ description: \"Arguments for the called tool, matching its input schema.\" })),\n});\n\nexport type McpToolParams = Static<typeof mcpToolSchema>;\n\nexport interface McpToolDetails {\n\taction: \"search\" | \"describe\" | \"list\" | \"call\";\n\tserver: string | undefined;\n\ttool: string | undefined;\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owns when a server is running and nothing about how it is reached.
|
|
3
|
+
*
|
|
4
|
+
* The `McpConnection` seam is why this module has no MCP SDK import and no child
|
|
5
|
+
* process: lifecycle is the part with the interesting failure modes (a connect that
|
|
6
|
+
* never returns, a broken server retried on every call, two callers racing the same
|
|
7
|
+
* spawn) and all of it is testable against a fake.
|
|
8
|
+
*/
|
|
9
|
+
import type { CachedTool, McpConnection, McpConnector, McpServerConfig, ServerState } from "./types.ts";
|
|
10
|
+
export interface McpServerManagerOptions {
|
|
11
|
+
servers: ReadonlyMap<string, McpServerConfig>;
|
|
12
|
+
connector: McpConnector;
|
|
13
|
+
now?: () => number;
|
|
14
|
+
connectTimeoutMs?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class McpServerManager {
|
|
17
|
+
private readonly servers;
|
|
18
|
+
private readonly connector;
|
|
19
|
+
private readonly now;
|
|
20
|
+
private readonly connectTimeoutMs;
|
|
21
|
+
private readonly entries;
|
|
22
|
+
constructor(options: McpServerManagerOptions);
|
|
23
|
+
state(name: string): ServerState;
|
|
24
|
+
private entry;
|
|
25
|
+
private connect;
|
|
26
|
+
private acquire;
|
|
27
|
+
withConnection<T>(name: string, use: (connection: McpConnection) => Promise<T>): Promise<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Connect every `eager` server and cache what it publishes.
|
|
30
|
+
*
|
|
31
|
+
* This is the answer to the cold-cache problem. A lazily configured server has no
|
|
32
|
+
* cached tools until something calls one, so it cannot be discovered by search; a
|
|
33
|
+
* user who wants a server discoverable from the first turn declares it `eager`.
|
|
34
|
+
* Failures are swallowed on purpose, because a broken server must not stop a
|
|
35
|
+
* session from starting.
|
|
36
|
+
*/
|
|
37
|
+
warmEagerServers(onTools: (server: McpServerConfig, tools: CachedTool[]) => void): Promise<void>;
|
|
38
|
+
/** Close every `ready` server whose idle window has passed, unless it is kept alive. */
|
|
39
|
+
sweepIdle(): Promise<void>;
|
|
40
|
+
private disconnect;
|
|
41
|
+
closeAll(): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=server-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-manager.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/server-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAYxG,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC9C,SAAS,EAAE,YAAY,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IAEpD,YAAY,OAAO,EAAE,uBAAuB,EAK3C;IAED,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAE/B;IAED,OAAO,CAAC,KAAK;YASC,OAAO;YAiCP,OAAO;IAkBf,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAOhG;IAED;;;;;;;;OAQG;IACG,gBAAgB,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrG;IAED,wFAAwF;IAClF,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ/B;YAEa,UAAU;IAUlB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAI9B;CACD","sourcesContent":["/**\n * Owns when a server is running and nothing about how it is reached.\n *\n * The `McpConnection` seam is why this module has no MCP SDK import and no child\n * process: lifecycle is the part with the interesting failure modes (a connect that\n * never returns, a broken server retried on every call, two callers racing the same\n * spawn) and all of it is testable against a fake.\n */\n\nimport type { CachedTool, McpConnection, McpConnector, McpServerConfig, ServerState } from \"./types.ts\";\n\nconst DEFAULT_CONNECT_TIMEOUT_MS = 30_000;\nconst FAILURE_BACKOFF_MS = 60_000;\n\ninterface Entry {\n\tstate: ServerState;\n\tconnection: McpConnection | undefined;\n\t/** Held so concurrent callers await one spawn instead of racing a second. */\n\tpending: Promise<McpConnection> | undefined;\n}\n\nexport interface McpServerManagerOptions {\n\tservers: ReadonlyMap<string, McpServerConfig>;\n\tconnector: McpConnector;\n\tnow?: () => number;\n\tconnectTimeoutMs?: number;\n}\n\nfunction isKeptAlive(server: McpServerConfig): boolean {\n\treturn server.lifecycle === \"keep-alive\" || server.lifecycle === \"lazy-keep-alive\";\n}\n\nexport class McpServerManager {\n\tprivate readonly servers: ReadonlyMap<string, McpServerConfig>;\n\tprivate readonly connector: McpConnector;\n\tprivate readonly now: () => number;\n\tprivate readonly connectTimeoutMs: number;\n\tprivate readonly entries = new Map<string, Entry>();\n\n\tconstructor(options: McpServerManagerOptions) {\n\t\tthis.servers = options.servers;\n\t\tthis.connector = options.connector;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;\n\t}\n\n\tstate(name: string): ServerState {\n\t\treturn this.entries.get(name)?.state ?? { kind: \"disconnected\" };\n\t}\n\n\tprivate entry(name: string): Entry {\n\t\tlet entry = this.entries.get(name);\n\t\tif (!entry) {\n\t\t\tentry = { state: { kind: \"disconnected\" }, connection: undefined, pending: undefined };\n\t\t\tthis.entries.set(name, entry);\n\t\t}\n\t\treturn entry;\n\t}\n\n\tprivate async connect(server: McpServerConfig, entry: Entry): Promise<McpConnection> {\n\t\tentry.state = { kind: \"connecting\", since: this.now() };\n\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst timeout = new Promise<never>((_, reject) => {\n\t\t\ttimer = setTimeout(\n\t\t\t\t() => reject(new Error(`Connecting to MCP server \"${server.name}\" timed out`)),\n\t\t\t\tthis.connectTimeoutMs,\n\t\t\t);\n\t\t});\n\n\t\ttry {\n\t\t\tconst connection = await Promise.race([this.connector(server), timeout]);\n\t\t\tconst at = this.now();\n\t\t\tentry.connection = connection;\n\t\t\tentry.state = { kind: \"ready\", since: at, lastUsed: at };\n\t\t\treturn connection;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tentry.connection = undefined;\n\t\t\tentry.state = {\n\t\t\t\tkind: \"failed\",\n\t\t\t\terror: message,\n\t\t\t\tfailedAt: this.now(),\n\t\t\t\tretryAfter: this.now() + FAILURE_BACKOFF_MS,\n\t\t\t};\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\tentry.pending = undefined;\n\t\t}\n\t}\n\n\tprivate async acquire(name: string): Promise<McpConnection> {\n\t\tconst server = this.servers.get(name);\n\t\tif (!server) throw new Error(`MCP server \"${name}\" is not configured`);\n\n\t\tconst entry = this.entry(name);\n\t\tif (entry.state.kind === \"ready\" && entry.connection) return entry.connection;\n\t\tif (entry.pending) return entry.pending;\n\n\t\t// A server that just failed is not retried until its window passes, so one\n\t\t// broken server cannot cost every call a spawn attempt.\n\t\tif (entry.state.kind === \"failed\" && this.now() < entry.state.retryAfter) {\n\t\t\tthrow new Error(entry.state.error);\n\t\t}\n\n\t\tentry.pending = this.connect(server, entry);\n\t\treturn entry.pending;\n\t}\n\n\tasync withConnection<T>(name: string, use: (connection: McpConnection) => Promise<T>): Promise<T> {\n\t\tconst connection = await this.acquire(name);\n\t\tconst result = await use(connection);\n\n\t\tconst entry = this.entry(name);\n\t\tif (entry.state.kind === \"ready\") entry.state = { ...entry.state, lastUsed: this.now() };\n\t\treturn result;\n\t}\n\n\t/**\n\t * Connect every `eager` server and cache what it publishes.\n\t *\n\t * This is the answer to the cold-cache problem. A lazily configured server has no\n\t * cached tools until something calls one, so it cannot be discovered by search; a\n\t * user who wants a server discoverable from the first turn declares it `eager`.\n\t * Failures are swallowed on purpose, because a broken server must not stop a\n\t * session from starting.\n\t */\n\tasync warmEagerServers(onTools: (server: McpServerConfig, tools: CachedTool[]) => void): Promise<void> {\n\t\tfor (const server of this.servers.values()) {\n\t\t\tif (server.lifecycle !== \"eager\") continue;\n\t\t\ttry {\n\t\t\t\tawait this.withConnection(server.name, async (connection) => {\n\t\t\t\t\tonTools(server, await connection.listTools());\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\t// Recorded as `failed` by `connect`; the next call reports it.\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Close every `ready` server whose idle window has passed, unless it is kept alive. */\n\tasync sweepIdle(): Promise<void> {\n\t\tconst at = this.now();\n\t\tfor (const [name, entry] of this.entries) {\n\t\t\tconst server = this.servers.get(name);\n\t\t\tif (!server || entry.state.kind !== \"ready\" || isKeptAlive(server)) continue;\n\t\t\tif (at - entry.state.lastUsed < server.idleTimeoutMinutes * 60_000) continue;\n\t\t\tawait this.disconnect(name, entry);\n\t\t}\n\t}\n\n\tprivate async disconnect(name: string, entry: Entry): Promise<void> {\n\t\tconst connection = entry.connection;\n\t\tentry.connection = undefined;\n\t\tentry.state = { kind: \"disconnected\" };\n\t\tif (connection) {\n\t\t\tawait connection.close().catch(() => undefined);\n\t\t}\n\t\tthis.entries.set(name, entry);\n\t}\n\n\tasync closeAll(): Promise<void> {\n\t\tfor (const [name, entry] of this.entries) {\n\t\t\tawait this.disconnect(name, entry);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owns when a server is running and nothing about how it is reached.
|
|
3
|
+
*
|
|
4
|
+
* The `McpConnection` seam is why this module has no MCP SDK import and no child
|
|
5
|
+
* process: lifecycle is the part with the interesting failure modes (a connect that
|
|
6
|
+
* never returns, a broken server retried on every call, two callers racing the same
|
|
7
|
+
* spawn) and all of it is testable against a fake.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_CONNECT_TIMEOUT_MS = 30_000;
|
|
10
|
+
const FAILURE_BACKOFF_MS = 60_000;
|
|
11
|
+
function isKeptAlive(server) {
|
|
12
|
+
return server.lifecycle === "keep-alive" || server.lifecycle === "lazy-keep-alive";
|
|
13
|
+
}
|
|
14
|
+
export class McpServerManager {
|
|
15
|
+
servers;
|
|
16
|
+
connector;
|
|
17
|
+
now;
|
|
18
|
+
connectTimeoutMs;
|
|
19
|
+
entries = new Map();
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.servers = options.servers;
|
|
22
|
+
this.connector = options.connector;
|
|
23
|
+
this.now = options.now ?? Date.now;
|
|
24
|
+
this.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
25
|
+
}
|
|
26
|
+
state(name) {
|
|
27
|
+
return this.entries.get(name)?.state ?? { kind: "disconnected" };
|
|
28
|
+
}
|
|
29
|
+
entry(name) {
|
|
30
|
+
let entry = this.entries.get(name);
|
|
31
|
+
if (!entry) {
|
|
32
|
+
entry = { state: { kind: "disconnected" }, connection: undefined, pending: undefined };
|
|
33
|
+
this.entries.set(name, entry);
|
|
34
|
+
}
|
|
35
|
+
return entry;
|
|
36
|
+
}
|
|
37
|
+
async connect(server, entry) {
|
|
38
|
+
entry.state = { kind: "connecting", since: this.now() };
|
|
39
|
+
let timer;
|
|
40
|
+
const timeout = new Promise((_, reject) => {
|
|
41
|
+
timer = setTimeout(() => reject(new Error(`Connecting to MCP server "${server.name}" timed out`)), this.connectTimeoutMs);
|
|
42
|
+
});
|
|
43
|
+
try {
|
|
44
|
+
const connection = await Promise.race([this.connector(server), timeout]);
|
|
45
|
+
const at = this.now();
|
|
46
|
+
entry.connection = connection;
|
|
47
|
+
entry.state = { kind: "ready", since: at, lastUsed: at };
|
|
48
|
+
return connection;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
entry.connection = undefined;
|
|
53
|
+
entry.state = {
|
|
54
|
+
kind: "failed",
|
|
55
|
+
error: message,
|
|
56
|
+
failedAt: this.now(),
|
|
57
|
+
retryAfter: this.now() + FAILURE_BACKOFF_MS,
|
|
58
|
+
};
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
if (timer)
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
entry.pending = undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async acquire(name) {
|
|
68
|
+
const server = this.servers.get(name);
|
|
69
|
+
if (!server)
|
|
70
|
+
throw new Error(`MCP server "${name}" is not configured`);
|
|
71
|
+
const entry = this.entry(name);
|
|
72
|
+
if (entry.state.kind === "ready" && entry.connection)
|
|
73
|
+
return entry.connection;
|
|
74
|
+
if (entry.pending)
|
|
75
|
+
return entry.pending;
|
|
76
|
+
// A server that just failed is not retried until its window passes, so one
|
|
77
|
+
// broken server cannot cost every call a spawn attempt.
|
|
78
|
+
if (entry.state.kind === "failed" && this.now() < entry.state.retryAfter) {
|
|
79
|
+
throw new Error(entry.state.error);
|
|
80
|
+
}
|
|
81
|
+
entry.pending = this.connect(server, entry);
|
|
82
|
+
return entry.pending;
|
|
83
|
+
}
|
|
84
|
+
async withConnection(name, use) {
|
|
85
|
+
const connection = await this.acquire(name);
|
|
86
|
+
const result = await use(connection);
|
|
87
|
+
const entry = this.entry(name);
|
|
88
|
+
if (entry.state.kind === "ready")
|
|
89
|
+
entry.state = { ...entry.state, lastUsed: this.now() };
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Connect every `eager` server and cache what it publishes.
|
|
94
|
+
*
|
|
95
|
+
* This is the answer to the cold-cache problem. A lazily configured server has no
|
|
96
|
+
* cached tools until something calls one, so it cannot be discovered by search; a
|
|
97
|
+
* user who wants a server discoverable from the first turn declares it `eager`.
|
|
98
|
+
* Failures are swallowed on purpose, because a broken server must not stop a
|
|
99
|
+
* session from starting.
|
|
100
|
+
*/
|
|
101
|
+
async warmEagerServers(onTools) {
|
|
102
|
+
for (const server of this.servers.values()) {
|
|
103
|
+
if (server.lifecycle !== "eager")
|
|
104
|
+
continue;
|
|
105
|
+
try {
|
|
106
|
+
await this.withConnection(server.name, async (connection) => {
|
|
107
|
+
onTools(server, await connection.listTools());
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Recorded as `failed` by `connect`; the next call reports it.
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Close every `ready` server whose idle window has passed, unless it is kept alive. */
|
|
116
|
+
async sweepIdle() {
|
|
117
|
+
const at = this.now();
|
|
118
|
+
for (const [name, entry] of this.entries) {
|
|
119
|
+
const server = this.servers.get(name);
|
|
120
|
+
if (!server || entry.state.kind !== "ready" || isKeptAlive(server))
|
|
121
|
+
continue;
|
|
122
|
+
if (at - entry.state.lastUsed < server.idleTimeoutMinutes * 60_000)
|
|
123
|
+
continue;
|
|
124
|
+
await this.disconnect(name, entry);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async disconnect(name, entry) {
|
|
128
|
+
const connection = entry.connection;
|
|
129
|
+
entry.connection = undefined;
|
|
130
|
+
entry.state = { kind: "disconnected" };
|
|
131
|
+
if (connection) {
|
|
132
|
+
await connection.close().catch(() => undefined);
|
|
133
|
+
}
|
|
134
|
+
this.entries.set(name, entry);
|
|
135
|
+
}
|
|
136
|
+
async closeAll() {
|
|
137
|
+
for (const [name, entry] of this.entries) {
|
|
138
|
+
await this.disconnect(name, entry);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=server-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-manager.js","sourceRoot":"","sources":["../../../src/core/mcp/server-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Owns when a server is running and nothing about how it is reached.\n *\n * The `McpConnection` seam is why this module has no MCP SDK import and no child\n * process: lifecycle is the part with the interesting failure modes (a connect that\n * never returns, a broken server retried on every call, two callers racing the same\n * spawn) and all of it is testable against a fake.\n */\n\nimport type { CachedTool, McpConnection, McpConnector, McpServerConfig, ServerState } from \"./types.ts\";\n\nconst DEFAULT_CONNECT_TIMEOUT_MS = 30_000;\nconst FAILURE_BACKOFF_MS = 60_000;\n\ninterface Entry {\n\tstate: ServerState;\n\tconnection: McpConnection | undefined;\n\t/** Held so concurrent callers await one spawn instead of racing a second. */\n\tpending: Promise<McpConnection> | undefined;\n}\n\nexport interface McpServerManagerOptions {\n\tservers: ReadonlyMap<string, McpServerConfig>;\n\tconnector: McpConnector;\n\tnow?: () => number;\n\tconnectTimeoutMs?: number;\n}\n\nfunction isKeptAlive(server: McpServerConfig): boolean {\n\treturn server.lifecycle === \"keep-alive\" || server.lifecycle === \"lazy-keep-alive\";\n}\n\nexport class McpServerManager {\n\tprivate readonly servers: ReadonlyMap<string, McpServerConfig>;\n\tprivate readonly connector: McpConnector;\n\tprivate readonly now: () => number;\n\tprivate readonly connectTimeoutMs: number;\n\tprivate readonly entries = new Map<string, Entry>();\n\n\tconstructor(options: McpServerManagerOptions) {\n\t\tthis.servers = options.servers;\n\t\tthis.connector = options.connector;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;\n\t}\n\n\tstate(name: string): ServerState {\n\t\treturn this.entries.get(name)?.state ?? { kind: \"disconnected\" };\n\t}\n\n\tprivate entry(name: string): Entry {\n\t\tlet entry = this.entries.get(name);\n\t\tif (!entry) {\n\t\t\tentry = { state: { kind: \"disconnected\" }, connection: undefined, pending: undefined };\n\t\t\tthis.entries.set(name, entry);\n\t\t}\n\t\treturn entry;\n\t}\n\n\tprivate async connect(server: McpServerConfig, entry: Entry): Promise<McpConnection> {\n\t\tentry.state = { kind: \"connecting\", since: this.now() };\n\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst timeout = new Promise<never>((_, reject) => {\n\t\t\ttimer = setTimeout(\n\t\t\t\t() => reject(new Error(`Connecting to MCP server \"${server.name}\" timed out`)),\n\t\t\t\tthis.connectTimeoutMs,\n\t\t\t);\n\t\t});\n\n\t\ttry {\n\t\t\tconst connection = await Promise.race([this.connector(server), timeout]);\n\t\t\tconst at = this.now();\n\t\t\tentry.connection = connection;\n\t\t\tentry.state = { kind: \"ready\", since: at, lastUsed: at };\n\t\t\treturn connection;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tentry.connection = undefined;\n\t\t\tentry.state = {\n\t\t\t\tkind: \"failed\",\n\t\t\t\terror: message,\n\t\t\t\tfailedAt: this.now(),\n\t\t\t\tretryAfter: this.now() + FAILURE_BACKOFF_MS,\n\t\t\t};\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\tentry.pending = undefined;\n\t\t}\n\t}\n\n\tprivate async acquire(name: string): Promise<McpConnection> {\n\t\tconst server = this.servers.get(name);\n\t\tif (!server) throw new Error(`MCP server \"${name}\" is not configured`);\n\n\t\tconst entry = this.entry(name);\n\t\tif (entry.state.kind === \"ready\" && entry.connection) return entry.connection;\n\t\tif (entry.pending) return entry.pending;\n\n\t\t// A server that just failed is not retried until its window passes, so one\n\t\t// broken server cannot cost every call a spawn attempt.\n\t\tif (entry.state.kind === \"failed\" && this.now() < entry.state.retryAfter) {\n\t\t\tthrow new Error(entry.state.error);\n\t\t}\n\n\t\tentry.pending = this.connect(server, entry);\n\t\treturn entry.pending;\n\t}\n\n\tasync withConnection<T>(name: string, use: (connection: McpConnection) => Promise<T>): Promise<T> {\n\t\tconst connection = await this.acquire(name);\n\t\tconst result = await use(connection);\n\n\t\tconst entry = this.entry(name);\n\t\tif (entry.state.kind === \"ready\") entry.state = { ...entry.state, lastUsed: this.now() };\n\t\treturn result;\n\t}\n\n\t/**\n\t * Connect every `eager` server and cache what it publishes.\n\t *\n\t * This is the answer to the cold-cache problem. A lazily configured server has no\n\t * cached tools until something calls one, so it cannot be discovered by search; a\n\t * user who wants a server discoverable from the first turn declares it `eager`.\n\t * Failures are swallowed on purpose, because a broken server must not stop a\n\t * session from starting.\n\t */\n\tasync warmEagerServers(onTools: (server: McpServerConfig, tools: CachedTool[]) => void): Promise<void> {\n\t\tfor (const server of this.servers.values()) {\n\t\t\tif (server.lifecycle !== \"eager\") continue;\n\t\t\ttry {\n\t\t\t\tawait this.withConnection(server.name, async (connection) => {\n\t\t\t\t\tonTools(server, await connection.listTools());\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\t// Recorded as `failed` by `connect`; the next call reports it.\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Close every `ready` server whose idle window has passed, unless it is kept alive. */\n\tasync sweepIdle(): Promise<void> {\n\t\tconst at = this.now();\n\t\tfor (const [name, entry] of this.entries) {\n\t\t\tconst server = this.servers.get(name);\n\t\t\tif (!server || entry.state.kind !== \"ready\" || isKeptAlive(server)) continue;\n\t\t\tif (at - entry.state.lastUsed < server.idleTimeoutMinutes * 60_000) continue;\n\t\t\tawait this.disconnect(name, entry);\n\t\t}\n\t}\n\n\tprivate async disconnect(name: string, entry: Entry): Promise<void> {\n\t\tconst connection = entry.connection;\n\t\tentry.connection = undefined;\n\t\tentry.state = { kind: \"disconnected\" };\n\t\tif (connection) {\n\t\t\tawait connection.close().catch(() => undefined);\n\t\t}\n\t\tthis.entries.set(name, entry);\n\t}\n\n\tasync closeAll(): Promise<void> {\n\t\tfor (const [name, entry] of this.entries) {\n\t\t\tawait this.disconnect(name, entry);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core MCP data shapes. Settled before any logic consumes them, per the plan's U1:
|
|
3
|
+
* a late change to `ServerState` or `CachedTool` is a rewrite of the server manager
|
|
4
|
+
* and the proxy tool, where an early one is a one-line diff.
|
|
5
|
+
*/
|
|
6
|
+
import type { Capability } from "../tools/contract.ts";
|
|
7
|
+
/**
|
|
8
|
+
* How to reach a server. Inferred from which fields the config entry carries, never
|
|
9
|
+
* declared: every MCP host writes `command` for stdio and `url` for HTTP, and none
|
|
10
|
+
* writes a `transport` field, so requiring one would reject the config files users
|
|
11
|
+
* already have.
|
|
12
|
+
*/
|
|
13
|
+
export type McpTransport = {
|
|
14
|
+
kind: "stdio";
|
|
15
|
+
command: string;
|
|
16
|
+
args: string[];
|
|
17
|
+
env: Record<string, string>;
|
|
18
|
+
cwd: string | undefined;
|
|
19
|
+
} | {
|
|
20
|
+
kind: "http";
|
|
21
|
+
url: string;
|
|
22
|
+
headers: Record<string, string>;
|
|
23
|
+
/** Environment variable holding a static bearer token. OAuth lives in the credential store (spec 2026-09-01-mcp-oauth). */
|
|
24
|
+
bearerTokenEnv: string | undefined;
|
|
25
|
+
};
|
|
26
|
+
export type McpLifecycle = "lazy" | "eager" | "keep-alive" | "lazy-keep-alive";
|
|
27
|
+
export declare const DEFAULT_LIFECYCLE: McpLifecycle;
|
|
28
|
+
export declare const DEFAULT_IDLE_TIMEOUT_MINUTES = 10;
|
|
29
|
+
/**
|
|
30
|
+
* A server's declared authority, and the only place it is stated. The capability set
|
|
31
|
+
* feeds the tool contract every tool on this server projects (ADR 0010), which is why
|
|
32
|
+
* an absent set resolves to the full one rather than the empty one.
|
|
33
|
+
*/
|
|
34
|
+
export interface McpServerConfig {
|
|
35
|
+
name: string;
|
|
36
|
+
transport: McpTransport;
|
|
37
|
+
/** Set when the user opted this HTTP server into OAuth (spec 2026-09-01-mcp-oauth). Server-level, so metadata cache keys ignore it. */
|
|
38
|
+
auth?: "oauth";
|
|
39
|
+
capabilities: ReadonlySet<Capability>;
|
|
40
|
+
lifecycle: McpLifecycle;
|
|
41
|
+
idleTimeoutMinutes: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Connection state. A union rather than a `connected` boolean beside a `lastError`
|
|
45
|
+
* string, so a state cannot carry data that is invalid for it.
|
|
46
|
+
*/
|
|
47
|
+
export type ServerState = {
|
|
48
|
+
kind: "disconnected";
|
|
49
|
+
} | {
|
|
50
|
+
kind: "connecting";
|
|
51
|
+
since: number;
|
|
52
|
+
} | {
|
|
53
|
+
kind: "ready";
|
|
54
|
+
since: number;
|
|
55
|
+
lastUsed: number;
|
|
56
|
+
} | {
|
|
57
|
+
kind: "failed";
|
|
58
|
+
error: string;
|
|
59
|
+
failedAt: number;
|
|
60
|
+
retryAfter: number;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* What the disk cache holds. Everything here is producible without a live connection,
|
|
64
|
+
* which is the property that lets search and describe answer with no server running.
|
|
65
|
+
*/
|
|
66
|
+
export interface CachedTool {
|
|
67
|
+
server: string;
|
|
68
|
+
name: string;
|
|
69
|
+
description: string;
|
|
70
|
+
inputSchema: unknown;
|
|
71
|
+
}
|
|
72
|
+
/** A config problem that dropped an entry or fell back to a default. Never thrown. */
|
|
73
|
+
export interface McpConfigDiagnostic {
|
|
74
|
+
path: string;
|
|
75
|
+
server: string | undefined;
|
|
76
|
+
message: string;
|
|
77
|
+
}
|
|
78
|
+
export interface McpConfig {
|
|
79
|
+
servers: ReadonlyMap<string, McpServerConfig>;
|
|
80
|
+
diagnostics: readonly McpConfigDiagnostic[];
|
|
81
|
+
}
|
|
82
|
+
/** One tool call's result, in the shape the proxy tool converts to `AgentToolResult`. */
|
|
83
|
+
export interface McpCallResult {
|
|
84
|
+
content: Array<{
|
|
85
|
+
type: "text";
|
|
86
|
+
text: string;
|
|
87
|
+
}>;
|
|
88
|
+
isError?: boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A live session with one server. The seam that keeps the MCP SDK out of the
|
|
92
|
+
* lifecycle logic, and lets the server manager be tested without a child process.
|
|
93
|
+
*/
|
|
94
|
+
export interface McpConnection {
|
|
95
|
+
listTools(): Promise<CachedTool[]>;
|
|
96
|
+
callTool(name: string, args: unknown): Promise<McpCallResult>;
|
|
97
|
+
close(): Promise<void>;
|
|
98
|
+
}
|
|
99
|
+
export type McpConnector = (server: McpServerConfig) => Promise<McpConnection>;
|
|
100
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACrB;IACA,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACvB,GACD;IACA,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,2HAA2H;IAC3H,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,CAAC;AAEL,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,YAAY,GAAG,iBAAiB,CAAC;AAE/E,eAAO,MAAM,iBAAiB,EAAE,YAAqB,CAAC;AACtD,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAE/C;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,YAAY,CAAC;IACxB,uIAAuI;IACvI,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IACtC,SAAS,EAAE,YAAY,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GACpB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E;;;GAGG;AACH,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACrB;AAED,sFAAsF;AACtF,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACzB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC9C,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAC5C;AAED,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC","sourcesContent":["/**\n * Core MCP data shapes. Settled before any logic consumes them, per the plan's U1:\n * a late change to `ServerState` or `CachedTool` is a rewrite of the server manager\n * and the proxy tool, where an early one is a one-line diff.\n */\n\nimport type { Capability } from \"../tools/contract.ts\";\n\n/**\n * How to reach a server. Inferred from which fields the config entry carries, never\n * declared: every MCP host writes `command` for stdio and `url` for HTTP, and none\n * writes a `transport` field, so requiring one would reject the config files users\n * already have.\n */\nexport type McpTransport =\n\t| {\n\t\t\tkind: \"stdio\";\n\t\t\tcommand: string;\n\t\t\targs: string[];\n\t\t\tenv: Record<string, string>;\n\t\t\tcwd: string | undefined;\n\t }\n\t| {\n\t\t\tkind: \"http\";\n\t\t\turl: string;\n\t\t\theaders: Record<string, string>;\n\t\t\t/** Environment variable holding a static bearer token. OAuth lives in the credential store (spec 2026-09-01-mcp-oauth). */\n\t\t\tbearerTokenEnv: string | undefined;\n\t };\n\nexport type McpLifecycle = \"lazy\" | \"eager\" | \"keep-alive\" | \"lazy-keep-alive\";\n\nexport const DEFAULT_LIFECYCLE: McpLifecycle = \"lazy\";\nexport const DEFAULT_IDLE_TIMEOUT_MINUTES = 10;\n\n/**\n * A server's declared authority, and the only place it is stated. The capability set\n * feeds the tool contract every tool on this server projects (ADR 0010), which is why\n * an absent set resolves to the full one rather than the empty one.\n */\nexport interface McpServerConfig {\n\tname: string;\n\ttransport: McpTransport;\n\t/** Set when the user opted this HTTP server into OAuth (spec 2026-09-01-mcp-oauth). Server-level, so metadata cache keys ignore it. */\n\tauth?: \"oauth\";\n\tcapabilities: ReadonlySet<Capability>;\n\tlifecycle: McpLifecycle;\n\tidleTimeoutMinutes: number;\n}\n\n/**\n * Connection state. A union rather than a `connected` boolean beside a `lastError`\n * string, so a state cannot carry data that is invalid for it.\n */\nexport type ServerState =\n\t| { kind: \"disconnected\" }\n\t| { kind: \"connecting\"; since: number }\n\t| { kind: \"ready\"; since: number; lastUsed: number }\n\t| { kind: \"failed\"; error: string; failedAt: number; retryAfter: number };\n\n/**\n * What the disk cache holds. Everything here is producible without a live connection,\n * which is the property that lets search and describe answer with no server running.\n */\nexport interface CachedTool {\n\tserver: string;\n\tname: string;\n\tdescription: string;\n\tinputSchema: unknown;\n}\n\n/** A config problem that dropped an entry or fell back to a default. Never thrown. */\nexport interface McpConfigDiagnostic {\n\tpath: string;\n\tserver: string | undefined;\n\tmessage: string;\n}\n\nexport interface McpConfig {\n\tservers: ReadonlyMap<string, McpServerConfig>;\n\tdiagnostics: readonly McpConfigDiagnostic[];\n}\n\n/** One tool call's result, in the shape the proxy tool converts to `AgentToolResult`. */\nexport interface McpCallResult {\n\tcontent: Array<{ type: \"text\"; text: string }>;\n\tisError?: boolean;\n}\n\n/**\n * A live session with one server. The seam that keeps the MCP SDK out of the\n * lifecycle logic, and lets the server manager be tested without a child process.\n */\nexport interface McpConnection {\n\tlistTools(): Promise<CachedTool[]>;\n\tcallTool(name: string, args: unknown): Promise<McpCallResult>;\n\tclose(): Promise<void>;\n}\n\nexport type McpConnector = (server: McpServerConfig) => Promise<McpConnection>;\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core MCP data shapes. Settled before any logic consumes them, per the plan's U1:
|
|
3
|
+
* a late change to `ServerState` or `CachedTool` is a rewrite of the server manager
|
|
4
|
+
* and the proxy tool, where an early one is a one-line diff.
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_LIFECYCLE = "lazy";
|
|
7
|
+
export const DEFAULT_IDLE_TIMEOUT_MINUTES = 10;
|
|
8
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/mcp/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA4BH,MAAM,CAAC,MAAM,iBAAiB,GAAiB,MAAM,CAAC;AACtD,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAC","sourcesContent":["/**\n * Core MCP data shapes. Settled before any logic consumes them, per the plan's U1:\n * a late change to `ServerState` or `CachedTool` is a rewrite of the server manager\n * and the proxy tool, where an early one is a one-line diff.\n */\n\nimport type { Capability } from \"../tools/contract.ts\";\n\n/**\n * How to reach a server. Inferred from which fields the config entry carries, never\n * declared: every MCP host writes `command` for stdio and `url` for HTTP, and none\n * writes a `transport` field, so requiring one would reject the config files users\n * already have.\n */\nexport type McpTransport =\n\t| {\n\t\t\tkind: \"stdio\";\n\t\t\tcommand: string;\n\t\t\targs: string[];\n\t\t\tenv: Record<string, string>;\n\t\t\tcwd: string | undefined;\n\t }\n\t| {\n\t\t\tkind: \"http\";\n\t\t\turl: string;\n\t\t\theaders: Record<string, string>;\n\t\t\t/** Environment variable holding a static bearer token. OAuth lives in the credential store (spec 2026-09-01-mcp-oauth). */\n\t\t\tbearerTokenEnv: string | undefined;\n\t };\n\nexport type McpLifecycle = \"lazy\" | \"eager\" | \"keep-alive\" | \"lazy-keep-alive\";\n\nexport const DEFAULT_LIFECYCLE: McpLifecycle = \"lazy\";\nexport const DEFAULT_IDLE_TIMEOUT_MINUTES = 10;\n\n/**\n * A server's declared authority, and the only place it is stated. The capability set\n * feeds the tool contract every tool on this server projects (ADR 0010), which is why\n * an absent set resolves to the full one rather than the empty one.\n */\nexport interface McpServerConfig {\n\tname: string;\n\ttransport: McpTransport;\n\t/** Set when the user opted this HTTP server into OAuth (spec 2026-09-01-mcp-oauth). Server-level, so metadata cache keys ignore it. */\n\tauth?: \"oauth\";\n\tcapabilities: ReadonlySet<Capability>;\n\tlifecycle: McpLifecycle;\n\tidleTimeoutMinutes: number;\n}\n\n/**\n * Connection state. A union rather than a `connected` boolean beside a `lastError`\n * string, so a state cannot carry data that is invalid for it.\n */\nexport type ServerState =\n\t| { kind: \"disconnected\" }\n\t| { kind: \"connecting\"; since: number }\n\t| { kind: \"ready\"; since: number; lastUsed: number }\n\t| { kind: \"failed\"; error: string; failedAt: number; retryAfter: number };\n\n/**\n * What the disk cache holds. Everything here is producible without a live connection,\n * which is the property that lets search and describe answer with no server running.\n */\nexport interface CachedTool {\n\tserver: string;\n\tname: string;\n\tdescription: string;\n\tinputSchema: unknown;\n}\n\n/** A config problem that dropped an entry or fell back to a default. Never thrown. */\nexport interface McpConfigDiagnostic {\n\tpath: string;\n\tserver: string | undefined;\n\tmessage: string;\n}\n\nexport interface McpConfig {\n\tservers: ReadonlyMap<string, McpServerConfig>;\n\tdiagnostics: readonly McpConfigDiagnostic[];\n}\n\n/** One tool call's result, in the shape the proxy tool converts to `AgentToolResult`. */\nexport interface McpCallResult {\n\tcontent: Array<{ type: \"text\"; text: string }>;\n\tisError?: boolean;\n}\n\n/**\n * A live session with one server. The seam that keeps the MCP SDK out of the\n * lifecycle logic, and lets the server manager be tested without a child process.\n */\nexport interface McpConnection {\n\tlistTools(): Promise<CachedTool[]>;\n\tcallTool(name: string, args: unknown): Promise<McpCallResult>;\n\tclose(): Promise<void>;\n}\n\nexport type McpConnector = (server: McpServerConfig) => Promise<McpConnection>;\n"]}
|
|
@@ -38,6 +38,7 @@ declare const ModelDefinitionSchema: Type.TObject<{
|
|
|
38
38
|
supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
|
|
39
39
|
supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
|
|
40
40
|
supportsUsageInStreaming: Type.TOptional<Type.TBoolean>;
|
|
41
|
+
supportsFinishReason: Type.TOptional<Type.TBoolean>;
|
|
41
42
|
maxTokensField: Type.TOptional<Type.TUnion<[Type.TLiteral<"max_completion_tokens">, Type.TLiteral<"max_tokens">]>>;
|
|
42
43
|
requiresToolResultName: Type.TOptional<Type.TBoolean>;
|
|
43
44
|
requiresAssistantAfterToolResult: Type.TOptional<Type.TBoolean>;
|
|
@@ -152,6 +153,7 @@ declare const ModelOverrideSchema: Type.TObject<{
|
|
|
152
153
|
supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
|
|
153
154
|
supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
|
|
154
155
|
supportsUsageInStreaming: Type.TOptional<Type.TBoolean>;
|
|
156
|
+
supportsFinishReason: Type.TOptional<Type.TBoolean>;
|
|
155
157
|
maxTokensField: Type.TOptional<Type.TUnion<[Type.TLiteral<"max_completion_tokens">, Type.TLiteral<"max_tokens">]>>;
|
|
156
158
|
requiresToolResultName: Type.TOptional<Type.TBoolean>;
|
|
157
159
|
requiresAssistantAfterToolResult: Type.TOptional<Type.TBoolean>;
|
|
@@ -243,6 +245,7 @@ declare const ProviderConfigSchema: Type.TObject<{
|
|
|
243
245
|
supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
|
|
244
246
|
supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
|
|
245
247
|
supportsUsageInStreaming: Type.TOptional<Type.TBoolean>;
|
|
248
|
+
supportsFinishReason: Type.TOptional<Type.TBoolean>;
|
|
246
249
|
maxTokensField: Type.TOptional<Type.TUnion<[Type.TLiteral<"max_completion_tokens">, Type.TLiteral<"max_tokens">]>>;
|
|
247
250
|
requiresToolResultName: Type.TOptional<Type.TBoolean>;
|
|
248
251
|
requiresAssistantAfterToolResult: Type.TOptional<Type.TBoolean>;
|
|
@@ -360,6 +363,7 @@ declare const ProviderConfigSchema: Type.TObject<{
|
|
|
360
363
|
supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
|
|
361
364
|
supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
|
|
362
365
|
supportsUsageInStreaming: Type.TOptional<Type.TBoolean>;
|
|
366
|
+
supportsFinishReason: Type.TOptional<Type.TBoolean>;
|
|
363
367
|
maxTokensField: Type.TOptional<Type.TUnion<[Type.TLiteral<"max_completion_tokens">, Type.TLiteral<"max_tokens">]>>;
|
|
364
368
|
requiresToolResultName: Type.TOptional<Type.TBoolean>;
|
|
365
369
|
requiresAssistantAfterToolResult: Type.TOptional<Type.TBoolean>;
|
|
@@ -474,6 +478,7 @@ declare const ProviderConfigSchema: Type.TObject<{
|
|
|
474
478
|
supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
|
|
475
479
|
supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
|
|
476
480
|
supportsUsageInStreaming: Type.TOptional<Type.TBoolean>;
|
|
481
|
+
supportsFinishReason: Type.TOptional<Type.TBoolean>;
|
|
477
482
|
maxTokensField: Type.TOptional<Type.TUnion<[Type.TLiteral<"max_completion_tokens">, Type.TLiteral<"max_tokens">]>>;
|
|
478
483
|
requiresToolResultName: Type.TOptional<Type.TBoolean>;
|
|
479
484
|
requiresAssistantAfterToolResult: Type.TOptional<Type.TBoolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-config.d.ts","sourceRoot":"","sources":["../../src/core/model-config.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAGxD,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAyJ5C,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAczB,CAAC;AAEH,QAAA,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmBvB,CAAC;AAEH,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWxB,CAAC;AAEH,QAAA,MAAM,oBAAoB;IACzB,0EAA0E;;EAEzE,CAAC;AASH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACnE,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACrE,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAsB3E,yCAAyC;AACzC,qBAAa,WAAW;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;IACpE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyC;IAC/D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAE3C,OAAO,eAQN;IAED,OAAa,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CA6C1E;IAED,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAE9D;IAED,cAAc,IAAI,SAAS,MAAM,EAAE,CAElC;IAED,oFAAoF;IACpF,QAAQ,IAAI,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAEjD;IAED,QAAQ,IAAI,MAAM,GAAG,SAAS,CAE7B;CACD","sourcesContent":["/** Immutable, credential-blind models.json snapshot. */\n\nimport { readFile } from \"node:fs/promises\";\nimport { type Static, Type } from \"typebox\";\nimport { Compile } from \"typebox/compile\";\nimport type { TLocalizedValidationError } from \"typebox/error\";\nimport { stripJsonComments } from \"../utils/json.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\n\nconst PercentileCutoffsSchema = Type.Object({\n\tp50: Type.Optional(Type.Number()),\n\tp75: Type.Optional(Type.Number()),\n\tp90: Type.Optional(Type.Number()),\n\tp99: Type.Optional(Type.Number()),\n});\n\nconst OpenRouterRoutingSchema = Type.Object({\n\tallow_fallbacks: Type.Optional(Type.Boolean()),\n\trequire_parameters: Type.Optional(Type.Boolean()),\n\tdata_collection: Type.Optional(Type.Union([Type.Literal(\"deny\"), Type.Literal(\"allow\")])),\n\tzdr: Type.Optional(Type.Boolean()),\n\tenforce_distillable_text: Type.Optional(Type.Boolean()),\n\torder: Type.Optional(Type.Array(Type.String())),\n\tonly: Type.Optional(Type.Array(Type.String())),\n\tignore: Type.Optional(Type.Array(Type.String())),\n\tquantizations: Type.Optional(Type.Array(Type.String())),\n\tsort: Type.Optional(\n\t\tType.Union([\n\t\t\tType.String(),\n\t\t\tType.Object({\n\t\t\t\tby: Type.Optional(Type.String()),\n\t\t\t\tpartition: Type.Optional(Type.Union([Type.String(), Type.Null()])),\n\t\t\t}),\n\t\t]),\n\t),\n\tmax_price: Type.Optional(\n\t\tType.Object({\n\t\t\tprompt: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\tcompletion: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\timage: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\taudio: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\trequest: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t}),\n\t),\n\tpreferred_min_throughput: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n\tpreferred_max_latency: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n});\n\nconst VercelGatewayRoutingSchema = Type.Object({\n\tonly: Type.Optional(Type.Array(Type.String())),\n\torder: Type.Optional(Type.Array(Type.String())),\n});\n\nconst ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);\nconst ThinkingLevelMapSchema = Type.Object({\n\toff: Type.Optional(ThinkingLevelMapValueSchema),\n\tminimal: Type.Optional(ThinkingLevelMapValueSchema),\n\tlow: Type.Optional(ThinkingLevelMapValueSchema),\n\tmedium: Type.Optional(ThinkingLevelMapValueSchema),\n\thigh: Type.Optional(ThinkingLevelMapValueSchema),\n\txhigh: Type.Optional(ThinkingLevelMapValueSchema),\n\tmax: Type.Optional(ThinkingLevelMapValueSchema),\n});\n\nconst ChatTemplateKwargScalarSchema = Type.Union([Type.String(), Type.Number(), Type.Boolean(), Type.Null()]);\nconst ChatTemplateKwargVariableSchema = Type.Object({\n\t$var: Type.Union([Type.Literal(\"thinking.enabled\"), Type.Literal(\"thinking.effort\")]),\n\tomitWhenOff: Type.Optional(Type.Boolean()),\n});\nconst ChatTemplateKwargSchema = Type.Union([ChatTemplateKwargScalarSchema, ChatTemplateKwargVariableSchema]);\n\nconst OpenAICompletionsCompatSchema = Type.Object({\n\tsupportsStore: Type.Optional(Type.Boolean()),\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsupportsReasoningEffort: Type.Optional(Type.Boolean()),\n\tsupportsUsageInStreaming: Type.Optional(Type.Boolean()),\n\tmaxTokensField: Type.Optional(Type.Union([Type.Literal(\"max_completion_tokens\"), Type.Literal(\"max_tokens\")])),\n\trequiresToolResultName: Type.Optional(Type.Boolean()),\n\trequiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),\n\trequiresThinkingAsText: Type.Optional(Type.Boolean()),\n\trequiresReasoningContentOnAssistantMessages: Type.Optional(Type.Boolean()),\n\tthinkingFormat: Type.Optional(\n\t\tType.Union([\n\t\t\tType.Literal(\"openai\"),\n\t\t\tType.Literal(\"openrouter\"),\n\t\t\tType.Literal(\"together\"),\n\t\t\tType.Literal(\"baseten\"),\n\t\t\tType.Literal(\"deepseek\"),\n\t\t\tType.Literal(\"zai\"),\n\t\t\tType.Literal(\"qwen\"),\n\t\t\tType.Literal(\"chat-template\"),\n\t\t\tType.Literal(\"qwen-chat-template\"),\n\t\t\tType.Literal(\"string-thinking\"),\n\t\t\tType.Literal(\"ant-ling\"),\n\t\t]),\n\t),\n\tchatTemplateKwargs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),\n\tchatTemplateArgs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),\n\tcacheControlFormat: Type.Optional(Type.Literal(\"anthropic\")),\n\topenRouterRouting: Type.Optional(OpenRouterRoutingSchema),\n\tvercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tdeferredToolsMode: Type.Optional(Type.Literal(\"kimi\")),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n});\n\nconst OpenAIResponsesCompatSchema = Type.Object({\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsAdditionalTools: Type.Optional(Type.Boolean()),\n\tsupportsToolSearch: Type.Optional(Type.Boolean()),\n});\n\nconst AnthropicMessagesCompatSchema = Type.Object({\n\tsupportsEagerToolInputStreaming: Type.Optional(Type.Boolean()),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tsupportsCacheControlOnTools: Type.Optional(Type.Boolean()),\n\tsupportsTemperature: Type.Optional(Type.Boolean()),\n\tforceAdaptiveThinking: Type.Optional(Type.Boolean()),\n\tallowEmptySignature: Type.Optional(Type.Boolean()),\n\tsupportsStrictTools: Type.Optional(Type.Boolean()),\n\tsupportsToolReferences: Type.Optional(Type.Boolean()),\n});\n\nconst ProviderCompatSchema = Type.Union([\n\tOpenAICompletionsCompatSchema,\n\tOpenAIResponsesCompatSchema,\n\tAnthropicMessagesCompatSchema,\n]);\n\nconst ModelCostRatesSchema = {\n\tinput: Type.Number(),\n\toutput: Type.Number(),\n\tcacheRead: Type.Number(),\n\tcacheWrite: Type.Number(),\n};\nconst ModelCostTierSchema = Type.Object({\n\tinputTokensAbove: Type.Number(),\n\t...ModelCostRatesSchema,\n});\nconst ModelCostSchema = Type.Object({\n\t...ModelCostRatesSchema,\n\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n});\n\nconst ModelDefinitionSchema = Type.Object({\n\tid: Type.String({ minLength: 1 }),\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(ModelCostSchema),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\tsamplingParams: Type.Optional(Type.Record(Type.String(), Type.Unknown())),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ModelOverrideSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(\n\t\tType.Object({\n\t\t\tinput: Type.Optional(Type.Number()),\n\t\t\toutput: Type.Optional(Type.Number()),\n\t\t\tcacheRead: Type.Optional(Type.Number()),\n\t\t\tcacheWrite: Type.Optional(Type.Number()),\n\t\t\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n\t\t}),\n\t),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\tsamplingParams: Type.Optional(Type.Record(Type.String(), Type.Unknown())),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ProviderConfigSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\tapiKey: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\toauth: Type.Optional(Type.Literal(\"radius\")),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n\tauthHeader: Type.Optional(Type.Boolean()),\n\tmodels: Type.Optional(Type.Array(ModelDefinitionSchema)),\n\tmodelOverrides: Type.Optional(Type.Record(Type.String(), ModelOverrideSchema)),\n});\n\nconst RoleDefinitionSchema = Type.Object({\n\t/** Ordered, non-empty list of canonical \"provider/modelId\" candidates. */\n\tmodels: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),\n});\n\nconst ModelsConfigSchema = Type.Object({\n\tproviders: Type.Record(Type.String(), ProviderConfigSchema),\n\t/** Additive, credential-blind role -> ordered model-candidate mapping. */\n\troles: Type.Optional(Type.Record(Type.String(), RoleDefinitionSchema)),\n});\nconst validateModelsConfig = Compile(ModelsConfigSchema);\n\nexport type ModelsJsonModel = Static<typeof ModelDefinitionSchema>;\nexport type ModelsJsonModelOverride = Static<typeof ModelOverrideSchema>;\nexport type ModelsJsonProvider = Static<typeof ProviderConfigSchema>;\nexport type ModelsJsonRoleDefinition = Static<typeof RoleDefinitionSchema>;\ntype ModelsJson = Static<typeof ModelsConfigSchema>;\n\nfunction formatValidationPath(error: TLocalizedValidationError): string {\n\tif (error.keyword === \"required\") {\n\t\tconst requiredProperties = (error.params as { requiredProperties?: string[] }).requiredProperties;\n\t\tconst requiredProperty = requiredProperties?.[0];\n\t\tif (requiredProperty) {\n\t\t\tconst basePath = error.instancePath.replace(/^\\//, \"\").replace(/\\//g, \".\");\n\t\t\treturn basePath ? `${basePath}.${requiredProperty}` : requiredProperty;\n\t\t}\n\t}\n\tconst path = error.instancePath.replace(/^\\//, \"\").replace(/\\//g, \".\");\n\treturn path || \"root\";\n}\n\nfunction deepFreeze<T>(value: T): T {\n\tif (typeof value !== \"object\" || value === null || Object.isFrozen(value)) return value;\n\tfor (const child of Object.values(value)) deepFreeze(child);\n\treturn Object.freeze(value);\n}\n\n/** One immutable load of models.json. */\nexport class ModelConfig {\n\tprivate readonly providers: ReadonlyMap<string, ModelsJsonProvider>;\n\tprivate readonly roles: ReadonlyMap<string, readonly string[]>;\n\tprivate readonly error: string | undefined;\n\n\tprivate constructor(\n\t\tproviders: ReadonlyMap<string, ModelsJsonProvider>,\n\t\troles: ReadonlyMap<string, readonly string[]> = new Map(),\n\t\terror?: string,\n\t) {\n\t\tthis.providers = providers;\n\t\tthis.roles = roles;\n\t\tthis.error = error;\n\t}\n\n\tstatic async load(modelsJsonPath: string | undefined): Promise<ModelConfig> {\n\t\tif (!modelsJsonPath) return new ModelConfig(new Map());\n\t\tconst path = normalizePath(modelsJsonPath);\n\t\tlet content: string;\n\t\ttry {\n\t\t\tcontent = await readFile(path, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return new ModelConfig(new Map());\n\t\t\treturn new ModelConfig(\n\t\t\t\tnew Map(),\n\t\t\t\tnew Map(),\n\t\t\t\t`Failed to load models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${path}`,\n\t\t\t);\n\t\t}\n\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(stripJsonComments(content));\n\t\t} catch (error) {\n\t\t\treturn new ModelConfig(\n\t\t\t\tnew Map(),\n\t\t\t\tnew Map(),\n\t\t\t\t`Failed to parse models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${path}`,\n\t\t\t);\n\t\t}\n\n\t\tif (!validateModelsConfig.Check(parsed)) {\n\t\t\tconst errors =\n\t\t\t\tvalidateModelsConfig\n\t\t\t\t\t.Errors(parsed)\n\t\t\t\t\t.map((error) => ` - ${formatValidationPath(error)}: ${error.message}`)\n\t\t\t\t\t.join(\"\\n\") || \"Unknown schema error\";\n\t\t\treturn new ModelConfig(new Map(), new Map(), `Invalid models.json schema:\\n${errors}\\n\\nFile: ${path}`);\n\t\t}\n\n\t\tconst config = parsed as ModelsJson;\n\t\tconst providers = new Map<string, ModelsJsonProvider>();\n\t\tfor (const [providerId, provider] of Object.entries(config.providers)) {\n\t\t\tproviders.set(providerId, deepFreeze(structuredClone(provider)));\n\t\t}\n\t\tconst roles = new Map<string, readonly string[]>();\n\t\tfor (const [roleName, role] of Object.entries(config.roles ?? {})) {\n\t\t\troles.set(roleName, Object.freeze([...role.models]));\n\t\t}\n\t\treturn new ModelConfig(providers, roles);\n\t}\n\n\tgetProvider(providerId: string): ModelsJsonProvider | undefined {\n\t\treturn this.providers.get(providerId);\n\t}\n\n\tgetProviderIds(): readonly string[] {\n\t\treturn [...this.providers.keys()];\n\t}\n\n\t/** Additive role -> ordered \"provider/modelId\" candidate references, unresolved. */\n\tgetRoles(): ReadonlyMap<string, readonly string[]> {\n\t\treturn this.roles;\n\t}\n\n\tgetError(): string | undefined {\n\t\treturn this.error;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"model-config.d.ts","sourceRoot":"","sources":["../../src/core/model-config.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAGxD,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AA2J5C,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAczB,CAAC;AAEH,QAAA,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmBvB,CAAC;AAEH,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWxB,CAAC;AAEH,QAAA,MAAM,oBAAoB;IACzB,0EAA0E;;EAEzE,CAAC;AASH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACnE,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACrE,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAsB3E,yCAAyC;AACzC,qBAAa,WAAW;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;IACpE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyC;IAC/D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAE3C,OAAO,eAQN;IAED,OAAa,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CA6C1E;IAED,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAE9D;IAED,cAAc,IAAI,SAAS,MAAM,EAAE,CAElC;IAED,oFAAoF;IACpF,QAAQ,IAAI,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAEjD;IAED,QAAQ,IAAI,MAAM,GAAG,SAAS,CAE7B;CACD","sourcesContent":["/** Immutable, credential-blind models.json snapshot. */\n\nimport { readFile } from \"node:fs/promises\";\nimport { type Static, Type } from \"typebox\";\nimport { Compile } from \"typebox/compile\";\nimport type { TLocalizedValidationError } from \"typebox/error\";\nimport { stripJsonComments } from \"../utils/json.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { stripBom } from \"../utils/text.ts\";\n\nconst PercentileCutoffsSchema = Type.Object({\n\tp50: Type.Optional(Type.Number()),\n\tp75: Type.Optional(Type.Number()),\n\tp90: Type.Optional(Type.Number()),\n\tp99: Type.Optional(Type.Number()),\n});\n\nconst OpenRouterRoutingSchema = Type.Object({\n\tallow_fallbacks: Type.Optional(Type.Boolean()),\n\trequire_parameters: Type.Optional(Type.Boolean()),\n\tdata_collection: Type.Optional(Type.Union([Type.Literal(\"deny\"), Type.Literal(\"allow\")])),\n\tzdr: Type.Optional(Type.Boolean()),\n\tenforce_distillable_text: Type.Optional(Type.Boolean()),\n\torder: Type.Optional(Type.Array(Type.String())),\n\tonly: Type.Optional(Type.Array(Type.String())),\n\tignore: Type.Optional(Type.Array(Type.String())),\n\tquantizations: Type.Optional(Type.Array(Type.String())),\n\tsort: Type.Optional(\n\t\tType.Union([\n\t\t\tType.String(),\n\t\t\tType.Object({\n\t\t\t\tby: Type.Optional(Type.String()),\n\t\t\t\tpartition: Type.Optional(Type.Union([Type.String(), Type.Null()])),\n\t\t\t}),\n\t\t]),\n\t),\n\tmax_price: Type.Optional(\n\t\tType.Object({\n\t\t\tprompt: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\tcompletion: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\timage: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\taudio: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\trequest: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t}),\n\t),\n\tpreferred_min_throughput: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n\tpreferred_max_latency: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n});\n\nconst VercelGatewayRoutingSchema = Type.Object({\n\tonly: Type.Optional(Type.Array(Type.String())),\n\torder: Type.Optional(Type.Array(Type.String())),\n});\n\nconst ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);\nconst ThinkingLevelMapSchema = Type.Object({\n\toff: Type.Optional(ThinkingLevelMapValueSchema),\n\tminimal: Type.Optional(ThinkingLevelMapValueSchema),\n\tlow: Type.Optional(ThinkingLevelMapValueSchema),\n\tmedium: Type.Optional(ThinkingLevelMapValueSchema),\n\thigh: Type.Optional(ThinkingLevelMapValueSchema),\n\txhigh: Type.Optional(ThinkingLevelMapValueSchema),\n\tmax: Type.Optional(ThinkingLevelMapValueSchema),\n});\n\nconst ChatTemplateKwargScalarSchema = Type.Union([Type.String(), Type.Number(), Type.Boolean(), Type.Null()]);\nconst ChatTemplateKwargVariableSchema = Type.Object({\n\t$var: Type.Union([Type.Literal(\"thinking.enabled\"), Type.Literal(\"thinking.effort\")]),\n\tomitWhenOff: Type.Optional(Type.Boolean()),\n});\nconst ChatTemplateKwargSchema = Type.Union([ChatTemplateKwargScalarSchema, ChatTemplateKwargVariableSchema]);\n\nconst OpenAICompletionsCompatSchema = Type.Object({\n\tsupportsStore: Type.Optional(Type.Boolean()),\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsupportsReasoningEffort: Type.Optional(Type.Boolean()),\n\tsupportsUsageInStreaming: Type.Optional(Type.Boolean()),\n\tsupportsFinishReason: Type.Optional(Type.Boolean()),\n\tmaxTokensField: Type.Optional(Type.Union([Type.Literal(\"max_completion_tokens\"), Type.Literal(\"max_tokens\")])),\n\trequiresToolResultName: Type.Optional(Type.Boolean()),\n\trequiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),\n\trequiresThinkingAsText: Type.Optional(Type.Boolean()),\n\trequiresReasoningContentOnAssistantMessages: Type.Optional(Type.Boolean()),\n\tthinkingFormat: Type.Optional(\n\t\tType.Union([\n\t\t\tType.Literal(\"openai\"),\n\t\t\tType.Literal(\"openrouter\"),\n\t\t\tType.Literal(\"together\"),\n\t\t\tType.Literal(\"baseten\"),\n\t\t\tType.Literal(\"deepseek\"),\n\t\t\tType.Literal(\"zai\"),\n\t\t\tType.Literal(\"qwen\"),\n\t\t\tType.Literal(\"chat-template\"),\n\t\t\tType.Literal(\"qwen-chat-template\"),\n\t\t\tType.Literal(\"string-thinking\"),\n\t\t\tType.Literal(\"ant-ling\"),\n\t\t]),\n\t),\n\tchatTemplateKwargs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),\n\tchatTemplateArgs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),\n\tcacheControlFormat: Type.Optional(Type.Literal(\"anthropic\")),\n\topenRouterRouting: Type.Optional(OpenRouterRoutingSchema),\n\tvercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tdeferredToolsMode: Type.Optional(Type.Literal(\"kimi\")),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n});\n\nconst OpenAIResponsesCompatSchema = Type.Object({\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsAdditionalTools: Type.Optional(Type.Boolean()),\n\tsupportsToolSearch: Type.Optional(Type.Boolean()),\n});\n\nconst AnthropicMessagesCompatSchema = Type.Object({\n\tsupportsEagerToolInputStreaming: Type.Optional(Type.Boolean()),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tsupportsCacheControlOnTools: Type.Optional(Type.Boolean()),\n\tsupportsTemperature: Type.Optional(Type.Boolean()),\n\tforceAdaptiveThinking: Type.Optional(Type.Boolean()),\n\tallowEmptySignature: Type.Optional(Type.Boolean()),\n\tsupportsStrictTools: Type.Optional(Type.Boolean()),\n\tsupportsToolReferences: Type.Optional(Type.Boolean()),\n});\n\nconst ProviderCompatSchema = Type.Union([\n\tOpenAICompletionsCompatSchema,\n\tOpenAIResponsesCompatSchema,\n\tAnthropicMessagesCompatSchema,\n]);\n\nconst ModelCostRatesSchema = {\n\tinput: Type.Number(),\n\toutput: Type.Number(),\n\tcacheRead: Type.Number(),\n\tcacheWrite: Type.Number(),\n};\nconst ModelCostTierSchema = Type.Object({\n\tinputTokensAbove: Type.Number(),\n\t...ModelCostRatesSchema,\n});\nconst ModelCostSchema = Type.Object({\n\t...ModelCostRatesSchema,\n\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n});\n\nconst ModelDefinitionSchema = Type.Object({\n\tid: Type.String({ minLength: 1 }),\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(ModelCostSchema),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\tsamplingParams: Type.Optional(Type.Record(Type.String(), Type.Unknown())),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ModelOverrideSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(\n\t\tType.Object({\n\t\t\tinput: Type.Optional(Type.Number()),\n\t\t\toutput: Type.Optional(Type.Number()),\n\t\t\tcacheRead: Type.Optional(Type.Number()),\n\t\t\tcacheWrite: Type.Optional(Type.Number()),\n\t\t\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n\t\t}),\n\t),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\tsamplingParams: Type.Optional(Type.Record(Type.String(), Type.Unknown())),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ProviderConfigSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\tapiKey: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\toauth: Type.Optional(Type.Literal(\"radius\")),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n\tauthHeader: Type.Optional(Type.Boolean()),\n\tmodels: Type.Optional(Type.Array(ModelDefinitionSchema)),\n\tmodelOverrides: Type.Optional(Type.Record(Type.String(), ModelOverrideSchema)),\n});\n\nconst RoleDefinitionSchema = Type.Object({\n\t/** Ordered, non-empty list of canonical \"provider/modelId\" candidates. */\n\tmodels: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),\n});\n\nconst ModelsConfigSchema = Type.Object({\n\tproviders: Type.Record(Type.String(), ProviderConfigSchema),\n\t/** Additive, credential-blind role -> ordered model-candidate mapping. */\n\troles: Type.Optional(Type.Record(Type.String(), RoleDefinitionSchema)),\n});\nconst validateModelsConfig = Compile(ModelsConfigSchema);\n\nexport type ModelsJsonModel = Static<typeof ModelDefinitionSchema>;\nexport type ModelsJsonModelOverride = Static<typeof ModelOverrideSchema>;\nexport type ModelsJsonProvider = Static<typeof ProviderConfigSchema>;\nexport type ModelsJsonRoleDefinition = Static<typeof RoleDefinitionSchema>;\ntype ModelsJson = Static<typeof ModelsConfigSchema>;\n\nfunction formatValidationPath(error: TLocalizedValidationError): string {\n\tif (error.keyword === \"required\") {\n\t\tconst requiredProperties = (error.params as { requiredProperties?: string[] }).requiredProperties;\n\t\tconst requiredProperty = requiredProperties?.[0];\n\t\tif (requiredProperty) {\n\t\t\tconst basePath = error.instancePath.replace(/^\\//, \"\").replace(/\\//g, \".\");\n\t\t\treturn basePath ? `${basePath}.${requiredProperty}` : requiredProperty;\n\t\t}\n\t}\n\tconst path = error.instancePath.replace(/^\\//, \"\").replace(/\\//g, \".\");\n\treturn path || \"root\";\n}\n\nfunction deepFreeze<T>(value: T): T {\n\tif (typeof value !== \"object\" || value === null || Object.isFrozen(value)) return value;\n\tfor (const child of Object.values(value)) deepFreeze(child);\n\treturn Object.freeze(value);\n}\n\n/** One immutable load of models.json. */\nexport class ModelConfig {\n\tprivate readonly providers: ReadonlyMap<string, ModelsJsonProvider>;\n\tprivate readonly roles: ReadonlyMap<string, readonly string[]>;\n\tprivate readonly error: string | undefined;\n\n\tprivate constructor(\n\t\tproviders: ReadonlyMap<string, ModelsJsonProvider>,\n\t\troles: ReadonlyMap<string, readonly string[]> = new Map(),\n\t\terror?: string,\n\t) {\n\t\tthis.providers = providers;\n\t\tthis.roles = roles;\n\t\tthis.error = error;\n\t}\n\n\tstatic async load(modelsJsonPath: string | undefined): Promise<ModelConfig> {\n\t\tif (!modelsJsonPath) return new ModelConfig(new Map());\n\t\tconst path = normalizePath(modelsJsonPath);\n\t\tlet content: string;\n\t\ttry {\n\t\t\tcontent = await readFile(path, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return new ModelConfig(new Map());\n\t\t\treturn new ModelConfig(\n\t\t\t\tnew Map(),\n\t\t\t\tnew Map(),\n\t\t\t\t`Failed to load models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${path}`,\n\t\t\t);\n\t\t}\n\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(stripJsonComments(stripBom(content)));\n\t\t} catch (error) {\n\t\t\treturn new ModelConfig(\n\t\t\t\tnew Map(),\n\t\t\t\tnew Map(),\n\t\t\t\t`Failed to parse models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${path}`,\n\t\t\t);\n\t\t}\n\n\t\tif (!validateModelsConfig.Check(parsed)) {\n\t\t\tconst errors =\n\t\t\t\tvalidateModelsConfig\n\t\t\t\t\t.Errors(parsed)\n\t\t\t\t\t.map((error) => ` - ${formatValidationPath(error)}: ${error.message}`)\n\t\t\t\t\t.join(\"\\n\") || \"Unknown schema error\";\n\t\t\treturn new ModelConfig(new Map(), new Map(), `Invalid models.json schema:\\n${errors}\\n\\nFile: ${path}`);\n\t\t}\n\n\t\tconst config = parsed as ModelsJson;\n\t\tconst providers = new Map<string, ModelsJsonProvider>();\n\t\tfor (const [providerId, provider] of Object.entries(config.providers)) {\n\t\t\tproviders.set(providerId, deepFreeze(structuredClone(provider)));\n\t\t}\n\t\tconst roles = new Map<string, readonly string[]>();\n\t\tfor (const [roleName, role] of Object.entries(config.roles ?? {})) {\n\t\t\troles.set(roleName, Object.freeze([...role.models]));\n\t\t}\n\t\treturn new ModelConfig(providers, roles);\n\t}\n\n\tgetProvider(providerId: string): ModelsJsonProvider | undefined {\n\t\treturn this.providers.get(providerId);\n\t}\n\n\tgetProviderIds(): readonly string[] {\n\t\treturn [...this.providers.keys()];\n\t}\n\n\t/** Additive role -> ordered \"provider/modelId\" candidate references, unresolved. */\n\tgetRoles(): ReadonlyMap<string, readonly string[]> {\n\t\treturn this.roles;\n\t}\n\n\tgetError(): string | undefined {\n\t\treturn this.error;\n\t}\n}\n"]}
|
|
@@ -4,6 +4,7 @@ import { Type } from "typebox";
|
|
|
4
4
|
import { Compile } from "typebox/compile";
|
|
5
5
|
import { stripJsonComments } from "../utils/json.js";
|
|
6
6
|
import { normalizePath } from "../utils/paths.js";
|
|
7
|
+
import { stripBom } from "../utils/text.js";
|
|
7
8
|
const PercentileCutoffsSchema = Type.Object({
|
|
8
9
|
p50: Type.Optional(Type.Number()),
|
|
9
10
|
p75: Type.Optional(Type.Number()),
|
|
@@ -62,6 +63,7 @@ const OpenAICompletionsCompatSchema = Type.Object({
|
|
|
62
63
|
supportsDeveloperRole: Type.Optional(Type.Boolean()),
|
|
63
64
|
supportsReasoningEffort: Type.Optional(Type.Boolean()),
|
|
64
65
|
supportsUsageInStreaming: Type.Optional(Type.Boolean()),
|
|
66
|
+
supportsFinishReason: Type.Optional(Type.Boolean()),
|
|
65
67
|
maxTokensField: Type.Optional(Type.Union([Type.Literal("max_completion_tokens"), Type.Literal("max_tokens")])),
|
|
66
68
|
requiresToolResultName: Type.Optional(Type.Boolean()),
|
|
67
69
|
requiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),
|
|
@@ -230,7 +232,7 @@ export class ModelConfig {
|
|
|
230
232
|
}
|
|
231
233
|
let parsed;
|
|
232
234
|
try {
|
|
233
|
-
parsed = JSON.parse(stripJsonComments(content));
|
|
235
|
+
parsed = JSON.parse(stripJsonComments(stripBom(content)));
|
|
234
236
|
}
|
|
235
237
|
catch (error) {
|
|
236
238
|
return new ModelConfig(new Map(), new Map(), `Failed to parse models.json: ${error instanceof Error ? error.message : error}\n\nFile: ${path}`);
|