@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”Reject internally-incoherent descriptors at construction time.
The important one: promptCaching: 'explicit' without promptCacheMinTokens
is a trap, because the orchestrator would have no threshold below which to
refuse a breakpoint — and a below-minimum breakpoint is accepted by the API
and then silently does nothing.
function assertCapabilitiesConsistent(caps: ProviderCapabilities, who: string): voidSource: packages/slingshot-ai/src/provider/capabilities.ts
buildProvider
Section titled “buildProvider”Resolve one configured provider into a live AiProvider.
Resolution order, most-specific first:
provider— a ready-made instance (DI; how tests inject the fake).createProvider— a custom factory (the escape hatch).kindin the built-in registry (kind defaults to the config key, soproviders: { anthropic: {...} }needs no explicitkind).
async function buildProvider(name: string, config: AiProviderConfig, deps: BuildProviderDeps,): Promise<AiProvider>Source: packages/slingshot-ai/src/provider/registry.ts
builtinProviderKinds
Section titled “builtinProviderKinds”The kinds currently registered. Used in error messages and by tests.
function builtinProviderKinds(): readonly string[]Source: packages/slingshot-ai/src/provider/registry.ts
chooseStructuredMode
Section titled “chooseStructuredMode”Pick how to get JSON out of this provider.
function chooseStructuredMode(capabilities: ProviderCapabilities): StructuredModeSource: packages/slingshot-ai/src/lib/structured.ts
createAiPackage
Section titled “createAiPackage”Create the slingshot-ai package.
Two framework contracts shape this file:
-
Secrets. Each provider’s API key is resolved from the framework secret repository during
setupMiddleware— never fromprocess.env— and a provider that declares a key it cannot get fails the BOOT. A party should not discover a missing API key at the moment the first player taps a button. -
Capability publication is declarative. The framework resolves the values in
capabilities.providesEAGERLY, atpublishPackageRuntimeState, which runs before this package’ssetupMiddleware— so the real client does not exist yet at resolution time. Publishing imperatively instead doesn’t work either: the framework re-runs its declarative pass at the top ofsetupPostand wipes the slot. The way through (precedent:slingshot-notifications) is to publish stable FACADES whose methods defer to a ref thatsetupMiddlewarefills in. Consumers get one identity-stable object for the package’s lifetime, and a consumer that somehow reaches a method before boot completes gets a precise error rather thanundefined.
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”Any endpoint speaking /chat/completions. baseUrl + defaultModel required.
function createOpenAiCompatibleProvider(name: string, config: AiProviderConfig, deps: BuildProviderDeps,): AiProviderSource: packages/slingshot-ai/src/provider/openaiCompatible.ts
createOpenAiProvider
Section titled “createOpenAiProvider”OpenAI proper — the same adapter with the endpoint, capabilities, and prices filled in.
function createOpenAiProvider(name: string, config: AiProviderConfig, deps: BuildProviderDeps,): AiProviderSource: packages/slingshot-ai/src/provider/openaiCompatible.ts
extractJson
Section titled “extractJson”Extract a JSON object from text that may be wrapped in prose, fences, or both.
A model told “reply with JSON” will nonetheless say “Sure! Here’s the JSON:” and wrap it in ```json. This is not a bug in the model; it is the reality of the prompt path, and it is what this function is for.
function extractJson(text: string): string | nullSource: packages/slingshot-ai/src/lib/structured.ts
parseStructured
Section titled “parseStructured”The single validation point for EVERY provider.
advisory is whatever parsed object the provider handed us (Anthropic’s
parsed_output, OpenAI’s parsed content). We do not trust it — we validate
it. If it’s absent or wrong, we fall back to parsing the text.
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”Register a built-in adapter. Called by the adapter modules at import time.
function registerBuiltinProvider(kind: string, factory: ProviderFactory): voidSource: packages/slingshot-ai/src/provider/registry.ts
resolveCapabilities
Section titled “resolveCapabilities”Merge an adapter’s declared capabilities with any config-level overrides.
function resolveCapabilities(base: ProviderCapabilities, overrides?: Partial<ProviderCapabilities>,): ProviderCapabilitiesSource: packages/slingshot-ai/src/provider/capabilities.ts
sanitizeJsonSchema
Section titled “sanitizeJsonSchema”Strip keywords a strict structured-output provider rejects, and force
additionalProperties: false on every object (which those providers require).
Recursion ($ref pointing at an ancestor) is not supported by strict
structured output either; we detect and reject it loudly rather than send
something that will 400 with a much worse message.
function sanitizeJsonSchema(input: unknown): SanitizeResultSource: packages/slingshot-ai/src/lib/structured.ts
toJsonSchema
Section titled “toJsonSchema”Convert a zod schema to a sanitized JSON Schema.
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”Provider-owned package contract for slingshot-ai.
Source: packages/slingshot-ai/src/public.ts
AiClientCap
Section titled “AiClientCap”Generation: generate / generateStructured / stream / background.
Source: packages/slingshot-ai/src/public.ts
AiModerationCap
Section titled “AiModerationCap”Content moderation verdicts. May be backed by a different provider than generation.
Source: packages/slingshot-ai/src/public.ts
AiUsageCap
Section titled “AiUsageCap”Usage, cost, and spend reads.
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”The conservative baseline.
Every field assumes the LEAST capable plausible backend. An adapter that forgets to declare something therefore gets degraded-but-correct behavior rather than silently-wrong behavior. That asymmetry is on purpose: the cost of under-declaring is a JSON repair loop; the cost of over-declaring is a card that never validates and a party that stops.
Source: packages/slingshot-ai/src/provider/capabilities.ts
DEFAULT_PRICING
Section titled “DEFAULT_PRICING”Built-in price table, per million tokens.
Config providers[x].pricing deep-merges OVER this, so a price change never
requires a package release.
Source: packages/slingshot-ai/src/lib/pricing.ts
Classes
Section titled “Classes”AiConfigError
Section titled “AiConfigError”Invalid configuration: unknown provider kind, missing API key for a
configured provider, an undefined moderation policy, a defaultProvider that
names no configured provider. Always thrown at boot or at call-construction
time — never mid-flight.
Source: packages/slingshot-ai/src/errors.ts
AiContentBlockedError
Section titled “AiContentBlockedError”Moderation blocked the content and the request asked for onBlocked: 'throw'.
Source: packages/slingshot-ai/src/errors.ts
AiError
Section titled “AiError”Base class for every error the package throws.
Source: packages/slingshot-ai/src/errors.ts
AiProviderError
Section titled “AiProviderError”A provider returned an error. Wraps the underlying SDK/HTTP failure.
retryable is the adapter’s judgment, and the retry layer honors it — a 400
is never retried, a connection reset always is.
Source: packages/slingshot-ai/src/errors.ts
AiRateLimitError
Section titled “AiRateLimitError”Rate limited. retryAfterMs comes from the provider when it supplies one.
Source: packages/slingshot-ai/src/errors.ts
AiRefusalError
Section titled “AiRefusalError”The model declined the request (Anthropic’s stop_reason: 'refusal', which
arrives as an HTTP 200 with empty or partial content — hence a distinct class
rather than a provider error).
Source: packages/slingshot-ai/src/errors.ts
AiSpendLimitError
Section titled “AiSpendLimitError”The pre-flight spend guard refused the call.
Thrown BEFORE the HTTP request — a post-hoc check notices a runaway loop, a pre-flight check stops it.
Source: packages/slingshot-ai/src/errors.ts
AiStructuredOutputError
Section titled “AiStructuredOutputError”The model’s output could not be coerced into the requested schema, even after the repair loop. Carries the raw text and the final Zod error so the caller can log what actually came back — the single most useful thing to see when a local model misbehaves.
Source: packages/slingshot-ai/src/errors.ts
AiTimeoutError
Section titled “AiTimeoutError”The request exceeded timeoutMs.
Source: packages/slingshot-ai/src/errors.ts
AiUnsupportedFeatureError
Section titled “AiUnsupportedFeatureError”The caller asked for a feature the selected provider does not support, and
the package is configured with degradation: 'strict'.
Under the default degradation: 'warn' this is NOT thrown — the orchestrator
degrades and records an AiDegradation on the result instead.
Source: packages/slingshot-ai/src/errors.ts
Interfaces
Section titled “Interfaces”AiCacheAdapter
Section titled “AiCacheAdapter”Satisfied by the framework’s CacheAdapter. Strings only — we serialize.
Source: packages/slingshot-ai/src/lib/seams.ts
AiClient
Section titled “AiClient”Generation. Consumed via ctx.capabilities.require(AiClientCap).
Source: packages/slingshot-ai/src/types.ts
AiDegradation
Section titled “AiDegradation”A record that you got less than you asked for.
This is the package’s central honesty mechanism. AiResult.degradations is
empty exactly when everything requested was honored — so an app (or a test)
can assert result.degradations.length === 0 and mean it.
Source: packages/slingshot-ai/src/types.ts
AiEventBus
Section titled “AiEventBus”Satisfied by the framework’s SlingshotEventBus (via its dynamic overload).
Source: packages/slingshot-ai/src/lib/seams.ts
AiItemVerdict
Section titled “AiItemVerdict”Per-item verdict when an array was moderated in one batched call.
Source: packages/slingshot-ai/src/types.ts
AiLogger
Section titled “AiLogger”The slice of Logger this package needs. Structural, so any Logger satisfies it.
Source: packages/slingshot-ai/src/provider/types.ts
AiModerationRequest
Section titled “AiModerationRequest”Ask the moderator to check this call’s output before it reaches the caller.
Source: packages/slingshot-ai/src/types.ts
AiModerator
Section titled “AiModerator”Safety verdicts. A separate capability because it is independently useful: moderating player-typed content involves no generation at all, and a package that only needs safety should not take a dependency on a surface that can spend money making tokens. It is also the natural swap point for a non-LLM classifier.
Source: packages/slingshot-ai/src/types.ts
AiProvider
Section titled “AiProvider”The transport contract every adapter implements.
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”App-owned durable spend enforcement seam.
Slingshot invokes this for every provider attempt, including retries and
structured-output repairs. Throwing from reserve prevents the paid call.
Source: packages/slingshot-ai/src/types.ts
AiSpendReservation
Section titled “AiSpendReservation”Reservation returned by a durable, request-scoped spend controller.
Source: packages/slingshot-ai/src/types.ts
AiSpendReservationRequest
Section titled “AiSpendReservationRequest”One provider attempt presented to an app-supplied durable budget controller.
Source: packages/slingshot-ai/src/types.ts
AiSpendSettlement
Section titled “AiSpendSettlement”Actual accounting supplied when a reserved provider attempt finishes.
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
AiTool
Section titled “AiTool”A tool the model may call.
execute lives here — the framework runs the loop — because spend re-entry,
the iteration cap and argument validation are the orchestrator’s job, and an
app-authored while loop around generate() cannot do any of the three. The
app still observes every call through AiStream events and
AiResult.toolCalls, which is what a tool-trace UI needs and all it needs.
The framework NEVER retries execute. Retry policy for a side-effecting app
function is the app’s business; the package’s retry layer covers the provider
call only.
A throwing execute becomes an isError tool result the model can react to.
It does not kill the turn and it does not surface as an exception to the app.
Source: packages/slingshot-ai/src/types.ts
AiToolCallRecord
Section titled “AiToolCallRecord”What one tool call did, recorded on the result.
Source: packages/slingshot-ai/src/types.ts
AiToolContext
Section titled “AiToolContext”What the orchestrator hands a tool when it runs it. Deliberately minimal.
Source: packages/slingshot-ai/src/types.ts
AiUsage
Section titled “AiUsage”Token counts plus cost. costUsd: null means UNKNOWN; 0 means genuinely free.
Source: packages/slingshot-ai/src/types.ts
AiUsageFilter
Section titled “AiUsageFilter”Source: packages/slingshot-ai/src/types.ts
AiUsageReader
Section titled “AiUsageReader”Reads over usage/cost/spend. Consumed by admin surfaces, never by generation code.
Source: packages/slingshot-ai/src/types.ts
AiUsageRecordView
Section titled “AiUsageRecordView”Source: packages/slingshot-ai/src/types.ts
AiUsageRow
Section titled “AiUsageRow”One persisted usage row. Mirrors the AiUsageRecord entity.
Source: packages/slingshot-ai/src/lib/seams.ts
AiUsageStore
Section titled “AiUsageStore”Persistence for the usage ledger. Satisfied by the entity adapter.
write is deliberately fire-and-forget from the orchestrator’s point of view:
a failed usage INSERT must never fail the generation the user is waiting on.
It is a ledger, not a transaction.
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”A system prompt split by stability.
stable MUST be byte-identical across calls — any change invalidates the
prompt cache for everything after it. The package hashes each segment and
warns, naming the offending segment, when one drifts. Put per-call content
(a timestamp, a match id, a roster) in volatile, which is always rendered
after the cache breakpoint.
Source: packages/slingshot-ai/src/types.ts
NormalizedRequest
Section titled “NormalizedRequest”Everything a provider needs to make exactly one call.
Source: packages/slingshot-ai/src/provider/types.ts
NormalizedStructured
Section titled “NormalizedStructured”The structured-output ask, pre-resolved by the orchestrator.
Source: packages/slingshot-ai/src/provider/types.ts
NormalizedTool
Section titled “NormalizedTool”One tool, in the shape a transport needs.
The orchestrator has already sanitized the JSON Schema (same treatment as
NormalizedStructured.jsonSchema), so an adapter sends it as-is.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderCapabilities
Section titled “ProviderCapabilities”What a provider can actually do.
The orchestrator reads this to decide how to satisfy a request and what to
record as a degradation when it cannot. Apps read it via
client.capabilitiesOf(provider) to ask “can I rely on X here?”.
An adapter MUST declare these honestly. Over-declaring is the only way to get silently wrong behavior out of this package.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderDeps
Section titled “ProviderDeps”What the package hands an adapter factory. The API key is resolved by the PACKAGE.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderResult
Section titled “ProviderResult”What a provider hands back.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderStream
Section titled “ProviderStream”A stream of deltas plus the assembled result.
Conformance invariant: the concatenation of every text delta MUST equal
(await finalResult()).text. Adapters that emulate streaming over a
non-streaming API satisfy this trivially; real streaming adapters get it
wrong surprisingly often, which is why the suite asserts it.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderToolCall
Section titled “ProviderToolCall”A tool call as it came off the wire.
argumentsJson is verbatim provider text — NOT parsed, NOT validated, NOT
repaired. An adapter that pre-parses this is making a policy decision, which
is the one thing a transport must never do.
Source: packages/slingshot-ai/src/provider/types.ts
ProviderUsage
Section titled “ProviderUsage”Raw token counts. Cost is NOT the provider’s job.
inputTokens MUST EXCLUDE the cached counts. The four fields are DISJOINT
— pricing.computeUsage() bills them additively, so an adapter that reports a
total which already contains its cache reads charges for them twice.
This is not hypothetical: Anthropic reports disjoint counts natively
(input_tokens excludes cache_read_input_tokens), while the OpenAI family
reports prompt_tokens as a TOTAL with cached_tokens as a subset of it.
Adapters in that family must subtract. DeepSeek, helpfully, reports the
already-disjoint prompt_cache_miss_tokens.
Source: packages/slingshot-ai/src/provider/types.ts
RenderedSystemBlock
Section titled “RenderedSystemBlock”A single rendered system block. cache: true marks a breakpoint AFTER this block.
Source: packages/slingshot-ai/src/provider/types.ts
SpendStatus
Section titled “SpendStatus”Source: packages/slingshot-ai/src/types.ts
SystemSegment
Section titled “SystemSegment”One addressable chunk of the system prompt. The id is what drift warnings name.
Source: packages/slingshot-ai/src/types.ts
AiBackgroundHandle
Section titled “AiBackgroundHandle”Discriminated on purpose: a caller physically cannot ignore whether the work
was queued or ran inline. {runId?: string} would let them.
Source: packages/slingshot-ai/src/types.ts
AiContentPart
Section titled “AiContentPart”Source: packages/slingshot-ai/src/types.ts
AiDegradableFeature
Section titled “AiDegradableFeature”Every feature the orchestrator can silently do worse. It never does so silently.
Source: packages/slingshot-ai/src/types.ts
AiEffort
Section titled “AiEffort”Source: packages/slingshot-ai/src/types.ts
AiErrorCode
Section titled “AiErrorCode”Machine-readable discriminator carried by every error in the taxonomy.
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”Canonical plugin configuration name.
Source: packages/slingshot-ai/src/config.ts
AiPluginConfigInput
Section titled “AiPluginConfigInput”Raw input accepted by the AI plugin configuration schema.
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”tool_call_delta and tool_call are two different things and neither is
derivable from the other.
tool_call_deltais passed through LIVE from the transport, one raw fragment of the model’s argument JSON at a time. It is advisory. Its job is latency: the function NAME lands in the first frame, hundreds of milliseconds before the arguments finish, so a trace UI can render “calling get_lift_trend…” immediately instead of showing nothing. It is emitted for calls that later fail validation too, because a trace is a record of what the model did.tool_callis emitted once per call, AFTERJSON.parseandschema.safeParse, with the validated arguments. This is the one an app may act on. Half of{"lift":"squis not arguments.
tool_result deliberately carries no return payload: a result can be large,
the app already has it (it wrote the tool), and a stream is the wrong place to
ship a page of rows.
Source: packages/slingshot-ai/src/types.ts
AiTags
Section titled “AiTags”Arbitrary labels recorded on the usage record and used as metric labels.
Source: packages/slingshot-ai/src/types.ts
AiTextPart
Section titled “AiTextPart”Source: packages/slingshot-ai/src/types.ts
AiToolCallPart
Section titled “AiToolCallPart”Source: packages/slingshot-ai/src/types.ts
AiToolChoice
Section titled “AiToolChoice”Source: packages/slingshot-ai/src/types.ts
AiToolResultPart
Section titled “AiToolResultPart”Source: packages/slingshot-ai/src/types.ts
ModelPricing
Section titled “ModelPricing”Source: packages/slingshot-ai/src/types.ts
ProviderFactory
Section titled “ProviderFactory”A factory for a custom provider — the escape hatch for anything not built in.
Source: packages/slingshot-ai/src/config.ts
ProviderStreamEvent
Section titled “ProviderStreamEvent”A streamed delta.
tool_call_delta is INCREMENTAL and advisory: providers dribble a function’s
arguments out over many frames, and argumentsDelta is one raw fragment of
that JSON. It exists so a trace UI can render “calling get_lift_trend…” the
moment the name lands rather than waiting for the whole call. name is filled
in on every event from the adapter’s own by-index accumulator, including the
argument-only frames that carry no name on the wire.
It shares its NAME and SHAPE with the public AiStreamEvent variant on
purpose, so the orchestrator forwards it by identity. Two events called
tool_call with different shapes on either side of this seam would be a trap
— the validated public tool_call carries parsed args, and nothing here can
produce those.
Source: packages/slingshot-ai/src/provider/types.ts
StructuredMode
Section titled “StructuredMode”How the orchestrator decided to get JSON out of this provider, given its declared capabilities. The adapter uses this to pick its request idiom.
Source: packages/slingshot-ai/src/provider/types.ts
SystemPrompt
Section titled “SystemPrompt”A plain string is treated as fully volatile (never cached).
Source: packages/slingshot-ai/src/types.ts