npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pieai/swimmer-ai-provider-kit

v0.17.0

Published

Server-only AI provider authorization, model transport, media and usage primitives for PieAI products.

Readme

SwimmerAIProviderKit

AI provider authorization, model transport, media and usage primitives shared by PieAI products. Server-only by default; the explicit ./openai-live-webrtc leaf contains credential-free browser media controls and never imports server auth.

This package is the product-line AI socket. Product code must not call OpenRouter or model-vendor SDKs directly. The package connects providers; it does not own the application's agent, business operations or user-facing character.

Identity migration

The last independently confirmed public package is @pieai/[email protected]. The current 0.17.0 candidate adds the credential-check leaf described below; its registry publication is a separate gate. Existing xAI subscription OAuth and per-request TTS credentials keep their account, payer and dispatch boundaries. The current work index retains the publication and integration evidence. Earlier release receipts are historical, not instructions to publish those versions again. The pre-rename package remains available as @pieai/[email protected]. Consumer integration, real voice quality and product billing still require their own acceptance; they are not established by the package release. Existing published versions remain available and are not modified or unpublished.

All existing subpaths and the 63 root runtime exports are retained. New code should use named subpaths. The native tool stream is a separate opt-in subpath; the existing text adapters are still tool-free. See the active migration for preservation, validation, release and consumer status.

Manual API-key check (0.17)

import { checkProviderCredential } from '@pieai/swimmer-ai-provider-kit/credential-check';

// Server only, after host authentication and rate admission. Do not log the key.
const receipt = await checkProviderCredential({
  provider: 'xai', // or 'openrouter'; unsupported providers fail before I/O
  apiKey: submittedKey,
  signal: request.signal,
});
// receipt: provider, keyHash (SHA-256), last4, checkedAt,
// check='authenticated-metadata', modelAccess='not-tested'.

This performs one authenticated GET to the provider's official key metadata endpoint, not a public model list or a generated message/image. xAI's disabled, blocked-key and blocked-team responses are rejected. It uses the same explicit native-fetch pattern as the existing authorization transport, a 15-second abort bound, 32 KiB response bound, no redirects/retries, and sanitized typed errors. It adds no SDK/runtime dependency, token store or credential-discovery mechanism. The provider API, not local key syntax or a home-grown validation algorithm, confirms the credential. Unknown model access/billing remains unknown.

The host must bind its verified account and operation, then pass the same key and unmodified server-produced receipt to Backend's encrypted custody function. Never accept a receipt supplied by a browser or persist the key in ordinary preferences, logs or work archives. A receipt is not a signature or standalone authorization token; Backend grants its custody function only to the trusted product runtime. Encryption, durable save/revoke receipts and account ownership belong to SwimmerBackend, not this package.

Official contracts: OpenRouter key metadata and xAI API-key information.

Scope

Owns:

  • Server environment readers and fallback resolution
  • Product-owned model alias registries
  • OpenRouter model configuration and attribution headers
  • Provider-neutral chat-completion contract (./chat)
  • OpenRouter chat completion transport (./openrouter)
  • Gemini-direct chat completion transport (./gemini)
  • Privacy-safe, provider-neutral AI execution evidence validation
  • Local provider budget reservation/commit/release guards
  • Typed generator registries with optional provider-budget wrapping
  • Structured-output parse/generate on top of ./chat (./structured-output)
  • Named agent registry (./agents): lookup plus chat / structured invoke
  • Explicit Codex native tool streaming (./openai-codex-stream), without an agent loop, tool execution or automatic retry
  • Content safety (./content-safety): local text rules + Sightengine visual and adult-reference adapters
  • Dual TTS routing (./tts): language → western (ElevenLabs) / chinese (MiniMax)
  • Tripo 3D text-to-model adapter (./tripo)

Does not own prompts, product model policy, product schemas, game logic, wallet RPCs, database access, deployment topology, or a Mastra runtime. Each product deploys its own optional Mastra service and supplies its own model aliases, budgets, generators, standing instructions, and Zod (or other) schemas.

Public API contract

Import a named subpath. The root entry @pieai/swimmer-ai-provider-kit is a compatibility barrel for existing callers (SupaLuv). New code should not add root imports. Runtime values on the root barrel are locked by tests/public-exports.test.ts.

| Subpath | Stability | Source | Use it for | Do not use it for | | ---------------------------- | --------- | --------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | | ./env | stable | src/env.ts | Reading server env and key fallbacks | Client bundles, logging secret values | | ./models | stable | src/models.ts | Product alias registries; Mastra OpenRouter model config | Sending HTTP; owning product model policy | | ./chat | evolving | src/chat.ts | Typing a ChatCompletionTransport. ChatMessage.content is a string or text / image parts. | Calling a provider; parsing structured output | | ./structured-output | evolving | src/structured-output.ts | Declaring a { parse } schema and getting a validated object from chat text | Owning product schemas; wrapping Mastra; unifying transport errors | | ./agents | evolving | src/agents.ts | Looking up named model-call configurations and invoking them through ./chat / ./structured-output | An autonomous agent loop, tool execution, prompts or model policy | | ./openrouter | stable | src/openrouter.ts | Text and vision chat completions through OpenRouter. Optional signal cancels or times out a request. | Gemini-direct, product prompts | | ./openrouter-images | evolving | src/openrouter-images.ts | Bounded image requests and parsing a retained response without resubmission | Product adoption, image quality or billing truth | | ./openrouter-authorization | evolving | src/openrouter-authorization.ts | Official HTTPS S256 authorization URL and bounded, single code exchange | User/tenant state, encrypted custody, billing or supplier licensing | | ./openai-codex | evolving | src/openai-codex.ts | Device authorization, refresh, model inventory and a bounded tool-free text turn | Host account discovery, autonomous agents or commercial entitlement | | ./openai-codex-stream | evolving | src/openai-codex-stream.ts | One explicit-account native Pi stream with tools and tool-result history | Tool execution, durable sessions, consent or a hard output-token budget | | ./openai-live | evolving | src/openai-live.ts | One authenticated server-side GPT-Live session creation with client delegation | Browser API keys, subscriptions, retries, user consent or task execution | | ./openai-live-webrtc | evolving | src/openai-live-webrtc.ts | Credential-free official SDK WebRTC and acknowledged Live media controls | Server SDK/auth, backend task cancellation or proof that the user heard output | | ./openai-live-supervision | evolving | src/openai-live-supervision.ts | Server attachment to an existing session, original-deadline closure and trusted usage observations | Creating another session, product billing, or a provider-enforced hard cap | | ./pi-oauth | evolving | src/pi-oauth.ts | Pi-owned device login, refresh and bounded text for Copilot, xAI, Kimi Code and Radius | CLI credential discovery, brand login, arbitrary gateways, automatic account rotation | | ./xai-model-stream | evolving | src/xai-model-stream.ts | Explicit-account native Pi text and tool-result streaming | Another agent, tool execution or account fallback | | ./xai-media | evolving | src/xai-media.ts | One explicitly funded image/video submission and original-ID query | Implicit credentials, retries, entitlement or final billing guarantees | | ./gemini | evolving | src/gemini.ts | Gemini-direct ChatCompletionTransport for the same request shape | OpenRouter ids (google/…), product prompts, Mastra config | | ./execution-evidence | stable | src/execution-evidence.ts | Parsing privacy-safe usage/cost evidence | Pricing, billing decisions, raw provider payloads | | ./provider-budget | stable | src/provider-budget.ts | In-process cents reservation | Cross-process ledgers or wallet truth | | ./generator-registry | stable | src/generator-registry.ts | Wrapping named generators with optional budget | Owning generator implementations | | ./content-safety | stable | src/content-safety/ | Text/visual/adult-reference review; Sightengine signal normalize | Product visual thresholds, copy, or UI policy | | ./tts | stable | src/tts/ | Server-side speech synthesis with static or per-request caller keys | Browser bundles, ambient user-key discovery, billing policy | | ./tripo | stable | src/tripo.ts | Server-only Tripo text-to-model | Browser runtime; live image_to_model |

Expected consumers: server code in Pie products (Show, Collapse, SupaLuv, Sea, Anvil, TuringPact, Break, Non-Heroes). Browser code may import only the deliberately credential-free ./openai-live-webrtc leaf; never the root or server subpaths.

Caller-owned speech keys (0.16.0)

The ./tts entry remains server-only. Existing createElevenLabsTtsProvider({ apiKey }), createMiniMaxTtsProvider({ apiKey }) and createDualTtsFromEnv() calls continue to work. No environment variable is needed when the host explicitly resolves the authenticated caller's key:

import { createDualTtsRouter } from '@pieai/swimmer-ai-provider-kit/tts';

// Inside an authenticated server request; ownerId is server-verified, not a
// submitted characterId, email, arbitrary owner field or mutable global variable.
const router = createDualTtsRouter({
  resolveCredentials: async (provider) => {
    const apiKey = await encryptedAccountKeys.read(ownerId, provider);
    return apiKey ? { apiKey } : undefined;
  },
  elevenlabs: { voiceMap: approvedWesternVoices },
  minimax: { voiceMap: approvedChineseVoices },
});
const result = await router.synthesize({ text, language, characterId, signal });

The host owns those key-store/voice-map variables, user consent, encryption, authorization and cost limits. Keys never belong in browser synthesis bodies, URLs, logs or evidence. The resolver gets the selected provider and a read-only request snapshot, once per synthesis. It cannot select an endpoint. Failure, cancellation or a missing key never uses another account or application key, and never automatically resends the paid request. Trusted elevenlabs/minimax options select transport, model and voices separately from credentials. MiniMax uses Bearer authentication; no legacy GroupId is required.

durationMs and charactersBilled are optional observations, not price quotes: MiniMax maps extra_info.audio_length (milliseconds) and usage_characters; ElevenLabs preserves a numeric character-cost response header when supplied, without promising its availability or manufacturing a duration for binary audio. Missing/invalid metadata is omitted; zero is retained only when explicitly reported. The host must reconcile billing and verify actual audio separately. An aborted or failed request is not evidence of zero cost or permission to retry.

Both transports set redirect: 'error' so authorization cannot follow a redirect. TtsError contains only a stable code, provider and optional HTTP status; it never includes upstream response text, input text, keys or an unsafe error cause. Optional voice-list enumeration is not implemented; use the existing approved voice maps. Synthetic tests prove transport behavior, not real voices or delivery.

Provider contracts: MiniMax synchronous speech, ElevenLabs create speech.

Realtime voice (0.13.0 candidate)

./openai-live reuses [email protected], an optional peer separate from Pi's existing provider SDK. The host supplies an explicit API key, selected voice model, instructions, browser SDP offer, deadline and private request recorder. createOpenAILiveSession creates one store:false, client-delegated session at the official endpoint, bounds request/response bytes and disables retries. Only the opaque session ID and SDP answer are returned. The host must authenticate the user, authorize cost, validate origin, retain session identity and enforce its own lifetime before returning that answer. Origin checks alone are not login.

This API voice connection is distinct from an existing Codex subscription. An unacknowledged request remains unknown; retrying can create another billable session. Cancellation that races HTTP headers is not proof of a cancelled creation. The browser event allow-list does not admit developer-instruction changes or Responses tool calls. Browser context/receipts still cannot authorize backend work.

The optional onReturned hook retains the bounded HTTP response privately before SDK parsing. parseOpenAILiveSessionResponse can recover a retained successful response without calling the provider. The host must verify its original request/session/owner association. A lost retention acknowledgement is still unknown even if the response was successful; it does not permit another create.

./openai-live-webrtc exposes the official OpenAILiveWebRTC and LiveDataChannel rather than a copied SDP/state-machine implementation. createOpenAILiveControls binds before connecting, normalizes transcript fragments/delegation metadata and waits for session.started with the exact server-returned ID. Media remains muted until the owning application deliberately enables it after readiness.

connectOpenAILiveVoice assembles a dedicated single-audio-track lease and an output audio element around that SDK. It does not acquire media before the host's explicit start action. It releases even a permission request that resolves after cancellation. Its abort signal controls setup only; subsequent closure is explicit and never a backend-task cancellation. Existing unrelated tracks are untouched.

The controls take an application-owned, dedicated media lease. Closing releases that lease immediately, requests session.close once, waits a bounded time for session.closed, then closes the native connection. Unknown finalization remains unknown, not zero usage. Usage notifications are cumulative seconds, never sums across notifications. Muting/closing media never cancels a backend task.

Appended facts use a deliberately small 450-UTF-8-byte update budget; the provider remains authoritative for its 500-token limit. Oversized updates are refused, not truncated or routed through another model. A matching acknowledgement means accepted context, not exact wording, completed playback or user comprehension. Task revision/authorization and durable request deduplication belong to Nerve's host application, not this per-connection adapter.

For quiet application awareness, append({ kind: 'context', delegationId: null, ... }) sends the maintained SDK's session-wide thinking-context event. It is usable after readiness without creating a delegation or model task. The same exact acknowledgement and 450-byte limits apply. Null is not permitted for commentary: task-result speech still needs its real delegation identity. The host chooses and discloses any page or selection facts; ProviderKit neither reads the DOM nor creates developer instructions from application content.

The server-only superviseOpenAILiveSession uses the optional ws peer and the SDK's maintained sideband client to attach to an already created session. The product supplies its original absolute close deadline, explicit key, durable observation sink and a separately owned cancellation signal. The adapter never creates/forks a session, reconnects blindly, or schedules product tasks. Socket, observation and acknowledgement failures remain explicit uncertain outcomes. Final usage is provider-observed cumulative seconds, not client assertions. The product runs this bounded operation in its existing durable worker; a browser timer alone is not a spending control. Neither supervisor nor connection claims an absolute provider billing cap when the worker/network fails.

See the official WebRTC guide and client delegation. Current tests use the actual SDK with synthetic HTTP, a synthetic sideband socket and native browser WebRTC peers. The full media assembly also tests a late input lease after cancellation. These do not prove real microphones, Chinese speech quality, provider entitlement, latency, final charges or deployment readiness.

Additional Pi OAuth accounts

./pi-oauth reuses the pinned Pi provider implementations, not copies of their OAuth protocols. piOAuthCapabilities() is an offline inventory of the six requested families: the existing Codex and OpenRouter adapters plus GitHub Copilot, xAI, Kimi Code and Radius. Anthropic account login is excluded. Inventory presence is neither a real login nor permission to sell hosted subscription use. Radius billing is gateway-defined (subscription: null), not inferred as free.

import {
  authorizePiDevice,
  createPiOAuthChatTransport,
} from '@pieai/swimmer-ai-provider-kit/pi-oauth';

// Run in a durable, admitted, owner-bound server operation. The host provides
// the deadline, encrypted challenge/credential storage, consent and cancellation.
const account = await authorizePiDevice({
  provider: 'xai',
  signal: operationSignal,
  onDeviceCode: (challenge) => retainChallengeForThisOwner(challenge),
});
await retainCredentialForThisConnection(account);
const transport = await createPiOAuthChatTransport({
  account,
  model: selectedModelId,
  signal: taskSignal,
  onAttempt: (request) => retainPrivateTaskRequest(request),
});
// transport.complete() accepts one system message and one user message.
// The existing product task/runtime owns parsing, candidate review and adoption.

Each caller supplies a credential explicitly; no global account singleton, home-directory file, environment key fallback or framework login store is used. The host holds a cross-process claim before refreshPiOAuthCredential, saves the result before inference, and treats a lost outcome as unresolved. Kimi's maintained SDK may itself retry refresh; this adapter adds no retry. Copilot's maintained login can enable unconfigured model policies, so allowCopilotModelPolicyUpdates: true requires an informed product consent step. Enterprise/custom gateways are not silently admitted as arbitrary token destinations.

piOAuthTextModels() returns account-filtered model IDs; Radius reads its official gateway catalog. Catalogs remain connection-local. Model requests use Pi's maintained stream, with a fixed provider destination, caller deadline, one outbound attempt, no tools, bounded request/response bytes, and exact body evidence retained before dispatch. Errors do not expose provider response text or tokens. A displayed device code/URL is also private short-lived authorization material, not diagnostic metadata. Default tests use synthetic HTTP and standalone Node bundles, not real accounts or paid model calls. Directing UI, encrypted custody and live acceptance remain product integration work. See Pi providers and the installed SDK source for provider-specific behavior.

./openrouter-authorization is a provider-protocol adapter, not an Auth service. The host must pin its HTTPS callback to a user-bound, expiring, single-use attempt, persist exchanging before dispatch, and encrypt the returned key outside work/task records. An unknown exchange must not be retried: the provider may already have issued a key. The key is permanent/user-controlled, not a promise of refresh, subscription media allowances, or free model usage. Disconnect and provider-side revocation are separate actions. The adapter never reads environment credentials or starts a localhost server.

Reuse review (2026-09-14): this follows the official PKCE API contract with standard Node crypto and Fetch. Pi 0.85.1's existing OpenRouter flow is CLI-only and owns a loopback listener/manual-code race, so it cannot be directly hosted as the public callback. No Pi or third-party implementation source was copied. The host must separately satisfy the OpenRouter terms, model terms and regional restrictions; the protocol helper is not commercial permission or real-account evidence.

Internal assembly that happens to be exported (prefer the façade):

  • ./content-safety visual-asset helpers (assetLooksImage, visualAssetBlob, …)
  • ./content-safety FetchLike / individual Sightengine factories
  • ./content-safety parseSightengineResponse / maxSightengineScore / sightenginePathMatches (not on the root barrel)
  • ./content-safety createSightengineVisualTransport / readSightengineCredentials (not on the root barrel)
  • ./tts individual ElevenLabs / MiniMax factories when createDualTtsFromEnv is enough
  • ./tripo parse/map helpers when createTripoAdapter is enough

createOpenRouterChatTransport lives on ./openrouter. createGeminiChatTransport lives on ./gemini. Both implement ChatCompletionTransport. Neither is re-exported from the root barrel. parseStructuredOutput and createStructuredOutputClient live on ./structured-output only. createAgentRegistry lives on ./agents only.

Frozen seams

Re-measured 2026-08-13 (round 3). These look movable and are not. Do not re-open them in a later structural pass unless a real second responsibility or a dedicated capability plan appears.

| Seam | Leave it | Why | | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | src/tripo.ts as one file | Yes. ~1243 lines is a review trigger, not a split rule. | One provider pack (credentials, credit budget, transport, adapter, parse). Splitting would make the next Tripo change touch more files for the same job. | | ./models OpenRouter helpers | Yes. Keep createOpenRouterModel / createOpenRouterHeaders on ./models. | Collapse, TuringPact, and Show import ./models. Moving the path is a contract change. | | FetchLike / OpenRouterFetch / GeminiFetch / TripoFetch | Yes. Four test seams, not one shared fetch type. | Unifying them changes published types. | | OpenRouter's six redacted error strings | Yes. Do not rewrite them to match Gemini or a shared class. | Sea parses failed with (\d+). Collapse, Anvil, and SupaLuv catch the façade. A typed helper is a new capability and needs its own plan. | | Shared HTTP/JSON helpers extracted from OpenRouter + Gemini | No extract. | The overlap is isUnknownRecord / isAbortLike / maxTokens checks — not a missing layer. A utils file would be a grab bag. | | requestGeminiChatCompletion façade | Do not add. | The abstraction is already complete(). | | Per-directory Boundary README / knip / madge as gates | Do not add. | 25 files; rg is enough. Extra docs become parallel truth. | | Root barrel growth | Do not add ./chat, ./gemini, ./structured-output, ./agents, or new ./content-safety runtime values to .. | The barrel is locked at 63 runtime keys for existing root callers (SupaLuv). | | Default Sightengine visual decide | Yes. Keep 0.72 / 0.65 / 0.75, exclude nudity.context, and do not map OCR/PII. | Tightening it would silently change SupaLuv visual review. Product policy reads parseSightengineResponse. | | Whitespace-only Sightengine keys | Yes. trim() then missing → undefined. | Empty credentials are not credentials. Creating a provider that will 401 is worse than falling back to local visual-unavailable. |

ChatCompletionRequest.signal is optional, matching TtsSynthesizeRequest. The kit does not pick a default timeout; products that need one pass AbortSignal.timeout(ms). Without a signal the request JSON and fetch init are unchanged.

ChatMessage.content is either a string (the original call shape) or an array of { type: 'text', text } and { type: 'image', url } parts. url is a data: URL or an https: URL. OpenRouter serializes image parts to the OpenAI image_url wire shape. String-only callers do not change.

Show camera-vision migration

Replace server/openRouterHttp.ts / summarizeImageSeed with createOpenRouterChatTransport. Do not pass OpenRouter's nested image_url object — that is the wire format, not the kit contract.

import { createOpenRouterChatTransport } from '@pieai/swimmer-ai-provider-kit/openrouter';

const result = await createOpenRouterChatTransport({
  apiKey,
  appName: 'Show camera seed',
  appUrl: 'https://show.pieaistudio.com',
  fetchImpl,
}).complete({
  maxTokens: 90,
  messages: [
    {
      role: 'user',
      content: [
        { type: 'text', text: visionPrompt },
        { type: 'image', url: seed.dataUrl },
      ],
    },
  ],
  model: 'google/gemini-2.5-flash',
  signal: AbortSignal.timeout(15_000),
  temperature: 0.2,
});

const summary = result.content;

The same part array works for Show's OpenRouter visual-moderation path. Kit throws on HTTP / provider failure; Show currently inspects response.ok itself, so the catch / fail-closed mapping stays in Show. Do not change Show in this package.

When SHOW_MODEL_PROVIDER !== openrouter, use createGeminiChatTransport instead of stuffing Gemini into ./openrouter. Pass a Gemini model id (gemini-2.5-flash), not an OpenRouter id (google/gemini-2.5-flash). Gemini image parts must be data: URLs — the transport does not fetch https: images.

Non-Heroes character-AI migration

Replace new Mastra({ agents: { …: new Agent(…) } }). Keep product schemas, standing instructions, and the Colyseus-safe timeout wrapper in Non-Heroes.

import { createAgentRegistry } from '@pieai/swimmer-ai-provider-kit/agents';
import { createGeminiChatTransport } from '@pieai/swimmer-ai-provider-kit/gemini';
import { createOpenRouterChatTransport } from '@pieai/swimmer-ai-provider-kit/openrouter';

const transport =
  modelProvider === 'gemini'
    ? createGeminiChatTransport({ apiKey: geminiApiKey })
    : createOpenRouterChatTransport({
        apiKey: openRouterApiKey,
        appName: 'Non-Heroes character AI',
        appUrl: 'https://non-heroes.pieaistudio.com',
      });

const agents = createAgentRegistry({
  agents: {
    'non-heroes-boss-ai': {
      instructions: productOwnedBossInstructions,
      model: modelProvider === 'gemini' ? 'gemini-2.5-flash' : 'google/gemini-2.5-flash',
      name: 'Non-Heroes Boss AI',
    },
  },
  transport,
});

const result = await agents.generateStructured({
  agentId: 'non-heroes-boss-ai',
  instructions: perRequestInstructions,
  maxTokens: 320,
  prompt,
  schema: bossAiOutputSchema,
  signal: AbortSignal.timeout(timeoutMs),
  temperature: 0.35,
});

return result.object;

Pass a transport-native model id. Do not pass Mastra's openrouter/google/… wrapper to ./openrouter, and do not pass google/gemini-2.5-flash to ./gemini. Do not set process.env.GOOGLE_API_KEY just to satisfy Mastra. Zod schemas stay in the product — kit only calls schema.parse.

Usage

Do not upgrade consumers to the renamed package until its exact version is visible on the official registry. For an isolated evaluation of this source, verify and create a local package:

pnpm install --frozen-lockfile
pnpm verify
pnpm pack --out artifacts/swimmer-ai-provider-kit-0.12.0.tgz

The following imports describe the renamed API. Use the packed artifact in an isolated consumer, or the verified npm release after publication, and import only the narrow module needed by the service:

import {
  createContentModerationProvider,
  ADULT_COMEDY_MODERATION_POLICY,
} from '@pieai/swimmer-ai-provider-kit/content-safety';
import { createDualTtsRouter, resolveTtsRoute } from '@pieai/swimmer-ai-provider-kit/tts';
import { firstDefinedEnv } from '@pieai/swimmer-ai-provider-kit/env';
import { parseAiExecutionEvidence } from '@pieai/swimmer-ai-provider-kit/execution-evidence';
import { createModelRegistry, createOpenRouterModel } from '@pieai/swimmer-ai-provider-kit/models';
import { requestOpenRouterChatCompletion } from '@pieai/swimmer-ai-provider-kit/openrouter';
import { createGeminiChatTransport } from '@pieai/swimmer-ai-provider-kit/gemini';
import type { ChatCompletionTransport } from '@pieai/swimmer-ai-provider-kit/chat';
import { createStructuredOutputClient } from '@pieai/swimmer-ai-provider-kit/structured-output';
import { createAgentRegistry } from '@pieai/swimmer-ai-provider-kit/agents';

Execution evidence accepts bounded identifiers, exact integer-string usage and cost fields, and explicit completeness states. Unknown fields are discarded so raw prompts, completions, provider payloads, and accidental credential fields cannot cross through the parsed result. Products still decide pricing and whether a failed attempt is billable.

Assess a real-person reference separately from harmful-content moderation:

const moderation = createContentModerationProvider({
  sightengineApiUser: process.env.SIGHTENGINE_API_USER,
  sightengineApiSecret: process.env.SIGHTENGINE_API_SECRET,
});

const decision = await moderation.reviewAdultReference?.({
  kind: 'image',
  dataUrl: uploadedImageDataUrl,
});

if (decision?.status !== 'adult') {
  // Product decides how to explain minor, uncertain, or no_real_face.
}

The assessment uses Sightengine face-age, requires exactly one real face, and fails closed when credentials, the classifier, or a reliable score are unavailable. A normal photo of a minor is not labeled child exploitation: the result is minor_reference_detected, a feature eligibility decision.

Sightengine signals vs default decide

parseSightengineDecision is the published default decide. It does not change when a product wants a stricter (or looser) visual rule.

parseSightengineResponse is the normalize step that default decide used to throw away:

  • signals: every finite number in the JSON tree, with lowercased path keys. This includes text.email, nudity.context.indoor_other, nudity.bikini, and non-risk numbers such as request.operations.
  • raw: the classifier payload as returned. OCR match arrays and text.detected_categories are often strings, not numbers — they live here.

maxSightengineScore and sightenginePathMatches let a product write its own predicates. Example — block OCR email/phone the way Show's latch test does, without changing kit defaults:

import {
  maxSightengineScore,
  parseSightengineResponse,
  sightenginePathMatches,
} from '@pieai/swimmer-ai-provider-kit/content-safety';

const parsed = parseSightengineResponse(raw);
if (parsed.ok) {
  const pii = maxSightengineScore(parsed.signals, (path) =>
    sightenginePathMatches(path, [/email/, /phone/, /ssn/, /pii/]),
  );
  if (pii !== undefined && pii >= 0.7) {
    // Product decision. Kit default still allows this payload.
  }
}

nudity.context.indoor_other is a scene-location class (indoor vs beach/pool), not an adult-content score. Sightengine documents it for combining with swimwear ("allow bikini at a pool, not indoors"). A product that wants that rule should join nudity.bikini with nudity.context.*. Treating indoor_other alone as visual_adult is expressible and is probably wrong.

Whitespace-only apiUser / apiSecret stay missing credentials. readSightengineCredentials, createSightengineVisualTransport, and createSightengineVisualProvider all return undefined so callers fall back to local visual_review_unavailable. That is input validation, not a policy threshold.

Show Sightengine migration

Do not wrap kit default decide and then re-run Show's parser. Use the transport, then apply Show's predicates to signals / raw.

import {
  createSightengineVisualTransport,
  maxSightengineScore,
  moderationFallback,
  sightenginePathMatches,
} from '@pieai/swimmer-ai-provider-kit/content-safety';

const transport = createSightengineVisualTransport({
  apiSecret: process.env.SIGHTENGINE_API_SECRET,
  apiUser: process.env.SIGHTENGINE_API_USER,
  fetchImpl,
});

const checked = await transport?.check(asset);
if (!checked) {
  // keys missing — same as today's kit factory
}
if (!checked?.ok) {
  return moderationFallback(checked?.reasonCode ?? 'visual_review_unavailable', 'sightengine');
}

const pii = maxSightengineScore(checked.signals, (path) =>
  sightenginePathMatches(path, [/email/, /phone/, /ssn/, /pii/]),
);
if (pii !== undefined && pii >= 0.7) {
  return {
    allowed: false,
    category: 'private_info',
    provider: 'sightengine',
    reasonCode: 'visual_private_info',
  };
}

Kit still does not own Show's local text rules, reviewOutput asset extraction, or OpenRouter vision fallback. FormData filenames stay asset-image.png / asset-video.mp4; Sightengine does not use the name as a signal. Do not change Show in this package.

Secrets are inputs only. This package never stores, logs, or bundles secret values.

Images API

createOpenRouterImageTransport and OpenRouterImageError live on @pieai/swimmer-ai-provider-kit/openrouter-images, not the root barrel. The transport uses the dedicated Images API reviewed on 2026-09-13, not a chat-output workaround. Existing brand headers and usage normalization are reused; normalizeOpenRouterUsage is now also exported from ./openrouter without changing its behavior.

Each request requires a model, provider tag, prompt and caller-owned abort signal. It pins one image, allow_fallbacks: false, buffered PNG output and up to four bounded inline PNG/JPEG/WebP reference data URLs. It does not fetch reference URLs, retry, load host auth, discover models, choose prices, reserve funds, write files or adopt media. Products must verify the selected endpoint's current capabilities and price before obtaining consent.

The request hook runs after serialization and before sending; failure prevents dispatch. The returned-body hook receives bounded raw data before parsing for product-owned recovery. Hooks contain no headers but may contain private work or reference bytes, so products must apply their own authorized, redacted retention boundary. Error causes distinguish cancellation, rejection, storage, transport and invalid responses while financial outcome remains unknown after dispatch. Never infer a refund or retry from cancellation alone.

Provider-reported MIME and canonical base64 are transport evidence, not proof of valid media or artistic quality. Products still inspect bytes and candidates before adopting them. Video generation is not implemented by this subpath; existing TTS and vision contracts remain separate. Only synthetic HTTP tests have run; no real image, provider account or model bill has been tested.

The transport first shipped under the old package name in 0.7.0. Version 0.9.0 added parseOpenRouterImageResponse(model, retainedBody) on this same subpath, so a product can recover a previously retained response without calling generation again. It checks the bounded response shape/base64/MIME and normalizes usage; the product still verifies actual image bytes, owner/task binding, adoption and settlement. Parsing is not proof of image quality or authority to charge a user.

User-owned Codex connection (server only)

@pieai/swimmer-ai-provider-kit/openai-codex consumes the public APIs of the optional peer @earendil-works/[email protected]. Install that peer only in servers using this subpath; existing transports and root exports are unchanged. Node 24 is required.

Use authorizeCodexDevice({ signal, onDeviceCode }) in an isolated, bounded job. The callback receives only the official verification URL, user-visible code and expiry, not the private device identifier. Store the returned credential through the host's encrypted, owner-bound custody service. No credential is discovered from the machine, environment, browser or global Pi session. The host must obtain appropriate account consent and verify the provider's supported deployment use; this library does not grant commercial permission.

refreshCodexCredential(credential, signal) performs one refresh. The host holds an exclusive cross-process connection claim, saves the rotated credential before using it and treats a lost acknowledgement as uncertain. Never retry an uncertain refresh or infer provider-side revocation from a local disconnect.

createCodexChatTransport({ credential, model, onAttempt, fetchImpl? }) implements the existing chat contract for one system message and one text user message. Choose an explicit ID from codexTextModels(); catalog presence is not proof of account entitlement. A required abort signal, one SSE attempt, no tools, no hidden retry and no account/API-key fallback bound the execution. The credential and model are pinned when the transport is created.

onAttempt runs at the SDK's final fetch boundary. It receives decompressed JSON, its SHA-256 and the actual wire-body SHA-256 (identity or zstd encoding), without headers or credentials. This body may contain private work: retain it only through the product's authorized evidence store, with its redaction/retention rules. A callback failure prevents dispatch; the observer cannot modify the transmitted request. Observation is an attempted submission, not proof the provider read it.

The transport bounds request and response bytes and requires a completed stream. It does not claim a hard output-token cap: the Codex subscription protocol does not send max_output_tokens, even when the generic contract has maxTokens. Products must expose bounded calls/deadlines and the user's actual account quota. Reported usage has no invented API cost or free-credit promise. No ChatGPT web memory, image/video entitlement, account sharing or automatic quota rotation is provided. Current evidence is real installed SDK plus synthetic HTTP, not a real user login or paid model test.

Native Codex tool stream (server only)

Account usage observations

readCodexAccountStatus({ credential, signal }) on ./openai-codex reads the native Codex account usage endpoint. The credential must come from the host's existing explicit login and private custody; no local auth file, browser session, environment key or second account is discovered. It makes no inference request.

The result separates account fingerprint, optional plan, observation timestamp, account windows and additional feature-specific windows. Missing percentages stay missing; an unavailable reading is not zero or unlimited usage. The stable fingerprint is a display discriminator, not the account email or authentication. Display and routing policy belong to the product. A current quota observation never proves that a previous request did not execute and never authorizes replay.

Protocol reference: the native Codex backend client. No vendor source or third-party credential importer is incorporated.

Native tool transport

createCodexModelStream lives on ./openai-codex-stream. It accepts an explicit credential, a model ID, private request-retention callback and optional test fetch. Its stream(context, { signal }) accepts Pi's native context and returns Pi's native event stream; a successful final toolUse message may contain structured tool calls. A subsequent invocation can include the associated toolResult history. It does not convert text or arbitrary JSON into commands.

Version 0.13.1 adds optional reasoningEffort at factory creation: low, medium, high, or xhigh. Omission preserves low for existing callers. The value is validated and frozen with the model; changing the input object later cannot change an in-flight request. The host owns the choice and its usage budget. The SDK's exact retained request includes the actual reasoning setting; this is not a second model, a promised token limit, or automatic model/effort fallback.

The host supplies registered tools, deadlines, scope authorization, budget and durable execution. Incremental tool events are previews, not permission to execute. Failed streams finish with an empty, sanitized error message: never execute or replay a partial tool result. Numeric usage placeholders on failed responses mean usage is unknown, not that the attempt was free.

The source freezes credentials, model, context and cancellation signal per request; limits context to 128 messages, 32 uniquely named tools and 120,000 UTF-8 bytes; and shares the recorded, one-attempt, fixed-destination request boundary with the legacy text adapter. The existing ./openai-codex API does not gain tools implicitly. Other providers have not acquired tool-stream support through this addition. Synthetic tests and standalone Node bundles are not proof of live account entitlement, hosted-use permission or completed product integration.

Grok native tools and media are separate connections

./xai-model-stream exports createXaiModelStream and xaiTextModels. Supply the existing server-owned PiOAuthCredential, a catalogued model and an awaited onAttempt callback. A stream accepts Pi's native Context, including tools and prior tool results; it never executes those tools. Only a successful final native message may reach the host's capability checks. The host must retain its original request/dispatch fence and must not resend an unknown result. This path uses the Grok model's native reasoning default, without labelling it Codex high.

./xai-media exports createXaiMediaTransport, XAI_IMAGE_MODEL, XAI_VIDEO_MODEL and XaiMediaError. Its factory requires an explicit media API key or the explicit { account: PiOAuthCredential } alternative described below. generateImage submits one URL-output image; submitVideo returns only after retainSubmission has saved the original request ID; queryVideo performs one GET of that ID and never generates or retries a job. The same host must retain each POST through onAttempt before dispatch. A failed retention after dispatch is unknown; do not submit again. Invalid input and pre-dispatch refusal are distinguished from an unknown submit or failed GET.

Callers must pass reviewed image resolution/quality and video duration/resolution/ audio explicitly. A small engineering sample can use one 1k/low image and a 3-second 480p silent video; these parameters do not authorize payment or promise a fixed price. Image references must already be owned and approved by the host. Returned URLs are temporary observations, not verified files: use the host's existing guarded-download, byte/hash/MIME, storage and explicit-adoption paths. No media bytes, user credentials, arbitrary local files or public relay are discovered by either module. Tests use the real SDK with synthetic HTTP only.

Protocol references: subscription text integration, images and asynchronous videos. Authorization for text does not establish access or billing for these media APIs.

Explicit xAI media authorization

createXaiMediaTransport on ./xai-media accepts exactly one of { apiKey } or { account }, where account is a server-owned PiOAuthCredential with provider: 'xai'. Pi still owns OAuth login; the host stores and refreshes that same account before constructing the transport. No browser token, CLI auth-file lookup, agent loop, credential refresh loop or alternate paid key is introduced.

Both authorization modes use the existing image/video wire protocol and mandatory request/result retention. The account and expiry are frozen at construction; expiry or rejection does not authorize trying another account or API key. For an existing video, construct a transport from the refreshed original account and query the retained request ID; never submit another video to recover one.

Protocol evidence is xAI's Grok Build source at f0e3be1100ef5252488e3be8bb0e91cf68d8c305, especially api_key_provider.rs and the corresponding implementations/grok_build/media_bearer.rs. This is protocol research, not copied donor implementation. Its native path explicitly accepts xAI API keys or xAI OAuth tokens and refuses foreign-session fallback.

The xAI usage explanation describes a shared subscription allowance and optional extra usage credits. OAuth does not prove remaining included usage or disable already-enabled provider-side billing. Products must show the actual account/funding source and keep live generation closed until its entitlement and spending boundary are verified. Neither a zero platform-points quote nor a synthetic HTTP test proves zero supplier cost.

Verification commands

pnpm typecheck, pnpm test, pnpm build, or pnpm verify.

Origins

The first extraction came from behavior already present in TuringPact and Show. The product-specific migration examples above retain that integration context; they are not a fresh portfolio inventory or proof those consumers have upgraded.

doc/LearningFrom0/ is a historical beginner tutorial. It is not the public API SSOT.