@xlaunch/llm 0.2.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Harness error base with a stable machine-routable code and chained cause.
3
+ * Package errors extend it so tool results and replay can retain failure class.
4
+ * @module @xlaunch/llm/error
5
+ */
6
+ /**
7
+ * Base class for all harness errors. Carries a `code` (stable, programmatic —
8
+ * e.g. `NO_ADAPTER`, `INVALID_ARGS`, `INVARIANT`) distinct from the
9
+ * human-readable `message`, and supports `cause` chaining via the standard
10
+ * `ErrorOptions`. `name` defaults to the subclass constructor name.
11
+ */
12
+ export declare class HarnessError extends Error {
13
+ /** Stable machine-routable failure class (e.g. `RATE_LIMIT`); route on this, never by parsing `message`. */
14
+ readonly code: string;
15
+ constructor(message: string, code: string, options?: ErrorOptions);
16
+ }
17
+ /** Canonical provider-neutral code for a model request rejected because its context window was exceeded. */
18
+ export declare const CONTEXT_WINDOW_EXCEEDED_CODE = "CONTEXT_WINDOW_EXCEEDED";
19
+ /** Canonical provider-neutral code for an exhausted account quota or balance. */
20
+ export declare const QUOTA_EXCEEDED_CODE = "QUOTA";
21
+ /**
22
+ * Canonical provider-neutral code for a response that completed normally but
23
+ * carried no content blocks at all. Providers occasionally emit a degenerate
24
+ * completion (a terminal stop with zero output); adapters classify it as this
25
+ * failure instead of yielding an empty assistant message, because an empty
26
+ * message silently ends the turn with nothing for the user or the loop to act
27
+ * on. The attempt produced nothing durable, so retry policy treats it as safe
28
+ * to repeat.
29
+ */
30
+ export declare const EMPTY_RESPONSE_CODE = "EMPTY_RESPONSE";
31
+ /**
32
+ * Canonical provider-neutral code for a credential that was supplied but
33
+ * cannot be used — malformed rather than absent. Distinct from
34
+ * `MISSING_CREDENTIAL` because the fix differs: correct the stored value
35
+ * rather than supply one. Deliberately outside the default retryable set —
36
+ * a malformed credential fails identically on every attempt.
37
+ */
38
+ export declare const INVALID_CREDENTIAL_CODE = "INVALID_CREDENTIAL";
39
+ /**
40
+ * Recognize the context-overflow wording used by OpenAI-compatible providers
41
+ * and library adapters. Adapters pass all available provider code, type, and
42
+ * message text so both thrown and in-band delivery styles share one classifier.
43
+ * @param detail - provider error code/type/message text joined into one string.
44
+ * @returns true when the detail identifies a request exceeding the model context window.
45
+ */
46
+ export declare function isContextWindowExceededError(detail: string): boolean;
47
+ /**
48
+ * Recognize provider wording that identifies an exhausted account quota rather
49
+ * than a transient request-rate limit.
50
+ * @param detail - provider error code/type/message text joined into one string.
51
+ * @returns true only for terminal quota, balance, credit, budget, or usage-limit wording.
52
+ */
53
+ export declare function isQuotaExceededError(detail: string): boolean;
54
+ /**
55
+ * Render a thrown value with its full `cause` chain and AggregateError
56
+ * members, so transport wrappers like undici's `TypeError: fetch failed`
57
+ * surface the underlying failure instead of masking it. Plain structured
58
+ * failures render their own data-backed `message`. Diagnostic-surface
59
+ * rendering only (messages, notices, logs) — never parse the result; route on
60
+ * {@link HarnessError.code}.
61
+ * @param value - the caught value (`unknown` in catch clauses).
62
+ * @returns the outermost message first, each cause appended with `: ` (skipped
63
+ * when it repeats the wrapper message verbatim), and AggregateError members
64
+ * bracketed and `; `-joined.
65
+ */
66
+ export declare function errorChain(value: unknown): string;
67
+ /**
68
+ * Narrow an arbitrary thrown value to a HarnessError (for `instanceof` at runtime boundaries).
69
+ * @param value - the caught value (`unknown` in catch clauses).
70
+ * @returns true only for real instances; duck-typed or cross-realm errors do not narrow.
71
+ */
72
+ export declare function isHarnessError(value: unknown): value is HarnessError;
73
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Harness error base with a stable machine-routable code and chained cause.
3
+ * Package errors extend it so tool results and replay can retain failure class.
4
+ * @module @xlaunch/llm/error
5
+ */
6
+ /**
7
+ * Base class for all harness errors. Carries a `code` (stable, programmatic —
8
+ * e.g. `NO_ADAPTER`, `INVALID_ARGS`, `INVARIANT`) distinct from the
9
+ * human-readable `message`, and supports `cause` chaining via the standard
10
+ * `ErrorOptions`. `name` defaults to the subclass constructor name.
11
+ */
12
+ export class HarnessError extends Error {
13
+ /** Stable machine-routable failure class (e.g. `RATE_LIMIT`); route on this, never by parsing `message`. */
14
+ code;
15
+ constructor(message, code, options) {
16
+ super(message, options);
17
+ this.code = code;
18
+ this.name = new.target.name;
19
+ }
20
+ }
21
+ /** Canonical provider-neutral code for a model request rejected because its context window was exceeded. */
22
+ export const CONTEXT_WINDOW_EXCEEDED_CODE = 'CONTEXT_WINDOW_EXCEEDED';
23
+ /** Canonical provider-neutral code for an exhausted account quota or balance. */
24
+ export const QUOTA_EXCEEDED_CODE = 'QUOTA';
25
+ /**
26
+ * Canonical provider-neutral code for a response that completed normally but
27
+ * carried no content blocks at all. Providers occasionally emit a degenerate
28
+ * completion (a terminal stop with zero output); adapters classify it as this
29
+ * failure instead of yielding an empty assistant message, because an empty
30
+ * message silently ends the turn with nothing for the user or the loop to act
31
+ * on. The attempt produced nothing durable, so retry policy treats it as safe
32
+ * to repeat.
33
+ */
34
+ export const EMPTY_RESPONSE_CODE = 'EMPTY_RESPONSE';
35
+ /**
36
+ * Canonical provider-neutral code for a credential that was supplied but
37
+ * cannot be used — malformed rather than absent. Distinct from
38
+ * `MISSING_CREDENTIAL` because the fix differs: correct the stored value
39
+ * rather than supply one. Deliberately outside the default retryable set —
40
+ * a malformed credential fails identically on every attempt.
41
+ */
42
+ export const INVALID_CREDENTIAL_CODE = 'INVALID_CREDENTIAL';
43
+ /** Structured codes and plain phrases that explicitly name a context bound being exceeded. */
44
+ const STRUCTURED_CONTEXT_OVERFLOW = new RegExp(String.raw `(?:^|[^a-z0-9])context[\s_-](?:length|window)[\s_-]`
45
+ + String.raw `(?:exceed(?:ed|s)?|overflow(?:ed)?|limit[\s_-]exceeded)(?:$|[^a-z0-9])`, 'i');
46
+ /** Request-size wording that ties "too large" directly to model context capacity. */
47
+ const TOO_LARGE_FOR_CONTEXT = new RegExp(String.raw `\b(?:request|prompt|input|messages?)\s+(?:is\s+|are\s+)?`
48
+ + String.raw `too\s+(?:large|long)\s+for\s+(?:(?:this|the)\s+)?`
49
+ + String.raw `(?:model(?:'s)?\s+)?context(?:\s+window)?\b`, 'i');
50
+ /** "Exceeds" wording is safe only when its object is explicitly the model context. */
51
+ const EXCEEDS_MODEL_CONTEXT = new RegExp(String.raw `\b(?:input|prompt|request|messages?)\b.{0,40}`
52
+ + String.raw `\b(?:exceed(?:s|ed)?|overflows?|is\s+larger\s+than)\b.{0,40}`
53
+ + String.raw `\b(?:the\s+)?(?:model(?:'s)?\s+)?context(?:\s+(?:length|window))?\b`, 'i');
54
+ /**
55
+ * Recognize the context-overflow wording used by OpenAI-compatible providers
56
+ * and library adapters. Adapters pass all available provider code, type, and
57
+ * message text so both thrown and in-band delivery styles share one classifier.
58
+ * @param detail - provider error code/type/message text joined into one string.
59
+ * @returns true when the detail identifies a request exceeding the model context window.
60
+ */
61
+ export function isContextWindowExceededError(detail) {
62
+ return STRUCTURED_CONTEXT_OVERFLOW.test(detail)
63
+ || /\b(?:maximum|max)(?:\s+(?:allowed|supported))?\s+context\s+(?:length|window)\b/i.test(detail)
64
+ || TOO_LARGE_FOR_CONTEXT.test(detail)
65
+ || /\b(?:input|prompt|request)\s+(?:is\s+)?too\s+(?:long|large)\s+for\s+(?:this|the)\s+model\b/i.test(detail)
66
+ || EXCEEDS_MODEL_CONTEXT.test(detail);
67
+ }
68
+ /**
69
+ * Recognize provider wording that identifies an exhausted account quota rather
70
+ * than a transient request-rate limit.
71
+ * @param detail - provider error code/type/message text joined into one string.
72
+ * @returns true only for terminal quota, balance, credit, budget, or usage-limit wording.
73
+ */
74
+ export function isQuotaExceededError(detail) {
75
+ return /\binsufficient[\s_-]+(?:quota|balance|credits?)\b/i.test(detail)
76
+ || /\b(?:quota|usage[\s_-]+limit)[\s_-]+(?:exceeded|exhausted|reached)\b/i.test(detail)
77
+ || /\bexceed(?:ed|s)?[\s_-]+(?:(?:your|the)[\s_-]+)?(?:current[\s_-]+)?quota\b/i.test(detail)
78
+ || /\b(?:balance|credits?)[\s_-]+(?:exhausted|depleted)\b/i.test(detail)
79
+ || /\bout[\s_-]+of[\s_-]+(?:credits?|budget)\b/i.test(detail);
80
+ }
81
+ /**
82
+ * Render a thrown value with its full `cause` chain and AggregateError
83
+ * members, so transport wrappers like undici's `TypeError: fetch failed`
84
+ * surface the underlying failure instead of masking it. Plain structured
85
+ * failures render their own data-backed `message`. Diagnostic-surface
86
+ * rendering only (messages, notices, logs) — never parse the result; route on
87
+ * {@link HarnessError.code}.
88
+ * @param value - the caught value (`unknown` in catch clauses).
89
+ * @returns the outermost message first, each cause appended with `: ` (skipped
90
+ * when it repeats the wrapper message verbatim), and AggregateError members
91
+ * bracketed and `; `-joined.
92
+ */
93
+ export function errorChain(value) {
94
+ // Tracks the active recursion path (entries removed on exit), so only true
95
+ // cycles are flagged and a diamond-shared cause still renders in full.
96
+ const path = new Set();
97
+ const render = (current) => {
98
+ if (path.has(current))
99
+ return '<circular cause>';
100
+ path.add(current);
101
+ try {
102
+ if (!(current instanceof Error)) {
103
+ if (typeof current === 'object' && current !== null) {
104
+ const descriptor = Object.getOwnPropertyDescriptor(current, 'message');
105
+ if (descriptor !== undefined && 'value' in descriptor && typeof descriptor.value === 'string') {
106
+ return descriptor.value;
107
+ }
108
+ }
109
+ return String(current);
110
+ }
111
+ const message = current.message === '' ? current.name : current.message;
112
+ const members = current instanceof AggregateError && current.errors.length > 0
113
+ ? ` [${current.errors.map(render).join('; ')}]`
114
+ : '';
115
+ const causeText = current.cause === undefined || current.cause === null
116
+ ? ''
117
+ : render(current.cause);
118
+ // Wrappers like `new HarnessError(String(value), code, { cause: value })`
119
+ // repeat their cause verbatim; rendering it again would only add noise.
120
+ const cause = causeText === '' || causeText === message ? '' : `: ${causeText}`;
121
+ return `${message}${members}${cause}`;
122
+ }
123
+ catch {
124
+ // Only hostile coercion or hostile accessors (a throwing toString /
125
+ // Symbol.toPrimitive on a non-Error, or a throwing message/name/cause/
126
+ // errors getter on an Error subclass): this renderer feeds UI notices
127
+ // and logs, so nothing may escape. Inner frames catch their own throws,
128
+ // so only the hostile node collapses, not the whole chain.
129
+ return '<unrenderable value>';
130
+ }
131
+ finally {
132
+ path.delete(current);
133
+ }
134
+ };
135
+ return render(value);
136
+ }
137
+ /**
138
+ * Narrow an arbitrary thrown value to a HarnessError (for `instanceof` at runtime boundaries).
139
+ * @param value - the caught value (`unknown` in catch clauses).
140
+ * @returns true only for real instances; duck-typed or cross-realm errors do not narrow.
141
+ */
142
+ export function isHarnessError(value) {
143
+ return value instanceof HarnessError;
144
+ }
145
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1,408 @@
1
+ /**
2
+ * LLM service: adapter registry with a waterfall-interceptable streaming call
3
+ * API. Exports the `LlmRuntime` default, the abstract `LlmAdapter` for
4
+ * provider backends, and `BlockAssembler` for chunk assembly.
5
+ *
6
+ * @module @xlaunch/llm
7
+ */
8
+ import { Context } from '@xlaunch/cordis';
9
+ import { TypertRemoteService } from '@xlaunch/typert-protocol';
10
+ import type { GenerateOptions, LlmConfigurableProvider, LlmDiscoveredModel, LlmFailure, LlmImageRequestPricing, LlmModelContext, LlmModelDiscoveryRequest, LlmModelInfo, LlmResolvedModelInfo, LlmProviderInfo, ModelModality, StreamChunk } from './types.ts';
11
+ import type { ResolvedRetryPolicy } from './retry-policy.ts';
12
+ import type { ProviderRequestId } from './brand.ts';
13
+ import type { LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
14
+ import { HarnessError } from './error.ts';
15
+ import type { FileAttachmentRef } from '@xlaunch/attachment';
16
+ export * from './attribution.ts';
17
+ export * from './brand.ts';
18
+ export * from './error.ts';
19
+ export * from './api-key.ts';
20
+ export * from './types.ts';
21
+ export * from './content.ts';
22
+ export * from './assistant-stream.ts';
23
+ export * from './message.ts';
24
+ export * from './retry-policy.ts';
25
+ export { BlockAssembler } from './assembler.ts';
26
+ export { callConfigEquals, isAgentLoopRequest, markAgentLoopRequest } from './call-config.ts';
27
+ export type { LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
28
+ declare module '@xlaunch/cordis' {
29
+ interface Context {
30
+ llm: LlmRuntime;
31
+ }
32
+ interface Events {
33
+ /**
34
+ * Waterfall around every streaming model call (retry, replay, routing).
35
+ * Bound to the {@link LlmRuntime}; call `next()` to reach the resolved
36
+ * adapter's stream, or yield your own chunks to short-circuit.
37
+ * @param options - the full request. A LOOP-built request carries the
38
+ * process-local {@link markAgentLoopRequest} identity and arrives deep-frozen
39
+ * (mutation throws): its content is a pure function of the session log (the
40
+ * reconstructability Agent Note), so listeners read it, never rewrite it.
41
+ * Hand-built calls do not carry that marker; their messages already obey
42
+ * the immutable creation contract.
43
+ * @mode waterfall
44
+ */
45
+ 'llm/stream'(this: LlmRuntime, options: GenerateOptions, next: () => AsyncIterable<StreamChunk>): AsyncIterable<StreamChunk>;
46
+ }
47
+ }
48
+ /** Structured provider facts and cause accepted by {@link LlmError}. */
49
+ export interface LlmErrorOptions extends ErrorOptions {
50
+ /** Valid HTTP status observed at the provider boundary. */
51
+ status?: number;
52
+ /** Positive finite provider-requested delay in milliseconds. */
53
+ providerRetryAfterMs?: number;
54
+ /** Non-empty opaque provider request id. */
55
+ requestId?: ProviderRequestId;
56
+ }
57
+ /**
58
+ * Typed error for LLM-related failures. Extends {@link HarnessError}, so the
59
+ * `code` string (e.g. `AUTH`, `RATE_LIMIT`, `NO_ADAPTER`) is shared taxonomy.
60
+ */
61
+ export declare class LlmError extends HarnessError {
62
+ /** Serializable facts retained beside this live Error. */
63
+ readonly failure: LlmFailure;
64
+ /**
65
+ * @param message - non-empty human-readable failure summary.
66
+ * @param code - non-empty stable provider-neutral machine code.
67
+ * @param options - optional cause and validated serializable provider facts.
68
+ */
69
+ constructor(message: string, code: string, options?: LlmErrorOptions);
70
+ }
71
+ /**
72
+ * Accept one supplied credential, or refuse it as unusable.
73
+ *
74
+ * A stored key arrives from the credentials seam, a `.env` line, or a shell
75
+ * export, all of which pick up surrounding whitespace, so trimming is silent.
76
+ * Anything else fails here rather than inside `fetch`, whose ByteString
77
+ * refusal names a UTF-16 code point instead of the setting to change. The key
78
+ * never enters the message: `ref` names where to fix it, and echoing any part
79
+ * of a secret into a log or a UI is the failure this diagnosis avoids.
80
+ *
81
+ * Lives beside {@link LlmError} rather than in `./api-key.ts` so the predicate
82
+ * module stays dependency-free; every adapter shares this one diagnosis instead
83
+ * of keeping near-identical local copies.
84
+ * @param raw - the credential exactly as supplied.
85
+ * @param pkg - the refusing package name, prefixed to the diagnostic.
86
+ * @param ref - the credential reference the value resolved through.
87
+ * @returns the trimmed, usable key.
88
+ */
89
+ export declare function assertUsableApiKey(raw: string, pkg: string, ref: string): string;
90
+ /** One model call whose config and adapter registration were resolved together. */
91
+ export interface PreparedLlmCall {
92
+ /** Detached, deep-frozen config with any adapter-owned default materialized. */
93
+ readonly config: LlmCallConfig;
94
+ /** Immutable retry policy captured with the adapter registration. */
95
+ readonly retryPolicy: ResolvedRetryPolicy;
96
+ /** Detached context metadata resolved with the registration-bound call. */
97
+ readonly context?: LlmModelContext;
98
+ /** Exact model modalities captured with the adapter dispatch generation. */
99
+ readonly inputModalities?: readonly ModelModality[];
100
+ /** Config fields materialized by the captured adapter rather than proposed by the caller. */
101
+ readonly adapterDefaults: LlmCallConfigAdapterDefaults;
102
+ /**
103
+ * Dispatch this call once through the registration captured during
104
+ * preparation. The request's call-config fields must match {@link config};
105
+ * reuse or mismatch fails with `INVALID_PREPARED_CALL`.
106
+ * @param options - fully assembled request carrying the prepared config.
107
+ * @returns the chunk stream, including the `llm/stream` waterfall.
108
+ */
109
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
110
+ }
111
+ /** One adapter-owned model-resolution generation bound to its eventual stream call. */
112
+ export interface PreparedAdapterCall {
113
+ /** Exact model metadata from the same adapter generation as {@link stream}. */
114
+ readonly model: LlmResolvedModelInfo;
115
+ /** Dispatch through that generation without re-reading dynamic connection facts. */
116
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
117
+ }
118
+ /**
119
+ * Provider-wire adapter for the harness message and stream vocabulary. Register implementations
120
+ * with `ctx.llm.registerAdapter(providers, adapter)`. Every provider HTTP request must include
121
+ * `attributionHeaders()`; prove the headers are added in the wire request or library header hook. The
122
+ * library-backed gateway adapter meets this contract through gateway's header hook.
123
+ */
124
+ export declare abstract class LlmAdapter {
125
+ /**
126
+ * Describe one provider route owned by this adapter.
127
+ * @param provider - a route passed to `registerAdapter()` for this instance.
128
+ * @returns detached display metadata whose id must equal `provider`.
129
+ */
130
+ providerInfo(provider: string): LlmProviderInfo;
131
+ /**
132
+ * Return the provider-owned retry policy captured with this route.
133
+ * @param _provider - a route passed to `registerAdapter()` for this instance.
134
+ * @returns a resolved policy, or `undefined` to use the normal defaults.
135
+ */
136
+ providerRetryPolicy(_provider: string): ResolvedRetryPolicy | undefined;
137
+ /**
138
+ * Resolve provider-side request-image pricing for one exact model route.
139
+ * The default declares none, so consumers fall back to their own neutral
140
+ * estimate. Implementations must answer synchronously without I/O; the
141
+ * token meter resolves this per measurement.
142
+ * @param _provider - a route passed to `registerAdapter()` for this instance.
143
+ * @param _model - exact model id passed to {@link GenerateOptions.model}.
144
+ * @returns route-owned image pricing, or `undefined` when the route declares none.
145
+ */
146
+ imageRequestPricing(_provider: string, _model: string): LlmImageRequestPricing | undefined;
147
+ /**
148
+ * List models this adapter can currently advertise for one owned provider.
149
+ * The result is advisory: an adapter may accept unlisted model ids, and
150
+ * consumers must not turn absence into request rejection.
151
+ * @param _provider - one provider route owned by this adapter.
152
+ * @returns discoverable models in adapter-preferred order.
153
+ */
154
+ listModels(_provider: string): Promise<readonly LlmModelInfo[]>;
155
+ /**
156
+ * Resolve all metadata available for one exact model. This query is
157
+ * independent of the advisory catalog and does not validate request routing.
158
+ * @param provider - one provider route owned by this adapter.
159
+ * @param model - exact model id passed to {@link GenerateOptions.model}.
160
+ * @param _signal - cancellation for this exact-model lookup; asynchronous
161
+ * implementations must settle promptly after it aborts.
162
+ * @returns provider/model identity plus any context, call-default, and reasoning metadata.
163
+ */
164
+ resolveModel(provider: string, model: string, _signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
165
+ /**
166
+ * Bind exact model metadata and the eventual request dispatch to one adapter generation.
167
+ * Dynamic adapters override this so settings changes between preparation and
168
+ * dispatch cannot combine one generation's capabilities with another's endpoint.
169
+ * @param provider - registered provider route.
170
+ * @param model - exact model id.
171
+ * @param signal - cancellation for model resolution.
172
+ * @returns model metadata and a one-generation stream entry point.
173
+ */
174
+ prepareCall(provider: string, model: string, signal?: AbortSignal): Promise<PreparedAdapterCall>;
175
+ /**
176
+ * Stream one model call as raw chunks. The only required method.
177
+ * @param options - the fully-assembled request; implementations must honor `options.signal`.
178
+ * @returns the chunk stream, obeying the adapter contract documented on `StreamChunk`.
179
+ */
180
+ abstract stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
181
+ }
182
+ /**
183
+ * What {@link LlmRuntime.registerAdapter} returns: the disposer, plus an
184
+ * atomic route replacement for the same adapter instance.
185
+ */
186
+ export interface AdapterRegistrationHandle {
187
+ /** Release every route this registration currently holds. */
188
+ (): void;
189
+ /**
190
+ * Replace this registration's routes with `providers`, keeping the same
191
+ * adapter instance. The candidate set is validated in full first — a
192
+ * conflict with another adapter, an invalid name, or bad provider metadata
193
+ * throws and leaves the current routes untouched — and the swap itself is
194
+ * one synchronous section, so no request can observe a gap. An empty array
195
+ * is legal here (a settings section that emptied holds zero routes while
196
+ * staying registered), unlike an empty initial registration.
197
+ *
198
+ * Throws `LlmError` with code `REGISTRATION_DISPOSED` once the registration
199
+ * has been released: its routes are gone and its disposer has already run,
200
+ * so anything registered afterwards would have no owner left to release it.
201
+ * @param providers - the complete next route set for this registration.
202
+ */
203
+ replace(providers: string[]): void;
204
+ }
205
+ /**
206
+ * A live configurable-provider registration, disposable and atomically
207
+ * replaceable — the directory counterpart of {@link AdapterRegistrationHandle}.
208
+ */
209
+ export interface DirectoryRegistrationHandle {
210
+ /** Withdraw every entry this registration currently holds. */
211
+ (): void;
212
+ /**
213
+ * Replace this registration's entries with `entries`. The candidate set is
214
+ * validated in full first — an entry another registration already declares,
215
+ * a duplicate within the set, or invalid metadata throws and leaves the
216
+ * current entries untouched — and the swap is one synchronous section, so no
217
+ * reader observes a gap. An empty array is legal here, unlike an empty
218
+ * initial registration.
219
+ *
220
+ * Throws `LlmError` with code `REGISTRATION_DISPOSED` once the registration
221
+ * has been disposed.
222
+ */
223
+ replace(entries: readonly LlmConfigurableProvider[]): void;
224
+ }
225
+ /**
226
+ * The abstract `llm` service: an adapter registry plus a streaming model-call
227
+ * API, interceptable via the `llm/stream` waterfall.
228
+ */
229
+ export declare class LlmRuntime extends TypertRemoteService {
230
+ private adapters;
231
+ private directory;
232
+ private discoveries;
233
+ constructor(ctx: Context);
234
+ /** Notify topology observers without letting one broken listener veto the commit. */
235
+ private emitAdaptersUpdated;
236
+ /** Contained-listener diagnostic shared by the sync and async failure paths. */
237
+ private warnAdaptersListenerFailure;
238
+ /**
239
+ * Register an adapter for the given provider routes. Throws `LlmError` with code
240
+ * `DUPLICATE_ADAPTER` if any provider already has an adapter (all-or-nothing).
241
+ * Disposed with the fiber.
242
+ * @param providers - every provider route this adapter should serve.
243
+ * @param adapter - the adapter that streams calls for those providers.
244
+ * @returns the disposer, carrying {@link AdapterRegistrationHandle.replace}.
245
+ */
246
+ registerAdapter(providers: string[], adapter: LlmAdapter): AdapterRegistrationHandle;
247
+ /**
248
+ * Validate one candidate route set for `adapter`, treating routes this
249
+ * registration already holds as available. Nothing is mutated: a rejected
250
+ * candidate leaves the registry exactly as it was.
251
+ */
252
+ private prepareRoutes;
253
+ /**
254
+ * Swap this registration's routes for the prepared ones in one synchronous
255
+ * section, so no observer can see the registry between the release and the
256
+ * re-registration. The route set's one mutation point is also where
257
+ * `llm/adapters-updated` is published, so a `replace` announces itself
258
+ * exactly like a first registration.
259
+ */
260
+ private commitRoutes;
261
+ /**
262
+ * Describe provider routes with a registered adapter.
263
+ * @returns detached provider metadata in registration order.
264
+ */
265
+ listProviders(): LlmProviderInfo[];
266
+ /**
267
+ * Declare provider routes an adapter plugin can activate through
268
+ * configuration. Registration is all-or-nothing: an empty list, invalid
269
+ * entry, or a provider already declared by any registration throws
270
+ * `LlmError` without registering the rest. Disposed with the fiber.
271
+ * @param entries - every configurable provider this plugin owns.
272
+ * @returns a handle that withdraws all of them, and can atomically replace them.
273
+ */
274
+ registerConfigurableProviders(entries: readonly LlmConfigurableProvider[]): DirectoryRegistrationHandle;
275
+ /**
276
+ * List every declared configurable provider, registered or dormant.
277
+ * @returns detached directory entries in declaration order.
278
+ */
279
+ listConfigurableProviders(): LlmConfigurableProvider[];
280
+ /**
281
+ * Offer to interrogate provider endpoints on behalf of the settings
282
+ * namespace this plugin owns. The namespace is the key because that is what
283
+ * a configuration surface already holds from the configurable-provider
284
+ * directory, and because a provider being *added* has no route to name yet.
285
+ * Disposed with the fiber.
286
+ * @param settingsNs - the namespace whose profiles this discovery serves.
287
+ * @param discover - interrogates one endpoint and must honor the supplied signal.
288
+ * @returns the disposer that withdraws the offer.
289
+ */
290
+ registerModelDiscovery(settingsNs: string, discover: (request: LlmModelDiscoveryRequest, signal?: AbortSignal) => Promise<readonly LlmDiscoveredModel[]>): () => void;
291
+ /**
292
+ * Interrogate one provider endpoint for the models it advertises. The
293
+ * request describes a draft, not a stored route, so nothing here reads or
294
+ * writes settings or credentials — the caller owns both, and the reply is
295
+ * candidate metadata a surface may offer for adoption.
296
+ * @param settingsNs - namespace whose registered discovery serves this draft.
297
+ * @param request - the endpoint, protocol, and one-shot credential to use.
298
+ * @param signal - caller cancellation.
299
+ * @returns the advertised models, deduplicated in endpoint order.
300
+ */
301
+ discoverModels(settingsNs: string, request: LlmModelDiscoveryRequest, signal?: AbortSignal): Promise<LlmDiscoveredModel[]>;
302
+ /**
303
+ * Remote adapter for one draft provider interrogation.
304
+ * @param settingsNs - namespace whose registered discovery serves this draft.
305
+ * @param request - endpoint, protocol, and one-shot credential to use.
306
+ * @param signal - caller cancellation supplied by the Remote carrier.
307
+ * @returns advertised models in endpoint order.
308
+ * @throws RemoteError with `llm/model-discovery-rejected` when discovery refuses or fails.
309
+ */
310
+ remoteDiscoverModels(settingsNs: string, request: LlmModelDiscoveryRequest, signal: AbortSignal): Promise<LlmDiscoveredModel[]>;
311
+ /**
312
+ * Resolve the retry policy captured when one provider route was registered.
313
+ * @param provider - registered provider route to inspect.
314
+ * @returns the provider-owned policy, with normal defaults already resolved.
315
+ */
316
+ providerRetryPolicy(provider: string): ResolvedRetryPolicy;
317
+ /**
318
+ * Resolve provider-side request-image pricing for one exact route, or
319
+ * `undefined` when the provider is unregistered or declares none. Unknown
320
+ * providers degrade to `undefined` rather than throwing because callers
321
+ * price durable history whose route may no longer be mounted.
322
+ * @param provider - provider route named by a request header.
323
+ * @param model - exact model id named by the same header.
324
+ * @returns the owning adapter's image pricing for the route, when declared.
325
+ */
326
+ imageRequestPricing(provider: string, model: string): LlmImageRequestPricing | undefined;
327
+ /**
328
+ * Resolve the exact text one durable file occurrence contributes to every
329
+ * provider request in the current execution environment.
330
+ * @param ref - durable verbatim file reference from model history.
331
+ * @returns the same deterministic handle text used at adapter dispatch.
332
+ */
333
+ fileRequestText(ref: FileAttachmentRef): string;
334
+ /** Detach typed adapter-owned modality metadata. */
335
+ private detachedModalities;
336
+ /**
337
+ * Discover models advertised by one registered provider. Catalog membership
338
+ * is advisory and never changes routing or request validation.
339
+ * @param provider - registered provider route to inspect.
340
+ * @returns detached model metadata in adapter-preferred order.
341
+ */
342
+ listModels(provider: string): Promise<LlmModelInfo[]>;
343
+ /**
344
+ * Resolve and validate all metadata from the adapter that owns one exact
345
+ * route. The result is detached from adapter-owned objects; catalog
346
+ * membership remains advisory and does not control request routing.
347
+ * @param provider - registered provider route to inspect.
348
+ * @param model - exact model id passed to the adapter.
349
+ * @param signal - optional cancellation for adapter-owned asynchronous lookup.
350
+ * @returns exact model identity plus available context and reasoning metadata.
351
+ */
352
+ resolveModelInfo(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
353
+ private resolveModelInfoFor;
354
+ /** Validate and detach one adapter-returned exact model result. */
355
+ private normalizeModelInfo;
356
+ /**
357
+ * Validate a conversation call config against its exact model capability and
358
+ * materialize adapter-configured defaults. Unsupported explicit efforts
359
+ * reject before provider I/O; no clamping or aliasing is performed. This
360
+ * standalone query does not bind a later dispatch; use {@link prepareCall}
361
+ * when logging and streaming must share one adapter registration.
362
+ * @param config - provider/model route and optional request controls.
363
+ * @param signal - optional cancellation for adapter-owned capability lookup.
364
+ * @returns a detached config only when a default must be materialized.
365
+ */
366
+ resolveCallConfig(config: LlmCallConfig, signal?: AbortSignal): Promise<LlmCallConfig>;
367
+ private resolveCallFor;
368
+ /** Validate request controls against one already-bound exact model result. */
369
+ private resolveCallWithInfo;
370
+ /**
371
+ * Resolve one call under its current adapter registration. The returned
372
+ * one-shot handle keeps that registration across header logging and dispatch,
373
+ * so HMR cannot combine one adapter's capability result with another adapter.
374
+ * @param config - provider/model route and optional request controls.
375
+ * @param signal - optional cancellation for adapter-owned capability lookup.
376
+ * @returns a prepared config and its registration-bound stream entry point.
377
+ */
378
+ prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<PreparedLlmCall>;
379
+ private registration;
380
+ /** Remove replay state whose historical route is owned by another adapter. */
381
+ private forAdapter;
382
+ /**
383
+ * Resolve the current execution-world read path of one durable file
384
+ * reference through the mounted attachment and filesystem providers.
385
+ */
386
+ private fileReadPath;
387
+ /**
388
+ * Final adapter boundary. Adapter selection, dispatch, iterator construction,
389
+ * and iteration failures become one terminal failure chunk. Middleware and
390
+ * downstream consumer failures remain thrown plugin or consumer errors.
391
+ */
392
+ private adapterStream;
393
+ /**
394
+ * Stream one model call as raw chunks (token-level deltas). Replay state is
395
+ * retained only when the same adapter instance owns its historical provider
396
+ * and the target provider. Final adapter selection remains fixed through
397
+ * asynchronous exact-model resolution and dispatch. Adapter selection,
398
+ * dispatch, and iteration failures become terminal `error` or `aborted`
399
+ * finish chunks; middleware, nested-call, cleanup, and consumer failures
400
+ * remain thrown.
401
+ * @param options - the full request; `options.provider` selects the adapter.
402
+ * @returns the chunk stream, possibly wrapped by `llm/stream` listeners.
403
+ */
404
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
405
+ private streamWithRegistration;
406
+ }
407
+ export default LlmRuntime;
408
+ //# sourceMappingURL=index.d.ts.map