whale-code 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/bin/swag-agent.js +9 -0
- package/bin/swagmanager-mcp.js +321 -0
- package/dist/cli/app.d.ts +26 -0
- package/dist/cli/app.js +64 -0
- package/dist/cli/chat/AgentSelector.d.ts +14 -0
- package/dist/cli/chat/AgentSelector.js +14 -0
- package/dist/cli/chat/ChatApp.d.ts +9 -0
- package/dist/cli/chat/ChatApp.js +267 -0
- package/dist/cli/chat/ChatInput.d.ts +39 -0
- package/dist/cli/chat/ChatInput.js +509 -0
- package/dist/cli/chat/MarkdownText.d.ts +10 -0
- package/dist/cli/chat/MarkdownText.js +20 -0
- package/dist/cli/chat/MessageList.d.ts +37 -0
- package/dist/cli/chat/MessageList.js +80 -0
- package/dist/cli/chat/ModelSelector.d.ts +20 -0
- package/dist/cli/chat/ModelSelector.js +73 -0
- package/dist/cli/chat/RewindViewer.d.ts +26 -0
- package/dist/cli/chat/RewindViewer.js +185 -0
- package/dist/cli/chat/StoreSelector.d.ts +14 -0
- package/dist/cli/chat/StoreSelector.js +24 -0
- package/dist/cli/chat/StreamingText.d.ts +12 -0
- package/dist/cli/chat/StreamingText.js +12 -0
- package/dist/cli/chat/SubagentPanel.d.ts +45 -0
- package/dist/cli/chat/SubagentPanel.js +110 -0
- package/dist/cli/chat/TeamPanel.d.ts +21 -0
- package/dist/cli/chat/TeamPanel.js +42 -0
- package/dist/cli/chat/ToolIndicator.d.ts +25 -0
- package/dist/cli/chat/ToolIndicator.js +436 -0
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +39 -0
- package/dist/cli/chat/hooks/useAgentLoop.js +382 -0
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +37 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +387 -0
- package/dist/cli/commands/config-cmd.d.ts +10 -0
- package/dist/cli/commands/config-cmd.js +99 -0
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.js +172 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.js +278 -0
- package/dist/cli/commands/mcp.d.ts +12 -0
- package/dist/cli/commands/mcp.js +162 -0
- package/dist/cli/login/LoginApp.d.ts +7 -0
- package/dist/cli/login/LoginApp.js +157 -0
- package/dist/cli/print-mode.d.ts +31 -0
- package/dist/cli/print-mode.js +202 -0
- package/dist/cli/serve-mode.d.ts +37 -0
- package/dist/cli/serve-mode.js +636 -0
- package/dist/cli/services/agent-definitions.d.ts +25 -0
- package/dist/cli/services/agent-definitions.js +91 -0
- package/dist/cli/services/agent-events.d.ts +178 -0
- package/dist/cli/services/agent-events.js +175 -0
- package/dist/cli/services/agent-loop.d.ts +90 -0
- package/dist/cli/services/agent-loop.js +762 -0
- package/dist/cli/services/agent-worker-base.d.ts +97 -0
- package/dist/cli/services/agent-worker-base.js +220 -0
- package/dist/cli/services/auth-service.d.ts +30 -0
- package/dist/cli/services/auth-service.js +160 -0
- package/dist/cli/services/background-processes.d.ts +126 -0
- package/dist/cli/services/background-processes.js +318 -0
- package/dist/cli/services/browser-auth.d.ts +24 -0
- package/dist/cli/services/browser-auth.js +180 -0
- package/dist/cli/services/claude-md-loader.d.ts +16 -0
- package/dist/cli/services/claude-md-loader.js +58 -0
- package/dist/cli/services/config-store.d.ts +47 -0
- package/dist/cli/services/config-store.js +79 -0
- package/dist/cli/services/debug-log.d.ts +10 -0
- package/dist/cli/services/debug-log.js +52 -0
- package/dist/cli/services/error-logger.d.ts +58 -0
- package/dist/cli/services/error-logger.js +269 -0
- package/dist/cli/services/file-history.d.ts +21 -0
- package/dist/cli/services/file-history.js +83 -0
- package/dist/cli/services/format-server-response.d.ts +16 -0
- package/dist/cli/services/format-server-response.js +440 -0
- package/dist/cli/services/git-context.d.ts +11 -0
- package/dist/cli/services/git-context.js +66 -0
- package/dist/cli/services/hooks.d.ts +85 -0
- package/dist/cli/services/hooks.js +258 -0
- package/dist/cli/services/interactive-tools.d.ts +125 -0
- package/dist/cli/services/interactive-tools.js +260 -0
- package/dist/cli/services/keybinding-manager.d.ts +52 -0
- package/dist/cli/services/keybinding-manager.js +115 -0
- package/dist/cli/services/local-tools.d.ts +22 -0
- package/dist/cli/services/local-tools.js +697 -0
- package/dist/cli/services/lsp-manager.d.ts +18 -0
- package/dist/cli/services/lsp-manager.js +717 -0
- package/dist/cli/services/mcp-client.d.ts +48 -0
- package/dist/cli/services/mcp-client.js +157 -0
- package/dist/cli/services/memory-manager.d.ts +16 -0
- package/dist/cli/services/memory-manager.js +57 -0
- package/dist/cli/services/model-manager.d.ts +18 -0
- package/dist/cli/services/model-manager.js +71 -0
- package/dist/cli/services/model-router.d.ts +26 -0
- package/dist/cli/services/model-router.js +149 -0
- package/dist/cli/services/permission-modes.d.ts +13 -0
- package/dist/cli/services/permission-modes.js +43 -0
- package/dist/cli/services/rewind.d.ts +84 -0
- package/dist/cli/services/rewind.js +194 -0
- package/dist/cli/services/ripgrep.d.ts +28 -0
- package/dist/cli/services/ripgrep.js +138 -0
- package/dist/cli/services/sandbox.d.ts +29 -0
- package/dist/cli/services/sandbox.js +97 -0
- package/dist/cli/services/server-tools.d.ts +61 -0
- package/dist/cli/services/server-tools.js +543 -0
- package/dist/cli/services/session-persistence.d.ts +23 -0
- package/dist/cli/services/session-persistence.js +99 -0
- package/dist/cli/services/subagent-worker.d.ts +19 -0
- package/dist/cli/services/subagent-worker.js +41 -0
- package/dist/cli/services/subagent.d.ts +47 -0
- package/dist/cli/services/subagent.js +647 -0
- package/dist/cli/services/system-prompt.d.ts +7 -0
- package/dist/cli/services/system-prompt.js +198 -0
- package/dist/cli/services/team-lead.d.ts +73 -0
- package/dist/cli/services/team-lead.js +512 -0
- package/dist/cli/services/team-state.d.ts +77 -0
- package/dist/cli/services/team-state.js +398 -0
- package/dist/cli/services/teammate.d.ts +31 -0
- package/dist/cli/services/teammate.js +689 -0
- package/dist/cli/services/telemetry.d.ts +61 -0
- package/dist/cli/services/telemetry.js +209 -0
- package/dist/cli/services/tools/agent-tools.d.ts +14 -0
- package/dist/cli/services/tools/agent-tools.js +347 -0
- package/dist/cli/services/tools/file-ops.d.ts +15 -0
- package/dist/cli/services/tools/file-ops.js +487 -0
- package/dist/cli/services/tools/search-tools.d.ts +8 -0
- package/dist/cli/services/tools/search-tools.js +186 -0
- package/dist/cli/services/tools/shell-exec.d.ts +10 -0
- package/dist/cli/services/tools/shell-exec.js +168 -0
- package/dist/cli/services/tools/task-manager.d.ts +28 -0
- package/dist/cli/services/tools/task-manager.js +209 -0
- package/dist/cli/services/tools/web-tools.d.ts +11 -0
- package/dist/cli/services/tools/web-tools.js +395 -0
- package/dist/cli/setup/SetupApp.d.ts +9 -0
- package/dist/cli/setup/SetupApp.js +191 -0
- package/dist/cli/shared/MatrixIntro.d.ts +4 -0
- package/dist/cli/shared/MatrixIntro.js +83 -0
- package/dist/cli/shared/Theme.d.ts +74 -0
- package/dist/cli/shared/Theme.js +127 -0
- package/dist/cli/shared/WhaleBanner.d.ts +10 -0
- package/dist/cli/shared/WhaleBanner.js +12 -0
- package/dist/cli/shared/markdown.d.ts +21 -0
- package/dist/cli/shared/markdown.js +756 -0
- package/dist/cli/status/StatusApp.d.ts +4 -0
- package/dist/cli/status/StatusApp.js +105 -0
- package/dist/cli/stores/StoreApp.d.ts +7 -0
- package/dist/cli/stores/StoreApp.js +81 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +538 -0
- package/dist/local-agent/connection.d.ts +48 -0
- package/dist/local-agent/connection.js +332 -0
- package/dist/local-agent/discovery.d.ts +18 -0
- package/dist/local-agent/discovery.js +146 -0
- package/dist/local-agent/executor.d.ts +34 -0
- package/dist/local-agent/executor.js +241 -0
- package/dist/local-agent/index.d.ts +14 -0
- package/dist/local-agent/index.js +198 -0
- package/dist/node/adapters/base.d.ts +35 -0
- package/dist/node/adapters/base.js +10 -0
- package/dist/node/adapters/discord.d.ts +29 -0
- package/dist/node/adapters/discord.js +299 -0
- package/dist/node/adapters/email.d.ts +23 -0
- package/dist/node/adapters/email.js +218 -0
- package/dist/node/adapters/imessage.d.ts +17 -0
- package/dist/node/adapters/imessage.js +118 -0
- package/dist/node/adapters/slack.d.ts +26 -0
- package/dist/node/adapters/slack.js +259 -0
- package/dist/node/adapters/sms.d.ts +23 -0
- package/dist/node/adapters/sms.js +161 -0
- package/dist/node/adapters/telegram.d.ts +17 -0
- package/dist/node/adapters/telegram.js +101 -0
- package/dist/node/adapters/webchat.d.ts +27 -0
- package/dist/node/adapters/webchat.js +160 -0
- package/dist/node/adapters/whatsapp.d.ts +28 -0
- package/dist/node/adapters/whatsapp.js +230 -0
- package/dist/node/cli.d.ts +2 -0
- package/dist/node/cli.js +325 -0
- package/dist/node/config.d.ts +17 -0
- package/dist/node/config.js +31 -0
- package/dist/node/runtime.d.ts +50 -0
- package/dist/node/runtime.js +351 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +11 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.js +393 -0
- package/dist/server/handlers/analytics.d.ts +17 -0
- package/dist/server/handlers/analytics.js +266 -0
- package/dist/server/handlers/api-keys.d.ts +6 -0
- package/dist/server/handlers/api-keys.js +221 -0
- package/dist/server/handlers/billing.d.ts +33 -0
- package/dist/server/handlers/billing.js +272 -0
- package/dist/server/handlers/browser.d.ts +10 -0
- package/dist/server/handlers/browser.js +517 -0
- package/dist/server/handlers/catalog.d.ts +99 -0
- package/dist/server/handlers/catalog.js +976 -0
- package/dist/server/handlers/comms.d.ts +254 -0
- package/dist/server/handlers/comms.js +588 -0
- package/dist/server/handlers/creations.d.ts +6 -0
- package/dist/server/handlers/creations.js +479 -0
- package/dist/server/handlers/crm.d.ts +89 -0
- package/dist/server/handlers/crm.js +538 -0
- package/dist/server/handlers/discovery.d.ts +6 -0
- package/dist/server/handlers/discovery.js +288 -0
- package/dist/server/handlers/embeddings.d.ts +92 -0
- package/dist/server/handlers/embeddings.js +197 -0
- package/dist/server/handlers/enrichment.d.ts +8 -0
- package/dist/server/handlers/enrichment.js +768 -0
- package/dist/server/handlers/image-gen.d.ts +6 -0
- package/dist/server/handlers/image-gen.js +409 -0
- package/dist/server/handlers/inventory.d.ts +319 -0
- package/dist/server/handlers/inventory.js +447 -0
- package/dist/server/handlers/kali.d.ts +10 -0
- package/dist/server/handlers/kali.js +210 -0
- package/dist/server/handlers/llm-providers.d.ts +6 -0
- package/dist/server/handlers/llm-providers.js +673 -0
- package/dist/server/handlers/local-agent.d.ts +6 -0
- package/dist/server/handlers/local-agent.js +118 -0
- package/dist/server/handlers/meta-ads.d.ts +111 -0
- package/dist/server/handlers/meta-ads.js +2279 -0
- package/dist/server/handlers/nodes.d.ts +33 -0
- package/dist/server/handlers/nodes.js +699 -0
- package/dist/server/handlers/operations.d.ts +138 -0
- package/dist/server/handlers/operations.js +131 -0
- package/dist/server/handlers/platform.d.ts +23 -0
- package/dist/server/handlers/platform.js +227 -0
- package/dist/server/handlers/supply-chain.d.ts +19 -0
- package/dist/server/handlers/supply-chain.js +327 -0
- package/dist/server/handlers/transcription.d.ts +17 -0
- package/dist/server/handlers/transcription.js +121 -0
- package/dist/server/handlers/video-gen.d.ts +6 -0
- package/dist/server/handlers/video-gen.js +466 -0
- package/dist/server/handlers/voice.d.ts +8 -0
- package/dist/server/handlers/voice.js +1146 -0
- package/dist/server/handlers/workflow-steps.d.ts +86 -0
- package/dist/server/handlers/workflow-steps.js +2349 -0
- package/dist/server/handlers/workflows.d.ts +7 -0
- package/dist/server/handlers/workflows.js +989 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +2427 -0
- package/dist/server/lib/batch-client.d.ts +80 -0
- package/dist/server/lib/batch-client.js +467 -0
- package/dist/server/lib/code-worker-pool.d.ts +31 -0
- package/dist/server/lib/code-worker-pool.js +224 -0
- package/dist/server/lib/code-worker.d.ts +1 -0
- package/dist/server/lib/code-worker.js +188 -0
- package/dist/server/lib/compaction-service.d.ts +32 -0
- package/dist/server/lib/compaction-service.js +162 -0
- package/dist/server/lib/logger.d.ts +19 -0
- package/dist/server/lib/logger.js +46 -0
- package/dist/server/lib/otel.d.ts +38 -0
- package/dist/server/lib/otel.js +126 -0
- package/dist/server/lib/pg-rate-limiter.d.ts +21 -0
- package/dist/server/lib/pg-rate-limiter.js +86 -0
- package/dist/server/lib/prompt-sanitizer.d.ts +37 -0
- package/dist/server/lib/prompt-sanitizer.js +177 -0
- package/dist/server/lib/provider-capabilities.d.ts +85 -0
- package/dist/server/lib/provider-capabilities.js +190 -0
- package/dist/server/lib/provider-failover.d.ts +74 -0
- package/dist/server/lib/provider-failover.js +210 -0
- package/dist/server/lib/rate-limiter.d.ts +39 -0
- package/dist/server/lib/rate-limiter.js +147 -0
- package/dist/server/lib/server-agent-loop.d.ts +107 -0
- package/dist/server/lib/server-agent-loop.js +667 -0
- package/dist/server/lib/server-subagent.d.ts +78 -0
- package/dist/server/lib/server-subagent.js +203 -0
- package/dist/server/lib/session-checkpoint.d.ts +51 -0
- package/dist/server/lib/session-checkpoint.js +145 -0
- package/dist/server/lib/ssrf-guard.d.ts +13 -0
- package/dist/server/lib/ssrf-guard.js +240 -0
- package/dist/server/lib/supabase-client.d.ts +7 -0
- package/dist/server/lib/supabase-client.js +78 -0
- package/dist/server/lib/template-resolver.d.ts +31 -0
- package/dist/server/lib/template-resolver.js +215 -0
- package/dist/server/lib/utils.d.ts +16 -0
- package/dist/server/lib/utils.js +147 -0
- package/dist/server/local-agent-gateway.d.ts +82 -0
- package/dist/server/local-agent-gateway.js +426 -0
- package/dist/server/providers/anthropic.d.ts +20 -0
- package/dist/server/providers/anthropic.js +199 -0
- package/dist/server/providers/bedrock.d.ts +20 -0
- package/dist/server/providers/bedrock.js +194 -0
- package/dist/server/providers/gemini.d.ts +24 -0
- package/dist/server/providers/gemini.js +486 -0
- package/dist/server/providers/openai.d.ts +24 -0
- package/dist/server/providers/openai.js +522 -0
- package/dist/server/providers/registry.d.ts +32 -0
- package/dist/server/providers/registry.js +58 -0
- package/dist/server/providers/shared.d.ts +32 -0
- package/dist/server/providers/shared.js +124 -0
- package/dist/server/providers/types.d.ts +92 -0
- package/dist/server/providers/types.js +12 -0
- package/dist/server/proxy-handlers.d.ts +6 -0
- package/dist/server/proxy-handlers.js +89 -0
- package/dist/server/tool-router.d.ts +149 -0
- package/dist/server/tool-router.js +803 -0
- package/dist/server/validation.d.ts +24 -0
- package/dist/server/validation.js +301 -0
- package/dist/server/worker.d.ts +19 -0
- package/dist/server/worker.js +201 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +181 -0
- package/dist/shared/agent-core.d.ts +157 -0
- package/dist/shared/agent-core.js +534 -0
- package/dist/shared/anthropic-types.d.ts +105 -0
- package/dist/shared/anthropic-types.js +7 -0
- package/dist/shared/api-client.d.ts +90 -0
- package/dist/shared/api-client.js +379 -0
- package/dist/shared/constants.d.ts +33 -0
- package/dist/shared/constants.js +80 -0
- package/dist/shared/sse-parser.d.ts +26 -0
- package/dist/shared/sse-parser.js +259 -0
- package/dist/shared/tool-dispatch.d.ts +52 -0
- package/dist/shared/tool-dispatch.js +191 -0
- package/dist/shared/types.d.ts +72 -0
- package/dist/shared/types.js +7 -0
- package/dist/updater.d.ts +25 -0
- package/dist/updater.js +140 -0
- package/dist/webchat/widget.d.ts +0 -0
- package/dist/webchat/widget.js +397 -0
- package/package.json +95 -0
- package/src/cli/services/builtin-skills/commit.md +19 -0
- package/src/cli/services/builtin-skills/review-pr.md +21 -0
- package/src/cli/services/builtin-skills/review.md +18 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/otel.ts — OpenTelemetry SDK setup for workflow engine observability
|
|
3
|
+
*
|
|
4
|
+
* Phase 3.3: Adds distributed tracing spans for step execution, tool calls, LLM calls.
|
|
5
|
+
* Exports to any OTLP-compatible collector (Jaeger, Grafana Tempo, Honeycomb, etc.)
|
|
6
|
+
*
|
|
7
|
+
* Configuration:
|
|
8
|
+
* OTEL_EXPORTER_OTLP_ENDPOINT — Collector endpoint (e.g., http://localhost:4318)
|
|
9
|
+
* OTEL_SERVICE_NAME — Service name (default: "whale-workflow-engine")
|
|
10
|
+
* OTEL_ENABLED — Set to "true" to enable (disabled by default)
|
|
11
|
+
*
|
|
12
|
+
* Backward compatible: If OTEL is not enabled, all functions are no-ops.
|
|
13
|
+
*/
|
|
14
|
+
export interface SpanContext {
|
|
15
|
+
end: (attrs?: Record<string, unknown>) => void;
|
|
16
|
+
setError: (err: Error | string) => void;
|
|
17
|
+
setAttribute: (key: string, value: string | number | boolean) => void;
|
|
18
|
+
addEvent: (name: string, attrs?: Record<string, unknown>) => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Initialize OpenTelemetry SDK. Call once at startup.
|
|
22
|
+
* No-op if OTEL_ENABLED !== "true".
|
|
23
|
+
*/
|
|
24
|
+
export declare function initOtel(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Graceful shutdown — flush pending spans.
|
|
27
|
+
*/
|
|
28
|
+
export declare function shutdownOtel(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Start a new span for tracking an operation.
|
|
31
|
+
* Returns a SpanContext that wraps the OTEL span API.
|
|
32
|
+
* If OTEL is disabled, returns a no-op.
|
|
33
|
+
*/
|
|
34
|
+
export declare function startSpan(name: string, attributes?: Record<string, string | number | boolean>): SpanContext;
|
|
35
|
+
/**
|
|
36
|
+
* Convenience: wrap an async function in a span.
|
|
37
|
+
*/
|
|
38
|
+
export declare function withSpan<T>(name: string, attributes: Record<string, string | number | boolean>, fn: (span: SpanContext) => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/otel.ts — OpenTelemetry SDK setup for workflow engine observability
|
|
3
|
+
*
|
|
4
|
+
* Phase 3.3: Adds distributed tracing spans for step execution, tool calls, LLM calls.
|
|
5
|
+
* Exports to any OTLP-compatible collector (Jaeger, Grafana Tempo, Honeycomb, etc.)
|
|
6
|
+
*
|
|
7
|
+
* Configuration:
|
|
8
|
+
* OTEL_EXPORTER_OTLP_ENDPOINT — Collector endpoint (e.g., http://localhost:4318)
|
|
9
|
+
* OTEL_SERVICE_NAME — Service name (default: "whale-workflow-engine")
|
|
10
|
+
* OTEL_ENABLED — Set to "true" to enable (disabled by default)
|
|
11
|
+
*
|
|
12
|
+
* Backward compatible: If OTEL is not enabled, all functions are no-ops.
|
|
13
|
+
*/
|
|
14
|
+
const OTEL_ENABLED = process.env.OTEL_ENABLED === "true";
|
|
15
|
+
const OTEL_ENDPOINT = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "";
|
|
16
|
+
const SERVICE_NAME = process.env.OTEL_SERVICE_NAME || "whale-workflow-engine";
|
|
17
|
+
// Lazy-loaded SDK references
|
|
18
|
+
let _tracer = null;
|
|
19
|
+
let _sdk = null;
|
|
20
|
+
/**
|
|
21
|
+
* Initialize OpenTelemetry SDK. Call once at startup.
|
|
22
|
+
* No-op if OTEL_ENABLED !== "true".
|
|
23
|
+
*/
|
|
24
|
+
export async function initOtel() {
|
|
25
|
+
if (!OTEL_ENABLED || !OTEL_ENDPOINT)
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
// @ts-ignore — optional runtime dependency, gracefully handled by try/catch
|
|
29
|
+
const { NodeSDK } = await import("@opentelemetry/sdk-node");
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
const { OTLPTraceExporter } = await import("@opentelemetry/exporter-trace-otlp-http");
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
const { Resource } = await import("@opentelemetry/resources");
|
|
34
|
+
// @ts-ignore
|
|
35
|
+
const { ATTR_SERVICE_NAME } = await import("@opentelemetry/semantic-conventions");
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
const { trace } = await import("@opentelemetry/api");
|
|
38
|
+
const exporter = new OTLPTraceExporter({ url: `${OTEL_ENDPOINT}/v1/traces` });
|
|
39
|
+
_sdk = new NodeSDK({
|
|
40
|
+
resource: new Resource({ [ATTR_SERVICE_NAME]: SERVICE_NAME }),
|
|
41
|
+
traceExporter: exporter,
|
|
42
|
+
});
|
|
43
|
+
_sdk.start();
|
|
44
|
+
_tracer = trace.getTracer(SERVICE_NAME);
|
|
45
|
+
console.log(`[otel] initialized — exporting to ${OTEL_ENDPOINT}`);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
console.warn("[otel] Failed to initialize (SDK not installed?):", err.message);
|
|
49
|
+
// Non-fatal — all functions remain no-ops
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Graceful shutdown — flush pending spans.
|
|
54
|
+
*/
|
|
55
|
+
export async function shutdownOtel() {
|
|
56
|
+
if (_sdk) {
|
|
57
|
+
try {
|
|
58
|
+
await _sdk.shutdown();
|
|
59
|
+
}
|
|
60
|
+
catch { /* ignore */ }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** No-op span for when OTEL is disabled */
|
|
64
|
+
const noopSpan = {
|
|
65
|
+
end: () => { },
|
|
66
|
+
setError: () => { },
|
|
67
|
+
setAttribute: () => { },
|
|
68
|
+
addEvent: () => { },
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Start a new span for tracking an operation.
|
|
72
|
+
* Returns a SpanContext that wraps the OTEL span API.
|
|
73
|
+
* If OTEL is disabled, returns a no-op.
|
|
74
|
+
*/
|
|
75
|
+
export function startSpan(name, attributes) {
|
|
76
|
+
if (!_tracer)
|
|
77
|
+
return noopSpan;
|
|
78
|
+
try {
|
|
79
|
+
const span = _tracer.startSpan(name, {
|
|
80
|
+
attributes: attributes || {},
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
end: (attrs) => {
|
|
84
|
+
if (attrs) {
|
|
85
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
86
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
87
|
+
span.setAttribute(k, v);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
span.end();
|
|
92
|
+
},
|
|
93
|
+
setError: (err) => {
|
|
94
|
+
span.setStatus({ code: 2 /* ERROR */, message: typeof err === "string" ? err : err.message });
|
|
95
|
+
if (err instanceof Error) {
|
|
96
|
+
span.recordException(err);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
setAttribute: (key, value) => {
|
|
100
|
+
span.setAttribute(key, value);
|
|
101
|
+
},
|
|
102
|
+
addEvent: (eventName, attrs) => {
|
|
103
|
+
span.addEvent(eventName, attrs);
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return noopSpan;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Convenience: wrap an async function in a span.
|
|
113
|
+
*/
|
|
114
|
+
export async function withSpan(name, attributes, fn) {
|
|
115
|
+
const span = startSpan(name, attributes);
|
|
116
|
+
try {
|
|
117
|
+
const result = await fn(span);
|
|
118
|
+
span.end();
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
span.setError(err);
|
|
123
|
+
span.end();
|
|
124
|
+
throw err;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/pg-rate-limiter.ts — PostgreSQL-backed rate limiter
|
|
3
|
+
*
|
|
4
|
+
* Phase 3.4: Token bucket rate limiting backed by Postgres for cross-process consistency.
|
|
5
|
+
* Survives deploys and works across multiple Fly.io instances.
|
|
6
|
+
*
|
|
7
|
+
* Uses the in-memory limiter as a fast first-pass, then falls back to DB
|
|
8
|
+
* as the cross-process source of truth.
|
|
9
|
+
*/
|
|
10
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
11
|
+
export interface RateLimitResult {
|
|
12
|
+
allowed: boolean;
|
|
13
|
+
remaining: number;
|
|
14
|
+
retryAfterSeconds: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Check rate limit using in-memory cache first, then DB for authoritative check.
|
|
18
|
+
* The in-memory check prevents unnecessary DB round-trips for clearly-allowed requests.
|
|
19
|
+
*/
|
|
20
|
+
export declare function checkRateLimit(supabase: SupabaseClient, key: string, maxTokens?: number, refillRate?: number, // tokens per second
|
|
21
|
+
cost?: number): Promise<RateLimitResult>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/pg-rate-limiter.ts — PostgreSQL-backed rate limiter
|
|
3
|
+
*
|
|
4
|
+
* Phase 3.4: Token bucket rate limiting backed by Postgres for cross-process consistency.
|
|
5
|
+
* Survives deploys and works across multiple Fly.io instances.
|
|
6
|
+
*
|
|
7
|
+
* Uses the in-memory limiter as a fast first-pass, then falls back to DB
|
|
8
|
+
* as the cross-process source of truth.
|
|
9
|
+
*/
|
|
10
|
+
// Fast in-memory first-pass cache
|
|
11
|
+
const memoryBuckets = new Map();
|
|
12
|
+
/**
|
|
13
|
+
* Check rate limit using in-memory cache first, then DB for authoritative check.
|
|
14
|
+
* The in-memory check prevents unnecessary DB round-trips for clearly-allowed requests.
|
|
15
|
+
*/
|
|
16
|
+
export async function checkRateLimit(supabase, key, maxTokens = 100, refillRate = 1, // tokens per second
|
|
17
|
+
cost = 1) {
|
|
18
|
+
// Fast path: in-memory check
|
|
19
|
+
const memResult = checkMemoryLimit(key, maxTokens, refillRate, cost);
|
|
20
|
+
if (!memResult.allowed) {
|
|
21
|
+
return memResult; // Definitely rate limited
|
|
22
|
+
}
|
|
23
|
+
// Authoritative check via DB RPC
|
|
24
|
+
try {
|
|
25
|
+
const { data, error } = await supabase.rpc("check_rate_limit", {
|
|
26
|
+
p_key: key,
|
|
27
|
+
p_max_tokens: maxTokens,
|
|
28
|
+
p_refill_rate: refillRate,
|
|
29
|
+
p_cost: cost,
|
|
30
|
+
});
|
|
31
|
+
if (error) {
|
|
32
|
+
// DB unavailable — fall back to memory-only
|
|
33
|
+
return memResult;
|
|
34
|
+
}
|
|
35
|
+
const row = Array.isArray(data) ? data[0] : data;
|
|
36
|
+
if (!row)
|
|
37
|
+
return memResult;
|
|
38
|
+
// Sync memory cache with DB state
|
|
39
|
+
const bucket = memoryBuckets.get(key);
|
|
40
|
+
if (bucket) {
|
|
41
|
+
bucket.tokens = Number(row.remaining);
|
|
42
|
+
bucket.lastRefill = Date.now();
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
allowed: row.allowed,
|
|
46
|
+
remaining: Number(row.remaining),
|
|
47
|
+
retryAfterSeconds: Number(row.retry_after_seconds || 0),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// DB call failed — use memory result
|
|
52
|
+
return memResult;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* In-memory only rate limit check (no DB call).
|
|
57
|
+
* Use this for high-frequency checks where DB latency is unacceptable.
|
|
58
|
+
*/
|
|
59
|
+
function checkMemoryLimit(key, maxTokens, refillRate, cost) {
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
let bucket = memoryBuckets.get(key);
|
|
62
|
+
if (!bucket) {
|
|
63
|
+
bucket = { tokens: maxTokens, lastRefill: now, maxTokens, refillRate };
|
|
64
|
+
memoryBuckets.set(key, bucket);
|
|
65
|
+
}
|
|
66
|
+
// Refill tokens
|
|
67
|
+
const elapsed = (now - bucket.lastRefill) / 1000;
|
|
68
|
+
bucket.tokens = Math.min(bucket.maxTokens, bucket.tokens + elapsed * bucket.refillRate);
|
|
69
|
+
bucket.lastRefill = now;
|
|
70
|
+
if (bucket.tokens >= cost) {
|
|
71
|
+
bucket.tokens -= cost;
|
|
72
|
+
return { allowed: true, remaining: bucket.tokens, retryAfterSeconds: 0 };
|
|
73
|
+
}
|
|
74
|
+
const retryAfter = Math.ceil((cost - bucket.tokens) / bucket.refillRate);
|
|
75
|
+
return { allowed: false, remaining: bucket.tokens, retryAfterSeconds: retryAfter };
|
|
76
|
+
}
|
|
77
|
+
// Cleanup stale memory buckets every 5 minutes
|
|
78
|
+
setInterval(() => {
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
const staleMs = 300_000; // 5 minutes
|
|
81
|
+
for (const [key, bucket] of memoryBuckets) {
|
|
82
|
+
if (now - bucket.lastRefill > staleMs) {
|
|
83
|
+
memoryBuckets.delete(key);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}, 300_000);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Sanitizer — prevents injection attacks in user-provided system prompts
|
|
3
|
+
* and tool descriptions.
|
|
4
|
+
*
|
|
5
|
+
* Detects and strips:
|
|
6
|
+
* - Prompt injection markers (</system>, <|im_start|>, [INST], <<SYS>>, etc.)
|
|
7
|
+
* - Role confusion attempts (Human:/Assistant: turn markers)
|
|
8
|
+
* - Unicode control characters (zero-width spaces, RTL overrides, etc.)
|
|
9
|
+
* - Excessive length (>50,000 chars)
|
|
10
|
+
* - Repeated special characters (>100 in a row)
|
|
11
|
+
*
|
|
12
|
+
* No external dependencies — regex only.
|
|
13
|
+
*/
|
|
14
|
+
export interface SanitizeResult {
|
|
15
|
+
sanitized: string;
|
|
16
|
+
warnings: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sanitize a user-provided system prompt to prevent injection attacks.
|
|
20
|
+
* Returns the cleaned prompt and an array of warnings for anything modified.
|
|
21
|
+
*/
|
|
22
|
+
export declare function sanitizeSystemPrompt(prompt: string): SanitizeResult;
|
|
23
|
+
/**
|
|
24
|
+
* Validate that system prompt blocks follow Anthropic's expected format.
|
|
25
|
+
* Each block must be `{ type: "text", text: string }` with optional `cache_control`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateSystemPromptStructure(blocks: unknown[]): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Sanitize a tool description field to prevent injection via tool definitions.
|
|
30
|
+
* Same injection pattern detection as system prompts, with a 4096-char cap.
|
|
31
|
+
*/
|
|
32
|
+
export declare function sanitizeToolDescription(desc: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Sanitize a system prompt and log any warnings. Use this at integration points
|
|
35
|
+
* where you want automatic structured logging of modifications.
|
|
36
|
+
*/
|
|
37
|
+
export declare function sanitizeAndLog(prompt: string, source: string, context?: Record<string, unknown>): string;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Sanitizer — prevents injection attacks in user-provided system prompts
|
|
3
|
+
* and tool descriptions.
|
|
4
|
+
*
|
|
5
|
+
* Detects and strips:
|
|
6
|
+
* - Prompt injection markers (</system>, <|im_start|>, [INST], <<SYS>>, etc.)
|
|
7
|
+
* - Role confusion attempts (Human:/Assistant: turn markers)
|
|
8
|
+
* - Unicode control characters (zero-width spaces, RTL overrides, etc.)
|
|
9
|
+
* - Excessive length (>50,000 chars)
|
|
10
|
+
* - Repeated special characters (>100 in a row)
|
|
11
|
+
*
|
|
12
|
+
* No external dependencies — regex only.
|
|
13
|
+
*/
|
|
14
|
+
import { createLogger } from "./logger.js";
|
|
15
|
+
const log = createLogger("prompt-sanitizer");
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// CONSTANTS
|
|
18
|
+
// ============================================================================
|
|
19
|
+
const MAX_SYSTEM_PROMPT_LENGTH = 50_000;
|
|
20
|
+
const MAX_TOOL_DESCRIPTION_LENGTH = 4_096;
|
|
21
|
+
const MAX_REPEATED_CHARS = 100;
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// INJECTION PATTERNS
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/** Patterns that attempt to break out of system prompt context or impersonate roles. */
|
|
26
|
+
const INJECTION_PATTERNS = [
|
|
27
|
+
// XML/markup-style prompt boundaries
|
|
28
|
+
{ pattern: /<\/system>/gi, label: "</system> tag" },
|
|
29
|
+
{ pattern: /<system>/gi, label: "<system> tag" },
|
|
30
|
+
{ pattern: /<\/assistant>/gi, label: "</assistant> tag" },
|
|
31
|
+
{ pattern: /<\/user>/gi, label: "</user> tag" },
|
|
32
|
+
{ pattern: /<\/human>/gi, label: "</human> tag" },
|
|
33
|
+
// OpenAI ChatML markers
|
|
34
|
+
{ pattern: /<\|im_start\|>/gi, label: "<|im_start|> marker" },
|
|
35
|
+
{ pattern: /<\|im_end\|>/gi, label: "<|im_end|> marker" },
|
|
36
|
+
{ pattern: /<\|im_sep\|>/gi, label: "<|im_sep|> marker" },
|
|
37
|
+
{ pattern: /<\|endoftext\|>/gi, label: "<|endoftext|> marker" },
|
|
38
|
+
// Llama/Mistral instruction markers
|
|
39
|
+
{ pattern: /\[INST\]/gi, label: "[INST] marker" },
|
|
40
|
+
{ pattern: /\[\/INST\]/gi, label: "[/INST] marker" },
|
|
41
|
+
{ pattern: /<<SYS>>/gi, label: "<<SYS>> marker" },
|
|
42
|
+
{ pattern: /<<\/SYS>>/gi, label: "<</SYS>> marker" },
|
|
43
|
+
// Anthropic turn markers (only match when they look like turn boundaries)
|
|
44
|
+
{ pattern: /\n\nHuman:/g, label: "Human: turn marker" },
|
|
45
|
+
{ pattern: /\n\nAssistant:/g, label: "Assistant: turn marker" },
|
|
46
|
+
{ pattern: /^Human:/gm, label: "Human: at line start" },
|
|
47
|
+
{ pattern: /^Assistant:/gm, label: "Assistant: at line start" },
|
|
48
|
+
];
|
|
49
|
+
/**
|
|
50
|
+
* Unicode control character ranges to strip.
|
|
51
|
+
* Includes zero-width spaces, joiners, RTL/LTR overrides, and other invisible chars.
|
|
52
|
+
* Preserves normal whitespace (tab, newline, carriage return, space).
|
|
53
|
+
*/
|
|
54
|
+
const UNICODE_CONTROL_REGEX = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F\u200B-\u200F\u2028-\u202E\u2060-\u2069\uFEFF\uFFF9-\uFFFB]/g;
|
|
55
|
+
/** Repeated special character pattern (>100 of the same non-alphanumeric char) */
|
|
56
|
+
const REPEATED_SPECIAL_REGEX = /([^\w\s])\1{100,}/g;
|
|
57
|
+
/**
|
|
58
|
+
* Sanitize a user-provided system prompt to prevent injection attacks.
|
|
59
|
+
* Returns the cleaned prompt and an array of warnings for anything modified.
|
|
60
|
+
*/
|
|
61
|
+
export function sanitizeSystemPrompt(prompt) {
|
|
62
|
+
const warnings = [];
|
|
63
|
+
if (!prompt || typeof prompt !== "string") {
|
|
64
|
+
return { sanitized: "", warnings: ["Empty or non-string prompt provided"] };
|
|
65
|
+
}
|
|
66
|
+
let sanitized = prompt;
|
|
67
|
+
// 1. Strip Unicode control characters
|
|
68
|
+
const controlMatches = sanitized.match(UNICODE_CONTROL_REGEX);
|
|
69
|
+
if (controlMatches) {
|
|
70
|
+
warnings.push(`Stripped ${controlMatches.length} Unicode control character(s)`);
|
|
71
|
+
sanitized = sanitized.replace(UNICODE_CONTROL_REGEX, "");
|
|
72
|
+
}
|
|
73
|
+
// 2. Detect and strip injection patterns
|
|
74
|
+
for (const { pattern, label } of INJECTION_PATTERNS) {
|
|
75
|
+
// Reset lastIndex for global regexes
|
|
76
|
+
pattern.lastIndex = 0;
|
|
77
|
+
const matches = sanitized.match(pattern);
|
|
78
|
+
if (matches) {
|
|
79
|
+
warnings.push(`Stripped ${matches.length} instance(s) of ${label}`);
|
|
80
|
+
pattern.lastIndex = 0;
|
|
81
|
+
sanitized = sanitized.replace(pattern, "");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// 3. Truncate repeated special characters
|
|
85
|
+
const repeatedMatches = sanitized.match(REPEATED_SPECIAL_REGEX);
|
|
86
|
+
if (repeatedMatches) {
|
|
87
|
+
warnings.push(`Truncated ${repeatedMatches.length} sequence(s) of >100 repeated special characters`);
|
|
88
|
+
sanitized = sanitized.replace(REPEATED_SPECIAL_REGEX, (match, char) => {
|
|
89
|
+
return char.repeat(MAX_REPEATED_CHARS);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// 4. Enforce length limit
|
|
93
|
+
if (sanitized.length > MAX_SYSTEM_PROMPT_LENGTH) {
|
|
94
|
+
warnings.push(`Truncated system prompt from ${sanitized.length} to ${MAX_SYSTEM_PROMPT_LENGTH} characters`);
|
|
95
|
+
sanitized = sanitized.substring(0, MAX_SYSTEM_PROMPT_LENGTH);
|
|
96
|
+
}
|
|
97
|
+
return { sanitized, warnings };
|
|
98
|
+
}
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// VALIDATE SYSTEM PROMPT STRUCTURE
|
|
101
|
+
// ============================================================================
|
|
102
|
+
/**
|
|
103
|
+
* Validate that system prompt blocks follow Anthropic's expected format.
|
|
104
|
+
* Each block must be `{ type: "text", text: string }` with optional `cache_control`.
|
|
105
|
+
*/
|
|
106
|
+
export function validateSystemPromptStructure(blocks) {
|
|
107
|
+
if (!Array.isArray(blocks))
|
|
108
|
+
return false;
|
|
109
|
+
for (const block of blocks) {
|
|
110
|
+
if (!block || typeof block !== "object")
|
|
111
|
+
return false;
|
|
112
|
+
const b = block;
|
|
113
|
+
// Must have type: "text"
|
|
114
|
+
if (b.type !== "text")
|
|
115
|
+
return false;
|
|
116
|
+
// Must have text as string
|
|
117
|
+
if (typeof b.text !== "string")
|
|
118
|
+
return false;
|
|
119
|
+
// Only allowed fields: type, text, cache_control
|
|
120
|
+
const allowedFields = new Set(["type", "text", "cache_control"]);
|
|
121
|
+
for (const key of Object.keys(b)) {
|
|
122
|
+
if (!allowedFields.has(key))
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
// If cache_control present, validate its shape
|
|
126
|
+
if (b.cache_control !== undefined) {
|
|
127
|
+
if (!b.cache_control || typeof b.cache_control !== "object")
|
|
128
|
+
return false;
|
|
129
|
+
const cc = b.cache_control;
|
|
130
|
+
if (cc.type !== "ephemeral")
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// SANITIZE TOOL DESCRIPTION
|
|
138
|
+
// ============================================================================
|
|
139
|
+
/**
|
|
140
|
+
* Sanitize a tool description field to prevent injection via tool definitions.
|
|
141
|
+
* Same injection pattern detection as system prompts, with a 4096-char cap.
|
|
142
|
+
*/
|
|
143
|
+
export function sanitizeToolDescription(desc) {
|
|
144
|
+
if (!desc || typeof desc !== "string")
|
|
145
|
+
return "";
|
|
146
|
+
let sanitized = desc;
|
|
147
|
+
// Strip Unicode control characters
|
|
148
|
+
sanitized = sanitized.replace(UNICODE_CONTROL_REGEX, "");
|
|
149
|
+
// Strip injection patterns
|
|
150
|
+
for (const { pattern } of INJECTION_PATTERNS) {
|
|
151
|
+
pattern.lastIndex = 0;
|
|
152
|
+
sanitized = sanitized.replace(pattern, "");
|
|
153
|
+
}
|
|
154
|
+
// Truncate repeated special characters
|
|
155
|
+
sanitized = sanitized.replace(REPEATED_SPECIAL_REGEX, (match, char) => {
|
|
156
|
+
return char.repeat(MAX_REPEATED_CHARS);
|
|
157
|
+
});
|
|
158
|
+
// Enforce length limit
|
|
159
|
+
if (sanitized.length > MAX_TOOL_DESCRIPTION_LENGTH) {
|
|
160
|
+
sanitized = sanitized.substring(0, MAX_TOOL_DESCRIPTION_LENGTH);
|
|
161
|
+
}
|
|
162
|
+
return sanitized;
|
|
163
|
+
}
|
|
164
|
+
// ============================================================================
|
|
165
|
+
// CONVENIENCE: SANITIZE AND LOG
|
|
166
|
+
// ============================================================================
|
|
167
|
+
/**
|
|
168
|
+
* Sanitize a system prompt and log any warnings. Use this at integration points
|
|
169
|
+
* where you want automatic structured logging of modifications.
|
|
170
|
+
*/
|
|
171
|
+
export function sanitizeAndLog(prompt, source, context) {
|
|
172
|
+
const { sanitized, warnings } = sanitizeSystemPrompt(prompt);
|
|
173
|
+
if (warnings.length > 0) {
|
|
174
|
+
log.warn({ source, warnings, originalLength: prompt.length, sanitizedLength: sanitized.length, ...context }, `System prompt sanitized: ${warnings.length} modification(s)`);
|
|
175
|
+
}
|
|
176
|
+
return sanitized;
|
|
177
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Capability Matrix — static feature map for intelligent request routing.
|
|
3
|
+
*
|
|
4
|
+
* Each provider declares which features it supports. This enables:
|
|
5
|
+
* - Pre-flight checks before sending a request to a provider
|
|
6
|
+
* - Capability-aware failover (don't fail over to a provider that can't handle the request)
|
|
7
|
+
* - Intelligent routing (pick the best provider for a given request shape)
|
|
8
|
+
*/
|
|
9
|
+
import type { ModelProvider } from "../../shared/constants.js";
|
|
10
|
+
export interface ProviderCapabilities {
|
|
11
|
+
/** Can process images (vision) */
|
|
12
|
+
vision: boolean;
|
|
13
|
+
/** Can process PDF documents natively */
|
|
14
|
+
pdf: boolean;
|
|
15
|
+
/** Supports computer_use tool */
|
|
16
|
+
computerUse: boolean;
|
|
17
|
+
/** Can execute code natively (e.g. code interpreter) */
|
|
18
|
+
codeExecution: boolean;
|
|
19
|
+
/** Supports SSE streaming */
|
|
20
|
+
streaming: boolean;
|
|
21
|
+
/** Supports batch processing API */
|
|
22
|
+
batchApi: boolean;
|
|
23
|
+
/** Supports source citations */
|
|
24
|
+
citations: boolean;
|
|
25
|
+
/** Supports extended thinking / reasoning */
|
|
26
|
+
thinking: boolean;
|
|
27
|
+
/** Supports function calling / tool use */
|
|
28
|
+
toolUse: boolean;
|
|
29
|
+
/** Supports prompt caching */
|
|
30
|
+
caching: boolean;
|
|
31
|
+
/** Supports server-side context management (edits, compaction) */
|
|
32
|
+
contextManagement: boolean;
|
|
33
|
+
/** Maximum context window in tokens */
|
|
34
|
+
maxContextTokens: number;
|
|
35
|
+
/** Maximum output tokens */
|
|
36
|
+
maxOutputTokens: number;
|
|
37
|
+
}
|
|
38
|
+
/** Features that can be required for a request (boolean capabilities only) */
|
|
39
|
+
export type CapabilityFeature = {
|
|
40
|
+
[K in keyof ProviderCapabilities]: ProviderCapabilities[K] extends boolean ? K : never;
|
|
41
|
+
}[keyof ProviderCapabilities];
|
|
42
|
+
/** Request shape used to determine the best provider */
|
|
43
|
+
export interface RequestCapabilityRequirements {
|
|
44
|
+
hasImages?: boolean;
|
|
45
|
+
hasPdf?: boolean;
|
|
46
|
+
needsBatch?: boolean;
|
|
47
|
+
needsCitations?: boolean;
|
|
48
|
+
needsComputerUse?: boolean;
|
|
49
|
+
needsCodeExecution?: boolean;
|
|
50
|
+
needsThinking?: boolean;
|
|
51
|
+
needsContextManagement?: boolean;
|
|
52
|
+
minContextTokens?: number;
|
|
53
|
+
minOutputTokens?: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the full capability set for a provider.
|
|
57
|
+
* Returns a defensive copy so callers can't mutate the static map.
|
|
58
|
+
*/
|
|
59
|
+
export declare function getCapabilities(provider: ModelProvider): ProviderCapabilities;
|
|
60
|
+
/**
|
|
61
|
+
* Check if a provider supports a specific boolean feature.
|
|
62
|
+
*/
|
|
63
|
+
export declare function supportsFeature(provider: ModelProvider, feature: CapabilityFeature): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Find all providers that support a given boolean feature.
|
|
66
|
+
* Returns provider names sorted with anthropic first (default preference).
|
|
67
|
+
*/
|
|
68
|
+
export declare function findProvidersWithCapability(feature: CapabilityFeature): ModelProvider[];
|
|
69
|
+
/**
|
|
70
|
+
* Determine the best provider for a request based on its capability requirements.
|
|
71
|
+
*
|
|
72
|
+
* Strategy:
|
|
73
|
+
* 1. Filter to providers that satisfy ALL required capabilities
|
|
74
|
+
* 2. Prefer Anthropic as default
|
|
75
|
+
* 3. Among remaining candidates, prefer the one with the largest context window
|
|
76
|
+
*
|
|
77
|
+
* Returns "anthropic" as fallback if no provider matches all requirements
|
|
78
|
+
* (better to attempt than to hard-fail — the provider will return a clear error).
|
|
79
|
+
*/
|
|
80
|
+
export declare function getBestProviderForRequest(request: RequestCapabilityRequirements, excludeProviders?: Set<string>): ModelProvider;
|
|
81
|
+
/**
|
|
82
|
+
* Check if a provider can handle a request with the given requirements.
|
|
83
|
+
* Returns true if all requirements are met.
|
|
84
|
+
*/
|
|
85
|
+
export declare function canProviderHandleRequest(provider: ModelProvider, requirements: RequestCapabilityRequirements): boolean;
|