@lastshotlabs/slingshot-ai
npm install @lastshotlabs/slingshot-ai
Functions
Section titled “Functions”AI_PACKAGE_NAME
Section titled “AI_PACKAGE_NAME”Source: packages/slingshot-ai/src/plugin.ts
aiPackageConfigSchema
Section titled “aiPackageConfigSchema”Source: packages/slingshot-ai/src/config.ts
aiProviderConfigSchema
Section titled “aiProviderConfigSchema”Config Fields
Section titled “Config Fields”| Field | Description |
|---|---|
| `z.unknown()).optional(), | |
| timeoutMs` | Optional durable request-scoped reservation controller; invoked for every provider attempt.’, ), requireScope: z .boolean() .default(false) .describe(‘Reject generation without spendScope when a spend controller is configured. |
Source: packages/slingshot-ai/src/config.ts
assertCapabilitiesConsistent
Section titled “assertCapabilitiesConsistent”Capability descriptors: defaults, merging, and consistency checks.
function assertCapabilitiesConsistent(caps: ProviderCapabilities, who: string): voidSource: packages/slingshot-ai/src/provider/capabilities.ts
buildProvider
Section titled “buildProvider”Provider registry.
Shape copied deliberately from slingshot-oauth/src/connections.ts: a
built-in registry keyed by kind (which defaults to the config key), plus a
createProvider escape hatch, plus an “unknown kind” error that lists what
IS available. Same problem, same solved shape.
async function buildProvider(name: string, config: AiProviderConfig, deps: BuildProviderDeps,): Promise<AiProvider>Source: packages/slingshot-ai/src/provider/registry.ts
builtinProviderKinds
Section titled “builtinProviderKinds”Provider registry.
Shape copied deliberately from slingshot-oauth/src/connections.ts: a
built-in registry keyed by kind (which defaults to the config key), plus a
createProvider escape hatch, plus an “unknown kind” error that lists what
IS available. Same problem, same solved shape.
function builtinProviderKinds(): readonly string[]Source: packages/slingshot-ai/src/provider/registry.ts
chooseStructuredMode
Section titled “chooseStructuredMode”Structured output: zod → JSON Schema → a validated object, on ANY provider.
This is the riskiest code in the package, and the difference between “works on Ollama” and “doesn’t”. Three paths, chosen from the provider’s declared capabilities:
native → the provider enforces the schema. We still re-validate. json-mode → syntactically valid JSON, unenforced shape. We validate. prompt → no guarantee at all. We instruct, extract, validate, and repair.
Note what is common to all three: we always validate. The provider’s own
parsed object is advisory. That is why Anthropic returning parsed_output: null and a 7B model emitting a fenced code block with a trailing comma
converge on exactly one code path.
function chooseStructuredMode(capabilities: ProviderCapabilities): StructuredModeSource: packages/slingshot-ai/src/lib/structured.ts
createAiPackage
Section titled “createAiPackage”createAiPackage() — the package factory.
function createAiPackage(rawConfig: AiPackageConfigInput): SlingshotPackageDefinitionSource: packages/slingshot-ai/src/plugin.ts
createAnthropicProvider
Section titled “createAnthropicProvider”async function createAnthropicProvider(name: string, config: AiProviderConfig, deps: BuildProviderDeps,): Promise<AiProvider>Source: packages/slingshot-ai/src/provider/anthropic.ts
createGeminiProvider
Section titled “createGeminiProvider”function createGeminiProvider(name: string, config: AiProviderConfig, deps: BuildProviderDeps,): AiProviderSource: packages/slingshot-ai/src/provider/gemini.ts
createOpenAiCompatibleProvider
Section titled “createOpenAiCompatibleProvider”The OpenAI-compatible adapter — plain fetch, ZERO dependencies.
This is the highest-leverage adapter in the package, because the
/chat/completions shape is the de-facto lingua franca: Ollama, LM Studio,
llama.cpp, vLLM, OpenRouter, Groq, Together, and Gemini’s compat endpoint all
speak it. One adapter therefore turns “free local inference on the home
server” into a config change.
Which is also the design problem. Those backends have wildly different real abilities: vLLM with a grammar backend genuinely enforces a JSON Schema; a small Ollama model cannot reliably close a brace. So capabilities here are config-declarable, and the built-in defaults are deliberately pessimistic — an under-declared provider costs you a JSON repair loop, while an over-declared one costs you a card that never validates and a party that stops. Only the wrong one of those is silent.
function createOpenAiCompatibleProvider(name: string, config: AiProviderConfig, deps: BuildProviderDeps,): AiProviderSource: packages/slingshot-ai/src/provider/openaiCompatible.ts
createOpenAiProvider
Section titled “createOpenAiProvider”The OpenAI-compatible adapter — plain fetch, ZERO dependencies.
This is the highest-leverage adapter in the package, because the
/chat/completions shape is the de-facto lingua franca: Ollama, LM Studio,
llama.cpp, vLLM, OpenRouter, Groq, Together, and Gemini’s compat endpoint all
speak it. One adapter therefore turns “free local inference on the home
server” into a config change.
Which is also the design problem. Those backends have wildly different real abilities: vLLM with a grammar backend genuinely enforces a JSON Schema; a small Ollama model cannot reliably close a brace. So capabilities here are config-declarable, and the built-in defaults are deliberately pessimistic — an under-declared provider costs you a JSON repair loop, while an over-declared one costs you a card that never validates and a party that stops. Only the wrong one of those is silent.
function createOpenAiProvider(name: string, config: AiProviderConfig, deps: BuildProviderDeps,): AiProviderSource: packages/slingshot-ai/src/provider/openaiCompatible.ts
extractJson
Section titled “extractJson”Structured output: zod → JSON Schema → a validated object, on ANY provider.
This is the riskiest code in the package, and the difference between “works on Ollama” and “doesn’t”. Three paths, chosen from the provider’s declared capabilities:
native → the provider enforces the schema. We still re-validate. json-mode → syntactically valid JSON, unenforced shape. We validate. prompt → no guarantee at all. We instruct, extract, validate, and repair.
Note what is common to all three: we always validate. The provider’s own
parsed object is advisory. That is why Anthropic returning parsed_output: null and a 7B model emitting a fenced code block with a trailing comma
converge on exactly one code path.
function extractJson(text: string): string | nullSource: packages/slingshot-ai/src/lib/structured.ts
parseStructured
Section titled “parseStructured”Structured output: zod → JSON Schema → a validated object, on ANY provider.
This is the riskiest code in the package, and the difference between “works on Ollama” and “doesn’t”. Three paths, chosen from the provider’s declared capabilities:
native → the provider enforces the schema. We still re-validate. json-mode → syntactically valid JSON, unenforced shape. We validate. prompt → no guarantee at all. We instruct, extract, validate, and repair.
Note what is common to all three: we always validate. The provider’s own
parsed object is advisory. That is why Anthropic returning parsed_output: null and a 7B model emitting a fenced code block with a trailing comma
converge on exactly one code path.
function parseStructured<T>(options: { schema: z.ZodType<T>; advisory?: unknown; text: string; }): ParseAttempt<T>Source: packages/slingshot-ai/src/lib/structured.ts
registerBuiltinProvider
Section titled “registerBuiltinProvider”Provider registry.
Shape copied deliberately from slingshot-oauth/src/connections.ts: a
built-in registry keyed by kind (which defaults to the config key), plus a
createProvider escape hatch, plus an “unknown kind” error that lists what
IS available. Same problem, same solved shape.
function registerBuiltinProvider(kind: string, factory: ProviderFactory): voidSource: packages/slingshot-ai/src/provider/registry.ts
resolveCapabilities
Section titled “resolveCapabilities”Capability descriptors: defaults, merging, and consistency checks.
function resolveCapabilities(base: ProviderCapabilities, overrides?: Partial<ProviderCapabilities>,): ProviderCapabilitiesSource: packages/slingshot-ai/src/provider/capabilities.ts
sanitizeJsonSchema
Section titled “sanitizeJsonSchema”Structured output: zod → JSON Schema → a validated object, on ANY provider.
This is the riskiest code in the package, and the difference between “works on Ollama” and “doesn’t”. Three paths, chosen from the provider’s declared capabilities:
native → the provider enforces the schema. We still re-validate. json-mode → syntactically valid JSON, unenforced shape. We validate. prompt → no guarantee at all. We instruct, extract, validate, and repair.
Note what is common to all three: we always validate. The provider’s own
parsed object is advisory. That is why Anthropic returning parsed_output: null and a 7B model emitting a fenced code block with a trailing comma
converge on exactly one code path.
function sanitizeJsonSchema(input: unknown): SanitizeResultSource: packages/slingshot-ai/src/lib/structured.ts
toJsonSchema
Section titled “toJsonSchema”Structured output: zod → JSON Schema → a validated object, on ANY provider.
This is the riskiest code in the package, and the difference between “works on Ollama” and “doesn’t”. Three paths, chosen from the provider’s declared capabilities:
native → the provider enforces the schema. We still re-validate. json-mode → syntactically valid JSON, unenforced shape. We validate. prompt → no guarantee at all. We instruct, extract, validate, and repair.
Note what is common to all three: we always validate. The provider’s own
parsed object is advisory. That is why Anthropic returning parsed_output: null and a 7B model emitting a fenced code block with a trailing comma
converge on exactly one code path.
function toJsonSchema(schema: z.ZodType<unknown>, options: { logger?: AiLogger; strict: boolean; name: string },): Record<string, unknown>Source: packages/slingshot-ai/src/lib/structured.ts
Constants
Section titled “Constants”Public contract for slingshot-ai.
Three capabilities, not one — and not for symmetry:
AiClientCapis generation. Nine methods, all about producing tokens.AiModerationCapis safety verdicts. Independently useful: moderating player-typed content involves no generation, and a package that only needs safety shouldn’t depend on a surface that can spend money. It is also the swap point for a non-LLM classifier.AiUsageCapis a read-only projection over usage + spend. Different lifecycle, different consumer (admin/observability, never generation code).
The split makes capabilities.requires honest: a consumer that declares
[AiClientCap, AiModerationCap] gets a boot-time error if the package isn’t
installed, and says exactly what it uses.
Source: packages/slingshot-ai/src/public.ts
AiClientCap
Section titled “AiClientCap”Public contract for slingshot-ai.
Three capabilities, not one — and not for symmetry:
AiClientCapis generation. Nine methods, all about producing tokens.AiModerationCapis safety verdicts. Independently useful: moderating player-typed content involves no generation, and a package that only needs safety shouldn’t depend on a surface that can spend money. It is also the swap point for a non-LLM classifier.AiUsageCapis a read-only projection over usage + spend. Different lifecycle, different consumer (admin/observability, never generation code).
The split makes capabilities.requires honest: a consumer that declares
[AiClientCap, AiModerationCap] gets a boot-time error if the package isn’t
installed, and says exactly what it uses.
Source: packages/slingshot-ai/src/public.ts
AiModerationCap
Section titled “AiModerationCap”Public contract for slingshot-ai.
Three capabilities, not one — and not for symmetry:
AiClientCapis generation. Nine methods, all about producing tokens.AiModerationCapis safety verdicts. Independently useful: moderating player-typed content involves no generation, and a package that only needs safety shouldn’t depend on a surface that can spend money. It is also the swap point for a non-LLM classifier.AiUsageCapis a read-only projection over usage + spend. Different lifecycle, different consumer (admin/observability, never generation code).
The split makes capabilities.requires honest: a consumer that declares
[AiClientCap, AiModerationCap] gets a boot-time error if the package isn’t
installed, and says exactly what it uses.
Source: packages/slingshot-ai/src/public.ts
AiUsageCap
Section titled “AiUsageCap”Public contract for slingshot-ai.
Three capabilities, not one — and not for symmetry:
AiClientCapis generation. Nine methods, all about producing tokens.AiModerationCapis safety verdicts. Independently useful: moderating player-typed content involves no generation, and a package that only needs safety shouldn’t depend on a surface that can spend money. It is also the swap point for a non-LLM classifier.AiUsageCapis a read-only projection over usage + spend. Different lifecycle, different consumer (admin/observability, never generation code).
The split makes capabilities.requires honest: a consumer that declares
[AiClientCap, AiModerationCap] gets a boot-time error if the package isn’t
installed, and says exactly what it uses.
Source: packages/slingshot-ai/src/public.ts
AiUsageRecord
Section titled “AiUsageRecord”Source: packages/slingshot-ai/src/entities/aiUsage.ts
CONSERVATIVE_CAPABILITIES
Section titled “CONSERVATIVE_CAPABILITIES”Capability descriptors: defaults, merging, and consistency checks.
Source: packages/slingshot-ai/src/provider/capabilities.ts
DEFAULT_PRICING
Section titled “DEFAULT_PRICING”Cost computation.
The load-bearing distinction in this file is null vs 0:
- a NUMBER → we know the price and this is the cost
0→ genuinely free (local inference)null→ we do NOT know the price
null must survive aggregation (as unpricedCalls) rather than being summed
as zero. A dashboard that quietly reports $0.00 because it couldn’t price
half the calls is worse than one that says “I don’t know”.
Source: packages/slingshot-ai/src/lib/pricing.ts
Classes
Section titled “Classes”AiConfigError
Section titled “AiConfigError”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
AiContentBlockedError
Section titled “AiContentBlockedError”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
AiError
Section titled “AiError”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
AiProviderError
Section titled “AiProviderError”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
AiRateLimitError
Section titled “AiRateLimitError”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
AiRefusalError
Section titled “AiRefusalError”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
AiSpendLimitError
Section titled “AiSpendLimitError”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
AiStructuredOutputError
Section titled “AiStructuredOutputError”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
AiTimeoutError
Section titled “AiTimeoutError”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
AiUnsupportedFeatureError
Section titled “AiUnsupportedFeatureError”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
Interfaces
Section titled “Interfaces”AiCacheAdapter
Section titled “AiCacheAdapter”The seams between the orchestrator and the framework.
Every one of these is STRUCTURAL — the real framework types satisfy them without being imported here. Two reasons that matters:
- The orchestrator stays unit-testable with three-line fakes, so the tests that assert the interesting behavior (fail-closed moderation, pre-flight spend, cache coalescing) don’t have to boot an app.
- Every one of them is OPTIONAL. An app with no cache adapter, no queue, and no database still gets a fully working AI client — it just gets the in-memory versions, and says so.
Source: packages/slingshot-ai/src/lib/seams.ts
AiClient
Section titled “AiClient”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiDegradation
Section titled “AiDegradation”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiEventBus
Section titled “AiEventBus”The seams between the orchestrator and the framework.
Every one of these is STRUCTURAL — the real framework types satisfy them without being imported here. Two reasons that matters:
- The orchestrator stays unit-testable with three-line fakes, so the tests that assert the interesting behavior (fail-closed moderation, pre-flight spend, cache coalescing) don’t have to boot an app.
- Every one of them is OPTIONAL. An app with no cache adapter, no queue, and no database still gets a fully working AI client — it just gets the in-memory versions, and says so.
Source: packages/slingshot-ai/src/lib/seams.ts
AiItemVerdict
Section titled “AiItemVerdict”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiLogger
Section titled “AiLogger”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
AiModerationRequest
Section titled “AiModerationRequest”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiModerator
Section titled “AiModerator”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiProvider
Section titled “AiProvider”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
AiProviderInfo
Section titled “AiProviderInfo”Source: packages/slingshot-ai/src/types.ts
AiRequestBase
Section titled “AiRequestBase”Source: packages/slingshot-ai/src/types.ts
AiResult
Section titled “AiResult”Source: packages/slingshot-ai/src/types.ts
AiSpendController
Section titled “AiSpendController”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiSpendReservation
Section titled “AiSpendReservation”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiSpendReservationRequest
Section titled “AiSpendReservationRequest”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiSpendSettlement
Section titled “AiSpendSettlement”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiStream
Section titled “AiStream”Source: packages/slingshot-ai/src/types.ts
AiStructuredRequest
Section titled “AiStructuredRequest”Source: packages/slingshot-ai/src/types.ts
AiUsage
Section titled “AiUsage”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiUsageFilter
Section titled “AiUsageFilter”Source: packages/slingshot-ai/src/types.ts
AiUsageReader
Section titled “AiUsageReader”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiUsageRecordView
Section titled “AiUsageRecordView”Source: packages/slingshot-ai/src/types.ts
AiUsageRow
Section titled “AiUsageRow”The seams between the orchestrator and the framework.
Every one of these is STRUCTURAL — the real framework types satisfy them without being imported here. Two reasons that matters:
- The orchestrator stays unit-testable with three-line fakes, so the tests that assert the interesting behavior (fail-closed moderation, pre-flight spend, cache coalescing) don’t have to boot an app.
- Every one of them is OPTIONAL. An app with no cache adapter, no queue, and no database still gets a fully working AI client — it just gets the in-memory versions, and says so.
Source: packages/slingshot-ai/src/lib/seams.ts
AiUsageStore
Section titled “AiUsageStore”The seams between the orchestrator and the framework.
Every one of these is STRUCTURAL — the real framework types satisfy them without being imported here. Two reasons that matters:
- The orchestrator stays unit-testable with three-line fakes, so the tests that assert the interesting behavior (fail-closed moderation, pre-flight spend, cache coalescing) don’t have to boot an app.
- Every one of them is OPTIONAL. An app with no cache adapter, no queue, and no database still gets a fully working AI client — it just gets the in-memory versions, and says so.
Source: packages/slingshot-ai/src/lib/seams.ts
AiUsageSummary
Section titled “AiUsageSummary”Source: packages/slingshot-ai/src/types.ts
AiVerdict
Section titled “AiVerdict”Source: packages/slingshot-ai/src/types.ts
CachedSystem
Section titled “CachedSystem”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
NormalizedRequest
Section titled “NormalizedRequest”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
NormalizedStructured
Section titled “NormalizedStructured”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderCapabilities
Section titled “ProviderCapabilities”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderDeps
Section titled “ProviderDeps”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderResult
Section titled “ProviderResult”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderStream
Section titled “ProviderStream”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderUsage
Section titled “ProviderUsage”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
RenderedSystemBlock
Section titled “RenderedSystemBlock”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
SpendStatus
Section titled “SpendStatus”Source: packages/slingshot-ai/src/types.ts
SystemSegment
Section titled “SystemSegment”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiBackgroundHandle
Section titled “AiBackgroundHandle”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiContentPart
Section titled “AiContentPart”Source: packages/slingshot-ai/src/types.ts
AiDegradableFeature
Section titled “AiDegradableFeature”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiEffort
Section titled “AiEffort”Source: packages/slingshot-ai/src/types.ts
AiErrorCode
Section titled “AiErrorCode”Error taxonomy for slingshot-ai.
Consumers catch THESE classes — never a provider SDK’s own error types. Each adapter maps its SDK errors into this taxonomy (most-specific-first, never by string-matching), which is what lets an app swap Anthropic for a local model without touching a single catch block.
The throw-vs-return rule:
- THROW when the call produced no usable result through no choice of the caller (bad config, spend limit, rate limit after retries, connection failure, unparseable structured output, refusal, timeout).
- RETURN when the system worked and made a decision the caller may want to
inspect (moderation verdicts, degradations, usage,
stopReason: 'max_tokens'— the caller decides whether truncation matters).
Source: packages/slingshot-ai/src/errors.ts
AiImagePart
Section titled “AiImagePart”Source: packages/slingshot-ai/src/types.ts
AiMessage
Section titled “AiMessage”Source: packages/slingshot-ai/src/types.ts
AiMessageContent
Section titled “AiMessageContent”Source: packages/slingshot-ai/src/types.ts
AiPackageConfig
Section titled “AiPackageConfig”Source: packages/slingshot-ai/src/config.ts
AiPackageConfigInput
Section titled “AiPackageConfigInput”Source: packages/slingshot-ai/src/config.ts
AiPluginConfig
Section titled “AiPluginConfig”Configuration for slingshot-ai.
Note the deliberate absence of a top-level .superRefine():
validatePluginConfig is typed <S extends z.ZodObject> and
warnUnknownPluginKeys introspects the object shape, so a refinement wrapper
would break both. Cross-field checks live in createAiPackage() as plain
imperative throws — which also produces better error messages.
Source: packages/slingshot-ai/src/config.ts
AiPluginConfigInput
Section titled “AiPluginConfigInput”Configuration for slingshot-ai.
Note the deliberate absence of a top-level .superRefine():
validatePluginConfig is typed <S extends z.ZodObject> and
warnUnknownPluginKeys introspects the object shape, so a refinement wrapper
would break both. Cross-field checks live in createAiPackage() as plain
imperative throws — which also produces better error messages.
Source: packages/slingshot-ai/src/config.ts
AiProviderConfig
Section titled “AiProviderConfig”Source: packages/slingshot-ai/src/config.ts
AiSeverity
Section titled “AiSeverity”Source: packages/slingshot-ai/src/types.ts
AiStopReason
Section titled “AiStopReason”Source: packages/slingshot-ai/src/types.ts
AiStreamEvent
Section titled “AiStreamEvent”Source: packages/slingshot-ai/src/types.ts
AiTags
Section titled “AiTags”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts
AiTextPart
Section titled “AiTextPart”Source: packages/slingshot-ai/src/types.ts
ModelPricing
Section titled “ModelPricing”Source: packages/slingshot-ai/src/types.ts
ProviderFactory
Section titled “ProviderFactory”Configuration for slingshot-ai.
Note the deliberate absence of a top-level .superRefine():
validatePluginConfig is typed <S extends z.ZodObject> and
warnUnknownPluginKeys introspects the object shape, so a refinement wrapper
would break both. Cross-field checks live in createAiPackage() as plain
imperative throws — which also produces better error messages.
Source: packages/slingshot-ai/src/config.ts
ProviderStreamEvent
Section titled “ProviderStreamEvent”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
StructuredMode
Section titled “StructuredMode”The provider seam.
THE RULE THAT MAKES THIS PACKAGE WORK: providers are dumb transports.
A provider translates one NormalizedRequest into one HTTP call and
normalizes the response back into a ProviderResult. That is all it does.
Providers do NOT:
- validate schemas (the orchestrator
safeParses every result, from every provider — seeProviderResult.structuredbelow) - compute cost (they report raw token counts; pricing is orchestrator policy)
- decide retries (they classify errors; the retry layer decides)
- moderate (that is an independent call, possibly to a different provider)
Keeping policy out of adapters is why adding a new provider later is mechanical: write the translation, declare your capabilities, pass the conformance suite.
Source: packages/slingshot-ai/src/provider/types.ts
SystemPrompt
Section titled “SystemPrompt”The neutral surface: what apps consume.
Nothing here names a provider. An app written against these types runs on Claude, on GPT, or on a local Llama with a config change and no code change.
Source: packages/slingshot-ai/src/types.ts