# `@zephyrcloudio/harness-llm`

Portable Effect Schema and Stream contracts for one provider model attempt.

The package owns canonical instructions and messages, model-visible tool descriptors, per-request
generation and provider options, assistant-message-bound private continuation artifacts, exact
attempt usage with optional provider billing provenance, ordered model events, typed failures, and
the scoped `Model` service.
Provider adapters lower these values to their own wire formats.

It does not execute tools, retry requests, mutate history, persist data, run the model/tool
continuation loop, or decide Turn outcomes. The scripted model is a deterministic test provider and
uses only Effect synchronization primitives.

Production Turns use this contract through the Core-owned continuation loop. Provider-specific wire
values stay inside their native provider adapters.

Stateful providers use `openModelRequestChain` to allocate one scoped chain per Turn. The chain is
reused for retries and model/tool rounds, while request-chain state stays off the reusable service.

`ModelService.stream` **MUST return its declared `Stream` synchronously**. It must not be `async` or
return a `Promise`, thenable, or `Proxy` wrapper. Put asynchronous provider work inside the returned
Stream. Malformed async returns are unsupported trusted-code behavior. Ordinary Promise rejection
observation is best effort and is not a security boundary. Portable JavaScript cannot guarantee
observation for Proxy-hidden Promises or Promises with hostile `Symbol.species` behavior whose
`constructor` cannot be reversibly shadowed. A typed Turn outcome is not guaranteed. Under Node's
strict unhandled-rejection behavior, the process may terminate and the raw rejection reason may
appear in process diagnostics.

## Native provider authoring contract

`defineProvider` defines a native LLM provider module. A native provider module exports its
`defineProvider(...)` value as a named export (default exports are not used):

- `definitionId`: stable lowercase branded string matching `/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/`.
- `version`: author-owned version string.
- `optionsSchema`: Effect Schema codec for provider options.
- `credentials`: optional Effect Schema codec for private persisted credentials (e.g. API keys).
- `requestOptionsSchema`: Effect Schema codec for per-request options.
- `filterModel`: optional deterministic capability/protocol filter for catalog models.
- `correctModel`: optional narrow corrections for catalog models (upstream ID, limits, etc.).
- `acquireModel`: scoped synchronous Effect returning the `ModelService` for execution. All callbacks must be synchronous (no `async` / `Promise`): `acquireModel` returns its `Effect`, `filterModel` a `boolean`, `correctModel` the corrected metadata.
  Core decodes `options` once per setup with the definition's schema, so
  `acquireModel` receives already-validated options. `credentials` resolve live
  at each acquisition (stored/configured/env) and arrive as opaque `Redacted`
  values — validate emptiness defensively, as the compatible provider does. It constructs
  the `ModelService` directly instead of building a throwaway `Layer` per Turn.
  The acquisition input also carries the exact upstream model id, optional
  per-model API root, and selected wire `protocol` (`chat`, `responses`, or
  `messages`). Providers with a fixed protocol may ignore the protocol; a
  multi-protocol provider dispatches on it instead of hardcoding model lists.

## Declarative compatible providers

`makeCompatibleProviderDefinition` builds one native provider definition from validated data. It
uses Ze's Chat Completions, Responses, and Messages adapters directly; no AI SDK sits between Ze and
the upstream API. The definition can configure routes, bearer/header/no authentication,
protocol-specific authentication and headers, static headers, Session-derived headers, and typed
failure classification. Core combines that transport with catalog-selected model protocols,
endpoints, upstream IDs, and body defaults.

Chat and Responses authenticate with bearer tokens by default. Messages uses `x-api-key` by default.
An explicit authentication rule overrides these defaults. Unsupported protocols are rejected by the
schema rather than routed to a fallback wire.

## Provider base URLs

The protocol adapters share one base URL contract (`providers/base-url.ts`). A
configured base URL must be an absolute `http(s)` URL without credentials, query,
or fragment, including bare `?`/`#` delimiters. Query and fragment are rejected
rather than stripped or preserved: silently rewriting the configured root would
change proxy routing without telling the operator, and preserving them would
forward ambient URL material into every provider request. Endpoint paths are
joined from the validated parsed pathname, so trailing slashes never produce
double slashes or a missing path segment.

## Provider request deadlines

The protocol adapters share one deadline helper (`providers/timeout.ts`).
`requestTimeoutMs` bounds the whole HTTP request as total elapsed time, not
each idle interval. Omitted or null means unlimited. Progress never resets the
deadline: request setup, error-body reads, and every response-body pull are
bounded by the time remaining since the request started. Expiration fails the
attempt with `ModelTimeoutFailure` (`timeoutKind: 'attempt-limit'`) after the
adapter's normal body cleanup aborts the host request.

## Wire parser bounds

The protocol adapters share one size bound (`providers/limits.ts`): a single
SSE event and a single HTTP error body are each capped at 1 MiB. Overflow
fails the attempt with a typed malformed failure, so one pathological payload
cannot exhaust host memory before a typed failure is produced.

## Credential boundary threat model

The provider already holds the API key by design (it authenticates every
request). The boundary keeps the key out of the opaque values a provider hands
back, which a proxy or gateway that reflects the `Authorization` header could
echo: response IDs and their continuation reference, the provider finish
reason, and failure evidence (provider codes, request IDs, header evidence).
A value that contains the credential is dropped, not rewritten.

Model output (text, reasoning, tool input, and continuation payloads) is not
rewritten. As in OpenCode and Pi, keep secrets out of what the model can read
instead: route calls through a gateway that holds the provider key, and carry
credentials as `Redacted` values to the request header.

Non-goals: the boundary is not a sandbox (adapters are trusted code), and it
does not defend against a provider that misuses the key it already holds.

## Images in tool results

`ToolResultValue` accepts `{ type: 'content', content: [...] }` alongside text and JSON.
Each content part is text, JSON, or an image using the same URL/base64 references as
user images. The durable equivalent is `CanonicalToolResult` with `format: 'content'`
and the parts in `value`.

Responses and the Codex Responses deployment send these parts inside `function_call_output.output`.
Anthropic Messages nests them in `tool_result.content`. Chat Completions accepts only text in tool replies,
so its adapter sends text and JSON in the tool reply and adds labeled images in a user
message after the contiguous tool replies. Images retain their tool call IDs in those
labels; canonical history keeps them attached to the original tool result.


## Public TypeScript exports

- `AssistantContentPart`
- `AssistantMessage`
- `builtinCredentialTransports`
- `builtinLoginModule`
- `canonicalizeProviderFailure`
- `CompatibleModelDiscovery`
- `CompatibleProviderAuthentication`
- `CompatibleProviderConfig`
- `CompatibleProviderConfigValue`
- `CompatibleProviderCredentials`
- `CompatibleProviderCredentialsValue`
- `CompatibleProviderFailureRules`
- `CompatibleProviderOptions`
- `CompatibleProviderOptionsValue`
- `CompatibleProviderProtocolAuthentication`
- `CompatibleProviderProtocolHeaders`
- `compatibleProviderRequiresCredential`
- `CompatibleProviderRoutes`
- `CompletedContentPart`
- `CredentialTransport`
- `CredentialTransportBinding`
- `credentialTransportRegistry`
- `CredentialTransportRegistry`
- `defineProvider`
- `DeviceLoginDescriptor`
- `discoverCompatibleModels`
- `discoverDeviceLogin`
- `DiscoveredRemoteModel`
- `FinishReason`
- `GenerationSettings`
- `InvalidProviderDefinitionError`
- `joinProviderEndpoint`
- `LegacyProviderLogin`
- `makeCodexLogin`
- `makeCompatibleProviderDefinition`
- `makeModelLayer`
- `makeModelTurnContext`
- `makeOAuth2DeviceLogin`
- `makeScriptedModelLayer`
- `MalformedProviderDataFailure`
- `MalformedProviderResponseFailure`
- `MalformedProviderStreamFailure`
- `Model`
- `ModelAttemptUsage`
- `ModelAuthenticationFailure`
- `ModelCallIdentity`
- `ModelContextOverflowFailure`
- `ModelEvent`
- `ModelFailure`
- `ModelImplementation`
- `ModelIncompleteResponseFailure`
- `ModelInstruction`
- `ModelInstructions`
- `ModelInvalidRequestFailure`
- `ModelMessage`
- `ModelMessages`
- `ModelPermissionFailure`
- `ModelProtocol`
- `modelProviderId`
- `ModelRateLimitFailure`
- `ModelRequest`
- `ModelRequestChain`
- `ModelRequestChainImplementation`
- `ModelRequestState`
- `ModelRetryDirective`
- `ModelService`
- `ModelStreamTerminationFailure`
- `ModelTimeoutFailure`
- `ModelToolCallPart`
- `ModelToolChoice`
- `ModelToolDescriptor`
- `ModelTransportFailure`
- `ModelTurnContext`
- `modelTurnContextCall`
- `modelTurnContextSessionId`
- `ModelUnavailableFailure`
- `ModelUnsupportedCapabilityFailure`
- `NoProviderOptions`
- `normalizeProviderFailure`
- `OAuth2RefreshFailure`
- `openModelRequestChain`
- `PrivateContinuationArtifact`
- `PrivateContinuationArtifacts`
- `PrivateMessageContinuation`
- `PrivateMessageContinuations`
- `ProviderBilledUnit`
- `ProviderCost`
- `ProviderCredentialSource`
- `ProviderDefinition`
- `ProviderFailureInput`
- `ProviderLoginAttempt`
- `ProviderLoginError`
- `ProviderLoginMethodImplementation`
- `ProviderLoginModule`
- `ProviderModelAcquisitionInput`
- `ProviderModelCorrection`
- `ProviderModelDiscoveryError`
- `ProviderModelFilter`
- `ProviderRequestOptions`
- `ProviderResponseId`
- `ProviderUsageProvenance`
- `ReasoningBoundaryPart`
- `ReasoningPart`
- `refreshOAuth2Credential`
- `RefusalPart`
- `RunnerCredential`
- `ScriptedModel`
- `ScriptedModelControl`
- `ScriptedModelRequest`
- `ScriptedModelScript`
- `ScriptedModelStep`
- `staticRunnerCredential`
- `TextPart`
- `ToolJsonSchema`
- `ToolMessage`
- `ToolResultPart`
- `ToolResultValue`
- `upgradeLegacyCodexCredential`
- `UserContentPart`
- `UserMessage`
- `validateModelEventStream`

---

Generated from [`packages/llm/README.md`](https://github.com/ZephyrCloudIO/ze-harness/blob/main/packages/llm/README.md).
