@cleocode/contracts 2026.5.66 → 2026.5.68

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,585 @@
1
+ /**
2
+ * Session and executor interface contracts for the unified LLM provider architecture.
3
+ *
4
+ * Introduces the two missing abstraction layers that wire Phase 3 utility
5
+ * modules into the live execution path:
6
+ *
7
+ * - {@link LlmSession} — stateful per-conversation layer (history, OAuth refresh,
8
+ * rate-limit guard, credential pool rotation, retry policy).
9
+ * - {@link LlmExecutor} — agent-loop layer (tool-call orchestration, multi-turn
10
+ * replay, context compression, usage aggregation).
11
+ *
12
+ * Supporting types: {@link ExecutionRequest}, {@link ExecutionEvent},
13
+ * {@link AggregatedUsage}, {@link ToolCall}, {@link RetryPolicy},
14
+ * {@link SendOptions}, {@link TransportContext}, {@link NormalizedDelta}.
15
+ *
16
+ * Factory interfaces: {@link LlmSessionFactory}, {@link LlmExecutorFactory}.
17
+ *
18
+ * @module llm/interfaces
19
+ * @task T9281
20
+ * @epic T9261 T-LLM-CRED-CENTRALIZATION
21
+ * @see ADR-072 §Decision
22
+ */
23
+
24
+ import type { ClassifiedError } from './failover-reason.js';
25
+ import type {
26
+ LlmTransport,
27
+ NormalizedResponse,
28
+ NormalizedUsage,
29
+ TransportMessage,
30
+ TransportRequest,
31
+ TransportTool,
32
+ } from './normalized-response.js';
33
+ import type { ProviderId } from './provider-id.js';
34
+
35
+ // Re-export LlmTransport so consumers of this module can reference it without
36
+ // an additional import from normalized-response.js.
37
+ export type { LlmTransport };
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Streaming delta
41
+ // ---------------------------------------------------------------------------
42
+
43
+ /**
44
+ * A single streaming chunk emitted by {@link LlmTransport.stream}.
45
+ *
46
+ * `text` carries the incremental visible content. `reasoning` carries
47
+ * incremental reasoning/thinking output (Anthropic extended-thinking,
48
+ * OpenAI o1-series). Either field may be an empty string when the delta
49
+ * carries content for the other field only.
50
+ *
51
+ * `toolCallDelta` is populated for streaming tool-call argument deltas —
52
+ * consumers must accumulate these to reconstruct the full JSON arguments string.
53
+ */
54
+ export interface NormalizedDelta {
55
+ /** Incremental visible text content. Empty string when delta is tool-call-only. */
56
+ readonly text: string;
57
+ /** Incremental reasoning/thinking output. Empty string when not present. */
58
+ readonly reasoning: string;
59
+ /** Partial tool-call argument delta, if this chunk is part of a tool call. */
60
+ readonly toolCallDelta?: {
61
+ /** Index of the tool call being streamed (for multi-tool-call responses). */
62
+ readonly index: number;
63
+ /** Tool name (only present on the first delta for this index). */
64
+ readonly name?: string;
65
+ /** Partial arguments JSON fragment. */
66
+ readonly argumentsChunk: string;
67
+ };
68
+ /** Stop reason, populated on the final delta only. null for non-final deltas. */
69
+ readonly stopReason: string | null;
70
+ /** Usage accounting, populated on the final delta only. null for non-final deltas. */
71
+ readonly usage: NormalizedUsage | null;
72
+ }
73
+
74
+ // ---------------------------------------------------------------------------
75
+ // Transport execution context
76
+ // ---------------------------------------------------------------------------
77
+
78
+ /**
79
+ * Contextual metadata supplied to {@link LlmTransport.complete} and
80
+ * {@link LlmTransport.stream} alongside the request payload.
81
+ *
82
+ * Transports use this to set per-request metadata (request IDs, abort signals,
83
+ * feature flags) without polluting {@link TransportRequest}.
84
+ */
85
+ export interface TransportContext {
86
+ /**
87
+ * Unique identifier for this transport call.
88
+ * Typically matches the parent session or executor call ID.
89
+ */
90
+ readonly requestId: string;
91
+ /** AbortSignal for request cancellation. */
92
+ readonly signal?: AbortSignal;
93
+ /**
94
+ * Whether to enable extended thinking / reasoning mode.
95
+ * Only honoured by providers that support it (Anthropic, OpenAI o-series).
96
+ */
97
+ readonly enableThinking?: boolean;
98
+ /**
99
+ * Per-provider feature flags surfaced from {@link SendOptions}.
100
+ * Transports MUST ignore keys they do not recognise.
101
+ */
102
+ readonly providerFlags?: Readonly<Record<string, unknown>>;
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // Retry policy
107
+ // ---------------------------------------------------------------------------
108
+
109
+ /**
110
+ * Exponential-backoff retry policy for {@link LlmSession.send}.
111
+ *
112
+ * Backoff delay for attempt `n` (0-indexed) is computed as:
113
+ * `min(baseDelayMs * 2^n + jitter, maxDelayMs)`
114
+ * where `jitter` is a random value in `[0, baseDelayMs * 0.1]` when
115
+ * {@link jitter} is `true`.
116
+ */
117
+ export interface RetryPolicy {
118
+ /** Maximum number of send attempts (1 = no retry). */
119
+ readonly maxAttempts: number;
120
+ /** Base delay in milliseconds before the first retry. */
121
+ readonly baseDelayMs: number;
122
+ /** Maximum delay cap in milliseconds. */
123
+ readonly maxDelayMs: number;
124
+ /** Whether to add random jitter to prevent thundering-herd retries. */
125
+ readonly jitter: boolean;
126
+ }
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Send options
130
+ // ---------------------------------------------------------------------------
131
+
132
+ /**
133
+ * Per-call options passed to {@link LlmSession.send} and
134
+ * {@link LlmSession.stream}.
135
+ *
136
+ * All fields are optional. Unset fields fall back to the session defaults
137
+ * established at construction time.
138
+ */
139
+ export interface SendOptions {
140
+ /**
141
+ * Prompt-cache breakpoint injection strategy.
142
+ *
143
+ * - `'system_and_3'` — inject cache breakpoints after the system prompt and
144
+ * after the last 3 user messages (Anthropic extended-caching strategy).
145
+ * - `'prefix_and_2'` — inject at the shared prefix boundary and after 2
146
+ * most-recent turns (optimised for Gemini cached content).
147
+ * - `null` — no cache injection (default when not set).
148
+ */
149
+ readonly cacheStrategy?: 'system_and_3' | 'prefix_and_2' | null;
150
+ /**
151
+ * Override the retry policy for this specific call.
152
+ * When omitted, the session-level {@link RetryPolicy} applies.
153
+ */
154
+ readonly retryPolicy?: RetryPolicy;
155
+ /**
156
+ * Additional system prompt text appended to the session default system prompt.
157
+ * Useful for injecting per-call context (e.g. tool result summaries).
158
+ */
159
+ readonly systemSuffix?: string;
160
+ /**
161
+ * Whether to enable extended thinking / reasoning for this call.
162
+ * Defaults to false. Only effective on providers that support it.
163
+ */
164
+ readonly enableThinking?: boolean;
165
+ /**
166
+ * Per-provider feature flags passed through to {@link TransportContext.providerFlags}.
167
+ * Transports MUST ignore flags they do not recognise.
168
+ */
169
+ readonly providerFlags?: Readonly<Record<string, unknown>>;
170
+ }
171
+
172
+ // ---------------------------------------------------------------------------
173
+ // Session interface
174
+ // ---------------------------------------------------------------------------
175
+
176
+ /**
177
+ * Stateful per-conversation LLM session.
178
+ *
179
+ * Owns: conversation history, OAuth refresh (pre-call check against
180
+ * `ResolvedCredential.expiresAt < 60s`), `RateLimitGuard` pre-check,
181
+ * `CredentialPool` rotation on retriable errors (driven by
182
+ * `classifyError(err).shouldRotateCredential`), and retry policy with
183
+ * exponential backoff.
184
+ *
185
+ * Sessions are one-per-conversation; NOT shared across parallel agents.
186
+ *
187
+ * @see ADR-072 §Decision §"LlmSession — session level"
188
+ */
189
+ export interface LlmSession {
190
+ /** Underlying wire-level transport used by this session. */
191
+ readonly transport: LlmTransport;
192
+ /** Model identifier this session is bound to. */
193
+ readonly model: string;
194
+ /**
195
+ * Returns a snapshot of the current conversation history.
196
+ * The returned array is read-only — mutate via {@link append} or
197
+ * {@link truncateHistory}.
198
+ */
199
+ history(): readonly TransportMessage[];
200
+ /**
201
+ * Appends a message to the conversation history.
202
+ *
203
+ * @param message - The message to append.
204
+ */
205
+ append(message: TransportMessage): void;
206
+ /**
207
+ * Truncates the conversation history, keeping `keepFirst` messages from
208
+ * the start and `keepLast` messages from the end.
209
+ *
210
+ * Messages between the two preserved windows are dropped. If
211
+ * `keepFirst + keepLast >= history.length`, the history is left unchanged.
212
+ *
213
+ * @param keepFirst - Number of messages to preserve from the start.
214
+ * @param keepLast - Number of messages to preserve from the end.
215
+ */
216
+ truncateHistory(keepFirst: number, keepLast: number): void;
217
+ /**
218
+ * Executes a single completion call with the supplied messages.
219
+ *
220
+ * Handles pre-call OAuth refresh, rate-limit guard check, cache-breakpoint
221
+ * injection per `opts.cacheStrategy`, retry with exponential backoff, and
222
+ * credential pool rotation on retriable errors.
223
+ *
224
+ * @param messages - Messages to send (NOT appended to history automatically).
225
+ * @param opts - Optional per-call overrides.
226
+ * @returns A promise resolving to the normalized provider response.
227
+ */
228
+ send(messages: TransportMessage[], opts?: SendOptions): Promise<NormalizedResponse>;
229
+ /**
230
+ * Streaming variant of {@link send}.
231
+ *
232
+ * Yields {@link NormalizedDelta} chunks as they arrive. The final delta
233
+ * carries {@link NormalizedDelta.stopReason} and {@link NormalizedDelta.usage}.
234
+ *
235
+ * @param messages - Messages to send.
236
+ * @param opts - Optional per-call overrides.
237
+ * @returns An async iterable of delta chunks.
238
+ */
239
+ stream(messages: TransportMessage[], opts?: SendOptions): AsyncIterable<NormalizedDelta>;
240
+ /**
241
+ * Refreshes the OAuth credential bound to this session.
242
+ *
243
+ * Called automatically by {@link send} / {@link stream} when
244
+ * `ResolvedCredential.expiresAt` is less than 60 seconds in the future.
245
+ * May also be called manually by callers that track token expiry independently.
246
+ */
247
+ refreshCredential(): Promise<void>;
248
+ }
249
+
250
+ // ---------------------------------------------------------------------------
251
+ // Executor types
252
+ // ---------------------------------------------------------------------------
253
+
254
+ /**
255
+ * A resolved tool call produced by the model within an executor run.
256
+ *
257
+ * Distinct from {@link NormalizedToolCall} (which is the wire-level shape).
258
+ * `ToolCall` carries the parsed `args` object so executor consumers do not
259
+ * need to JSON-parse the arguments string themselves.
260
+ */
261
+ export interface ToolCall {
262
+ /** Provider-assigned tool-call id (may be null if provider does not surface one). */
263
+ readonly id: string | null;
264
+ /** Tool name as declared in the {@link ExecutionRequest}. */
265
+ readonly name: string;
266
+ /** Parsed arguments object (JSON-parsed from the provider arguments string). */
267
+ readonly args: Record<string, unknown>;
268
+ /** Raw arguments string from the provider (pre-parse). */
269
+ readonly rawArguments: string;
270
+ }
271
+
272
+ /**
273
+ * Usage totals aggregated across all iterations of an executor run.
274
+ *
275
+ * `costUsd` is computed via `computeCost` from `@cleocode/core` using the
276
+ * `usage-pricing` snapshot. It may be `null` when the model is not present
277
+ * in the pricing table.
278
+ */
279
+ export interface AggregatedUsage {
280
+ /** Total input tokens consumed across all iterations. */
281
+ readonly totalInputTokens: number;
282
+ /** Total output tokens generated across all iterations. */
283
+ readonly totalOutputTokens: number;
284
+ /** Total tokens served from prompt cache across all iterations. */
285
+ readonly totalCachedTokens: number;
286
+ /** Number of LLM API calls made during the run. */
287
+ readonly iterations: number;
288
+ /**
289
+ * Estimated total cost in USD. null when the model is absent from the
290
+ * pricing table or pricing data is unavailable.
291
+ */
292
+ readonly costUsd: number | null;
293
+ }
294
+
295
+ /**
296
+ * Discriminated-union event emitted by {@link LlmExecutor.run}.
297
+ *
298
+ * Consumers iterate the async generator and switch on `kind` to handle
299
+ * each event type.
300
+ *
301
+ * Six variants:
302
+ * - `response` — model emitted a text response (no tool call).
303
+ * - `tool_call` — model requested a tool call; executor is about to invoke it.
304
+ * - `tool_result` — tool handler returned a result; executor will continue the loop.
305
+ * - `context_compressed` — context engine compressed history to fit the context window.
306
+ * - `done` — run completed; aggregated usage is available.
307
+ * - `error` — an unrecoverable error terminated the run.
308
+ */
309
+ export type ExecutionEvent =
310
+ | {
311
+ /** Model emitted a text response (no tool call). */
312
+ readonly kind: 'response';
313
+ /** The full normalized response from the model. */
314
+ readonly response: NormalizedResponse;
315
+ }
316
+ | {
317
+ /** Model requested a tool call. */
318
+ readonly kind: 'tool_call';
319
+ /** The resolved tool call with parsed arguments. */
320
+ readonly toolCall: ToolCall;
321
+ /** Current iteration index (0-indexed). */
322
+ readonly iteration: number;
323
+ }
324
+ | {
325
+ /** Tool handler returned a result; executor will feed it back to the model. */
326
+ readonly kind: 'tool_result';
327
+ /** Id of the tool call this result resolves. */
328
+ readonly toolCallId: string | null;
329
+ /** Tool name. */
330
+ readonly toolName: string;
331
+ /** Raw result as a string (JSON-serialized if the handler returned an object). */
332
+ readonly result: string;
333
+ /** Current iteration index (0-indexed). */
334
+ readonly iteration: number;
335
+ }
336
+ | {
337
+ /** Context engine compressed history to fit the context window. */
338
+ readonly kind: 'context_compressed';
339
+ /** Token count before compression. */
340
+ readonly tokensBefore: number;
341
+ /** Token count after compression. */
342
+ readonly tokensAfter: number;
343
+ }
344
+ | {
345
+ /** Run completed successfully. */
346
+ readonly kind: 'done';
347
+ /** Aggregated usage across all iterations. */
348
+ readonly usage: AggregatedUsage;
349
+ /** Final normalized response from the last model turn. */
350
+ readonly finalResponse: NormalizedResponse;
351
+ }
352
+ | {
353
+ /** An unrecoverable error terminated the run. */
354
+ readonly kind: 'error';
355
+ /** Structured error classification. */
356
+ readonly error: ClassifiedError;
357
+ /** Iteration index at which the error occurred. */
358
+ readonly iteration: number;
359
+ };
360
+
361
+ /**
362
+ * Parameters for a single {@link LlmExecutor.run} invocation.
363
+ *
364
+ * `tools` is the full set of tools available in this run. The executor
365
+ * routes tool calls to `toolHandler` and feeds results back to the model
366
+ * until the model emits a stop reason other than `'tool_use'` / `'tool_calls'`.
367
+ */
368
+ export interface ExecutionRequest {
369
+ /** Initial messages to send to the model. */
370
+ readonly messages: TransportMessage[];
371
+ /**
372
+ * Tools available in this run.
373
+ * When empty or omitted, tool-call orchestration is skipped.
374
+ */
375
+ readonly tools?: TransportTool[];
376
+ /**
377
+ * Handler invoked for each tool call.
378
+ *
379
+ * Receives the resolved {@link ToolCall} and must return the tool result as
380
+ * a string (or a JSON-serializable value that the executor will stringify).
381
+ * Throwing causes the executor to emit an `error` event and terminate.
382
+ */
383
+ readonly toolHandler?: (call: ToolCall) => Promise<string | Record<string, unknown>>;
384
+ /**
385
+ * Maximum number of tool-call iterations before the executor terminates.
386
+ * Defaults to 10. Set to 1 to allow a single model turn with no tool calls.
387
+ */
388
+ readonly maxIterations?: number;
389
+ /** Per-call send options forwarded to {@link LlmSession.send}. */
390
+ readonly sendOptions?: SendOptions;
391
+ }
392
+
393
+ // ---------------------------------------------------------------------------
394
+ // Context engine
395
+ // ---------------------------------------------------------------------------
396
+
397
+ /**
398
+ * Optional context-compression engine supplied to {@link LlmExecutor}.
399
+ *
400
+ * When present, the executor calls {@link shouldCompress} after each model
401
+ * turn. If it returns `true`, the executor calls {@link compress} to reduce
402
+ * the session history before the next iteration, then emits a
403
+ * `context_compressed` event.
404
+ *
405
+ * When absent (undefined), the executor skips all compression checks silently.
406
+ *
407
+ * @see ADR-072 §"LlmExecutor — agent-loop level"
408
+ */
409
+ export interface ContextEngine {
410
+ /**
411
+ * Returns `true` when the current history warrants compression.
412
+ *
413
+ * @param history - Current read-only conversation history snapshot.
414
+ * @returns Whether compression should be applied before the next model turn.
415
+ */
416
+ shouldCompress(history: readonly TransportMessage[]): boolean;
417
+ /**
418
+ * Compresses the session history in-place (or returns a compressed copy).
419
+ *
420
+ * The executor replaces the session history with the returned messages after
421
+ * calling this method. It also emits a `context_compressed` event carrying
422
+ * the token counts before and after.
423
+ *
424
+ * @param history - Current read-only conversation history snapshot.
425
+ * @returns The compressed message array.
426
+ */
427
+ compress(history: readonly TransportMessage[]): Promise<TransportMessage[]>;
428
+ /**
429
+ * Estimate token count for a history snapshot.
430
+ *
431
+ * Used by the executor to populate {@link ExecutionEvent} `tokensBefore` /
432
+ * `tokensAfter` fields. May be an approximation (e.g. character / 4).
433
+ *
434
+ * @param history - Conversation history snapshot to measure.
435
+ * @returns Estimated token count.
436
+ */
437
+ estimateTokens(history: readonly TransportMessage[]): number;
438
+ }
439
+
440
+ // ---------------------------------------------------------------------------
441
+ // Executor interface
442
+ // ---------------------------------------------------------------------------
443
+
444
+ /**
445
+ * Agent-loop level LLM executor.
446
+ *
447
+ * Owns: tool-call orchestration, multi-turn replay, `ContextEngine`
448
+ * integration (compress when `shouldCompress(currentTokens)` returns true),
449
+ * usage aggregation, and {@link ExecutionEvent} discriminated-union emission.
450
+ *
451
+ * `auxiliary()` is the single-turn no-tool-loop side-channel for context
452
+ * compression, dream cycles, and hygiene scans.
453
+ *
454
+ * @see ADR-072 §Decision §"LlmExecutor — agent-loop level"
455
+ */
456
+ export interface LlmExecutor {
457
+ /** The underlying session used for all LLM calls. */
458
+ readonly session: LlmSession;
459
+ /**
460
+ * Executes the full agent loop for the given request.
461
+ *
462
+ * Yields {@link ExecutionEvent} values as they occur. The generator
463
+ * terminates when the model returns a non-tool-call stop reason, the
464
+ * `maxIterations` limit is reached, or an unrecoverable error occurs.
465
+ *
466
+ * @param request - The execution parameters including messages, tools, and handler.
467
+ * @returns An async generator of execution events.
468
+ */
469
+ run(request: ExecutionRequest): AsyncIterable<ExecutionEvent>;
470
+ /**
471
+ * Executes a single-turn no-tool-loop completion.
472
+ *
473
+ * Intended for auxiliary calls that must NOT enter the tool-call loop:
474
+ * context compression summaries, dream cycles, hygiene scans. Uses the same
475
+ * underlying {@link LlmSession} but bypasses tool registration and iteration
476
+ * tracking.
477
+ *
478
+ * @param messages - Messages to send.
479
+ * @param opts - Optional per-call overrides.
480
+ * @returns A promise resolving to the normalized provider response.
481
+ */
482
+ auxiliary(messages: TransportMessage[], opts?: SendOptions): Promise<NormalizedResponse>;
483
+ }
484
+
485
+ // ---------------------------------------------------------------------------
486
+ // Factory types
487
+ // ---------------------------------------------------------------------------
488
+
489
+ /**
490
+ * Options for constructing an {@link LlmSession} via {@link LlmSessionFactory}.
491
+ */
492
+ export interface SessionFactoryOptions {
493
+ /**
494
+ * Role name used to resolve the provider and model via `resolveLLMForRole`.
495
+ * Mutually exclusive with {@link providerId} + {@link model}.
496
+ */
497
+ readonly role?: string;
498
+ /**
499
+ * Explicit provider id. Must be paired with {@link model}.
500
+ * Mutually exclusive with {@link role}.
501
+ */
502
+ readonly providerId?: ProviderId;
503
+ /**
504
+ * Explicit model identifier. Must be paired with {@link providerId}.
505
+ * Mutually exclusive with {@link role}.
506
+ */
507
+ readonly model?: string;
508
+ /** Retry policy for the session. Falls back to the factory default when omitted. */
509
+ readonly retryPolicy?: RetryPolicy;
510
+ }
511
+
512
+ /**
513
+ * Factory for creating {@link LlmSession} instances.
514
+ *
515
+ * Abstracts the resolution chain:
516
+ * `role → resolveLLMForRole → ProviderRegistry → ResolvedCredential
517
+ * → transportForProvider(profile, credential) → new ConcreteSession(...)`.
518
+ *
519
+ * @see ADR-072 §2.2
520
+ */
521
+ export interface LlmSessionFactory {
522
+ /**
523
+ * Creates a session resolved from the given role name.
524
+ *
525
+ * @param role - CLEO role name (e.g. `'orchestrator'`, `'sentient'`).
526
+ * @returns A promise resolving to an initialized {@link LlmSession}.
527
+ */
528
+ createForRole(role: string): Promise<LlmSession>;
529
+ /**
530
+ * Creates a session with the given options.
531
+ *
532
+ * @param opts - Session construction options.
533
+ * @returns A promise resolving to an initialized {@link LlmSession}.
534
+ */
535
+ create(opts: SessionFactoryOptions): Promise<LlmSession>;
536
+ }
537
+
538
+ /**
539
+ * Options for constructing an {@link LlmExecutor} via {@link LlmExecutorFactory}.
540
+ */
541
+ export interface ExecutorFactoryOptions {
542
+ /**
543
+ * Pre-constructed session to use. When provided, the factory wraps it
544
+ * directly without creating a new session.
545
+ * Mutually exclusive with {@link sessionOptions}.
546
+ */
547
+ readonly session?: LlmSession;
548
+ /**
549
+ * Options forwarded to {@link LlmSessionFactory.create} when no explicit
550
+ * {@link session} is supplied.
551
+ * Mutually exclusive with {@link session}.
552
+ */
553
+ readonly sessionOptions?: SessionFactoryOptions;
554
+ /**
555
+ * Maximum number of tool-call iterations. Defaults to 10.
556
+ * Can be overridden per-run via {@link ExecutionRequest.maxIterations}.
557
+ */
558
+ readonly maxIterations?: number;
559
+ }
560
+
561
+ /**
562
+ * Factory for creating {@link LlmExecutor} instances.
563
+ *
564
+ * Wraps {@link LlmSessionFactory.createForRole} and constructs a
565
+ * `ConcreteExecutor` with the resolved session, tool handler registry, and
566
+ * optional context engine.
567
+ *
568
+ * @see ADR-072 §2.2
569
+ */
570
+ export interface LlmExecutorFactory {
571
+ /**
572
+ * Creates an executor resolved from the given role name.
573
+ *
574
+ * @param role - CLEO role name (e.g. `'orchestrator'`, `'sentient'`).
575
+ * @returns A promise resolving to an initialized {@link LlmExecutor}.
576
+ */
577
+ createForRole(role: string): Promise<LlmExecutor>;
578
+ /**
579
+ * Creates an executor with the given options.
580
+ *
581
+ * @param opts - Executor construction options.
582
+ * @returns A promise resolving to an initialized {@link LlmExecutor}.
583
+ */
584
+ create(opts: ExecutorFactoryOptions): Promise<LlmExecutor>;
585
+ }