agent-accelerator 0.5.8 → 0.6.0
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.
- package/README.md +26 -5
- package/examples/prompts/SYSTEM_PROMPT.md +2 -0
- package/examples/prompts/SYSTEM_PROMPT_AGENT.md +4 -0
- package/examples/prompts/SYSTEM_PROMPT_TOOLS.md +1 -0
- package/package.json +4 -1
- package/src/agent/agent.ts +374 -167
- package/src/agent/architecture.md +14 -12
- package/src/agent/config-blocks.ts +39 -12
- package/src/agent/context.ts +8 -3
- package/src/agent/delegation.ts +82 -29
- package/src/agent/loop.ts +449 -373
- package/src/agent/subagent.ts +1 -3
- package/src/agent/subagents-tool.ts +45 -7
- package/src/agent/subagents.md +3 -0
- package/src/agent/turns.ts +3 -1
- package/src/agent/worker.ts +58 -28
- package/src/agent/workers.ts +113 -20
- package/src/architecture.md +16 -15
- package/src/index.ts +34 -10
- package/src/models/architecture.md +4 -3
- package/src/models/catalog-cache.ts +6 -5
- package/src/models/catalog.ts +46 -49
- package/src/providers/architecture.md +10 -8
- package/src/providers/google.ts +150 -62
- package/src/providers/openai-compat.ts +60 -58
- package/src/providers/openai.ts +57 -39
- package/src/providers/openrouter.ts +87 -32
- package/src/providers/registry.ts +12 -26
- package/src/providers/shared.ts +89 -39
- package/src/providers.ts +56 -115
- package/src/scheduler/architecture.md +9 -8
- package/src/scheduler/scheduled-actions.md +4 -0
- package/src/scheduler/scheduler.ts +186 -60
- package/src/scheduler/store.ts +23 -10
- package/src/scheduler/time.ts +17 -3
- package/src/scheduler/tool.ts +33 -7
- package/src/session/architecture.md +7 -6
- package/src/session/journal.ts +38 -12
- package/src/session/store.ts +122 -61
- package/src/session/usage.ts +58 -9
- package/src/streaming/architecture.md +4 -3
- package/src/streaming/event-stream.ts +26 -12
- package/src/telemetry.ts +6 -2
- package/src/tools/architecture.md +8 -7
- package/src/tools/executor.ts +50 -48
- package/src/tools/schema.ts +10 -6
- package/src/tools/subagent-result.ts +6 -1
- package/src/tools/tool.ts +9 -11
- package/src/types/agent.ts +2 -21
- package/src/types/architecture.md +11 -10
- package/src/types/core.ts +1 -9
- package/src/types/errors.ts +26 -4
- package/src/types/model.ts +0 -4
- package/src/types/tool.ts +12 -2
- package/src/update-models.ts +5 -5
- package/src/utils/architecture.md +16 -15
- package/src/utils/cache.ts +1 -3
- package/src/utils/durable.ts +17 -5
- package/src/utils/env.ts +9 -7
- package/src/utils/errors.ts +50 -44
- package/src/utils/headers.ts +3 -10
- package/src/utils/media.ts +23 -8
- package/src/utils/raw-capture.ts +9 -2
- package/src/utils/retry.ts +17 -31
- package/src/utils/safe-io.ts +29 -7
- package/src/utils/serialization.ts +1 -4
- package/src/utils/session.ts +1 -2
package/src/index.ts
CHANGED
|
@@ -37,7 +37,14 @@ export { zodToJsonSchema, cleanJsonSchema } from "./tools/schema.ts";
|
|
|
37
37
|
*/
|
|
38
38
|
export { executeToolCalls } from "./tools/executor.ts";
|
|
39
39
|
|
|
40
|
-
export {
|
|
40
|
+
export {
|
|
41
|
+
assertPathAllowed,
|
|
42
|
+
enforceFileAccess,
|
|
43
|
+
fetchRemoteBytes,
|
|
44
|
+
isPrivateAddress,
|
|
45
|
+
RemoteFetchError,
|
|
46
|
+
MAX_REMOTE_FETCH_BYTES,
|
|
47
|
+
} from "./utils/safe-io.ts";
|
|
41
48
|
|
|
42
49
|
// Multi-Agent Delegation & Tools
|
|
43
50
|
export {
|
|
@@ -126,12 +133,8 @@ export {
|
|
|
126
133
|
GoogleInteractionsProvider,
|
|
127
134
|
clearInteractionChains,
|
|
128
135
|
} from "./providers/google.ts";
|
|
129
|
-
export {
|
|
130
|
-
|
|
131
|
-
} from "./providers/openrouter.ts";
|
|
132
|
-
export {
|
|
133
|
-
OpenAIResponsesProvider,
|
|
134
|
-
} from "./providers/openai.ts";
|
|
136
|
+
export { OpenRouterChatCompletionsProvider } from "./providers/openrouter.ts";
|
|
137
|
+
export { OpenAIResponsesProvider } from "./providers/openai.ts";
|
|
135
138
|
/**
|
|
136
139
|
* @deprecated Internal provider mappers/routing helpers. Prefer the
|
|
137
140
|
* provider-agnostic registry (`resolveModel`/`getProvider`); direct mapper
|
|
@@ -251,14 +254,36 @@ export {
|
|
|
251
254
|
emptyTotals,
|
|
252
255
|
} from "./session/store.ts";
|
|
253
256
|
export type { UsageReport, UsageLine, UsageTotals, AgentUsage, WorkerUsage, CostSource } from "./session/usage.ts";
|
|
254
|
-
export type {
|
|
257
|
+
export type {
|
|
258
|
+
SessionLimits,
|
|
259
|
+
SessionPayloadStatus,
|
|
260
|
+
PersistedAgentSession,
|
|
261
|
+
SessionTotals,
|
|
262
|
+
SessionAgentLike,
|
|
263
|
+
LoadedSessionDir,
|
|
264
|
+
PersistedSubAgentTrace,
|
|
265
|
+
SessionRunMark,
|
|
266
|
+
SessionRunEnd,
|
|
267
|
+
SessionPending,
|
|
268
|
+
SessionPendingPrompt,
|
|
269
|
+
SessionToolRecord,
|
|
270
|
+
} from "./session/store.ts";
|
|
255
271
|
|
|
256
272
|
// Utilities
|
|
257
273
|
/**
|
|
258
274
|
* @deprecated Session-routing internals. Prefer agent-level `sessionId`;
|
|
259
275
|
* direct use moves to the future `/internal` entry.
|
|
260
276
|
*/
|
|
261
|
-
export {
|
|
277
|
+
export {
|
|
278
|
+
createSessionId,
|
|
279
|
+
hashSessionPart,
|
|
280
|
+
createTrackingId,
|
|
281
|
+
nowMs,
|
|
282
|
+
newRunId,
|
|
283
|
+
newTurnId,
|
|
284
|
+
previewPrompt,
|
|
285
|
+
parseModelRef,
|
|
286
|
+
} from "./utils/session.ts";
|
|
262
287
|
export type { ParsedModelRef } from "./utils/session.ts";
|
|
263
288
|
/**
|
|
264
289
|
* @deprecated Raw env access. Prefer explicit `Agent` config (which wins over
|
|
@@ -344,7 +369,6 @@ export type {
|
|
|
344
369
|
RetryPolicyConfig,
|
|
345
370
|
AgentLogger,
|
|
346
371
|
ToolChoiceOption,
|
|
347
|
-
StopOption,
|
|
348
372
|
Budget,
|
|
349
373
|
BudgetState,
|
|
350
374
|
SubAgentRunIds,
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
The model catalog: context limits, prices, modalities and reasoning options come from models.dev, not from code.
|
|
4
4
|
|
|
5
|
-
| File
|
|
6
|
-
|
|
|
5
|
+
| File | What it is |
|
|
6
|
+
| :----------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
7
7
|
| `catalog-cache.ts` | Downloads and caches `models.dev/api.json` in `<home>/catalog/` (or `AGENT_CACHE_DIR`). Stale-while-revalidate: an expired catalog is served at once and refreshed in the background (8 s timeout, 10 min backoff after a failure); only a cold start waits. `AGENT_CATALOG_REFRESH=off` stops automatic refreshes. |
|
|
8
|
-
| `catalog.ts`
|
|
8
|
+
| `catalog.ts` | Lookups (`getModelFromCatalog`), `registerModel`, thinking validation (`validateModelThinking`, which throws `ThinkingLevelError`, a one-line `ConfigError`), and the exported `GOOGLE_/OPENAI_/OPENROUTER_MODELS` arrays (rebuilt in place when the catalog changes). |
|
|
9
9
|
|
|
10
10
|
## Invariants
|
|
11
|
+
|
|
11
12
|
- Never hand-edit the cache file; register custom models with `registerModel()`. Verified modality fixes go in `MODALITY_OVERRIDES`.
|
|
12
13
|
- The global bare-id fallback applies to first-class providers and explicit bare-id lookups only. A custom prefix never borrows another provider's price or modalities: unknown stays unknown.
|
|
13
14
|
- A payload that is not a catalog (fewer than 3 providers with models) never overwrites the cache.
|
|
@@ -313,7 +313,7 @@ export async function refreshModelCatalog(options: RefreshCatalogOptions = {}):
|
|
|
313
313
|
fallbackToDiskCache();
|
|
314
314
|
return getCatalogStatus();
|
|
315
315
|
}
|
|
316
|
-
} catch
|
|
316
|
+
} catch {
|
|
317
317
|
clearTimeout(timer);
|
|
318
318
|
// If download fails, retain disk cache if available
|
|
319
319
|
fallbackToDiskCache();
|
|
@@ -356,7 +356,6 @@ export async function refreshModelCatalog(options: RefreshCatalogOptions = {}):
|
|
|
356
356
|
return getCatalogStatus();
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
-
|
|
360
359
|
/** True when `AGENT_CATALOG_REFRESH` is `off`/`0`/`false`: never touch the network automatically. */
|
|
361
360
|
function automaticRefreshDisabled(): boolean {
|
|
362
361
|
const v = getEnv("AGENT_CATALOG_REFRESH")?.trim().toLowerCase();
|
|
@@ -388,9 +387,11 @@ export async function ensureModelCatalogFresh(options: RefreshCatalogOptions = {
|
|
|
388
387
|
const start = (): Promise<CatalogStatus> => {
|
|
389
388
|
if (!inFlightRefresh) {
|
|
390
389
|
const timeoutMs = options.timeoutMs ?? (current.loaded ? BACKGROUND_REFRESH_TIMEOUT_MS : undefined);
|
|
391
|
-
inFlightRefresh = refreshModelCatalog({ ...options, ...(timeoutMs !== undefined ? { timeoutMs } : {}) }).finally(
|
|
392
|
-
|
|
393
|
-
|
|
390
|
+
inFlightRefresh = refreshModelCatalog({ ...options, ...(timeoutMs !== undefined ? { timeoutMs } : {}) }).finally(
|
|
391
|
+
() => {
|
|
392
|
+
inFlightRefresh = null;
|
|
393
|
+
}
|
|
394
|
+
);
|
|
394
395
|
}
|
|
395
396
|
return inFlightRefresh;
|
|
396
397
|
};
|
package/src/models/catalog.ts
CHANGED
|
@@ -5,20 +5,10 @@
|
|
|
5
5
|
* rebuilt in place whenever it changes.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
getActiveCatalog,
|
|
10
|
-
registerCatalogUpdateListener,
|
|
11
|
-
refreshModelCatalog,
|
|
12
|
-
ensureModelCatalogFresh,
|
|
13
|
-
getCatalogStatus,
|
|
14
|
-
setCatalogTTL,
|
|
15
|
-
getCatalogTTL,
|
|
16
|
-
DEFAULT_CATALOG_TTL_MS,
|
|
17
|
-
type CatalogStatus,
|
|
18
|
-
type RefreshCatalogOptions,
|
|
19
|
-
} from "./catalog-cache.ts";
|
|
8
|
+
import { getActiveCatalog, registerCatalogUpdateListener } from "./catalog-cache.ts";
|
|
20
9
|
import type { ModelSpec, ProviderId, ModelLimit, ModelCost, ModelModalities } from "../types/model.ts";
|
|
21
10
|
import type { ThinkingLevel } from "../types/core.ts";
|
|
11
|
+
import { ConfigError, ValidationError } from "../types/errors.ts";
|
|
22
12
|
|
|
23
13
|
// ---------------------------------------------------------------------------
|
|
24
14
|
// Provider aliases — maps our internal ids to models.dev keys
|
|
@@ -48,8 +38,8 @@ function applyModalityOverrides(provider: string, modelId: string, base: ModelMo
|
|
|
48
38
|
MODALITY_OVERRIDES[`${provider}/${normalizeModelIdForLookup(modelId)}`];
|
|
49
39
|
if (!hit) return base;
|
|
50
40
|
return {
|
|
51
|
-
input: (
|
|
52
|
-
output: (
|
|
41
|
+
input: (hit.input ?? base.input) as ModelModalities["input"],
|
|
42
|
+
output: (hit.output ?? base.output) as ModelModalities["output"],
|
|
53
43
|
};
|
|
54
44
|
}
|
|
55
45
|
|
|
@@ -106,7 +96,7 @@ function customRegistryKey(provider: string, modelId: string): string {
|
|
|
106
96
|
*/
|
|
107
97
|
export function registerModel(spec: ModelSpec): void {
|
|
108
98
|
if (!spec || typeof spec.id !== "string" || typeof spec.provider !== "string") {
|
|
109
|
-
throw new
|
|
99
|
+
throw new ValidationError("[Agent Accelerator] registerModel requires spec.id and spec.provider strings");
|
|
110
100
|
}
|
|
111
101
|
customModelRegistry.set(customRegistryKey(String(spec.provider), String(spec.id)), { ...spec });
|
|
112
102
|
// Invalidate derived caches so the custom model is visible immediately.
|
|
@@ -135,30 +125,38 @@ export function getModelSource(provider: string, modelId: string): "catalog" | "
|
|
|
135
125
|
if (customModelRegistry.has(customRegistryKey(p, m)) || customModelRegistry.has(customRegistryKey(p, norm))) {
|
|
136
126
|
return "override";
|
|
137
127
|
}
|
|
138
|
-
if (
|
|
139
|
-
MODALITY_OVERRIDES[`${p}/${m}`] !== undefined ||
|
|
140
|
-
MODALITY_OVERRIDES[`${p}/${norm}`] !== undefined
|
|
141
|
-
) {
|
|
128
|
+
if (MODALITY_OVERRIDES[`${p}/${m}`] !== undefined || MODALITY_OVERRIDES[`${p}/${norm}`] !== undefined) {
|
|
142
129
|
return "override";
|
|
143
130
|
}
|
|
144
131
|
return getModelFromCatalog(provider, modelId) ? "catalog" : "unknown";
|
|
145
132
|
}
|
|
146
133
|
|
|
147
134
|
// Derives capability flags (thinking, caching, tools, structured output, modalities) from a raw catalog entry; in: mapModelSpec, out: ModelCapabilities.
|
|
148
|
-
function mapCapabilities(
|
|
135
|
+
function mapCapabilities(
|
|
136
|
+
raw: any,
|
|
137
|
+
cost: ModelCost,
|
|
138
|
+
reasoning: boolean,
|
|
139
|
+
toolCall: boolean,
|
|
140
|
+
modalities: ModelModalities
|
|
141
|
+
) {
|
|
149
142
|
return {
|
|
150
143
|
supportsThinking: reasoning,
|
|
151
144
|
supportsThinkingLevel: !!raw.reasoning_options,
|
|
152
|
-
supportsThinkingBudget:
|
|
145
|
+
supportsThinkingBudget:
|
|
146
|
+
Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "budget_tokens"),
|
|
153
147
|
supportsImplicitCaching: cost.cache_read !== undefined || cost.cache_write !== undefined,
|
|
154
148
|
supportsLongCacheRetention: cost.cache_read !== undefined,
|
|
155
149
|
supportsExplicitCaching: cost.cache_read !== undefined,
|
|
156
150
|
supportsParallelToolCalls: toolCall,
|
|
157
151
|
supportsStreaming: true,
|
|
158
152
|
supportsStructuredOutput: raw.structured_output === undefined ? true : !!raw.structured_output,
|
|
159
|
-
supportsReasoningToggle:
|
|
160
|
-
|
|
161
|
-
|
|
153
|
+
supportsReasoningToggle:
|
|
154
|
+
Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "toggle"),
|
|
155
|
+
supportsReasoningEffort:
|
|
156
|
+
Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "effort"),
|
|
157
|
+
modalities: [...(modalities.input || []), ...(modalities.output || [])].filter(
|
|
158
|
+
(v: any, i: any, a: any) => a.indexOf(v) === i
|
|
159
|
+
),
|
|
162
160
|
};
|
|
163
161
|
}
|
|
164
162
|
|
|
@@ -267,7 +265,9 @@ export function getModelFromCatalog(providerInput: string, modelIdInput: string)
|
|
|
267
265
|
}
|
|
268
266
|
|
|
269
267
|
const aliasProviders = PROVIDER_ALIASES[provider] || [provider];
|
|
270
|
-
const tryProviders = [
|
|
268
|
+
const tryProviders = [
|
|
269
|
+
...new Set([...aliasProviders, provider, provider.replace("-zen", ""), provider.replace("-go", "")]),
|
|
270
|
+
];
|
|
271
271
|
const catalog = getActiveCatalog();
|
|
272
272
|
|
|
273
273
|
for (const p of tryProviders) {
|
|
@@ -291,7 +291,11 @@ export function getModelFromCatalog(providerInput: string, modelIdInput: string)
|
|
|
291
291
|
// or normalized equality. The old loose `modelId.endsWith(key)` is removed
|
|
292
292
|
// (e.g. "gpt-4o-mini-extra" must not match "mini").
|
|
293
293
|
for (const [key, raw] of Object.entries(providerData.models as Record<string, any>)) {
|
|
294
|
-
if (
|
|
294
|
+
if (
|
|
295
|
+
key === modelId ||
|
|
296
|
+
key.endsWith(`/${modelId}`) ||
|
|
297
|
+
normalizeModelIdForLookup(key) === normalizeModelIdForLookup(modelId)
|
|
298
|
+
) {
|
|
295
299
|
result = mapModelSpec(p as ProviderId, key, raw as any);
|
|
296
300
|
setLookupCache(cacheKey, result);
|
|
297
301
|
return result;
|
|
@@ -410,7 +414,8 @@ export function getModelThinkingInfo(provider: string, modelId: string): ModelTh
|
|
|
410
414
|
allowed.add("dynamic");
|
|
411
415
|
|
|
412
416
|
const allowedLevels = Array.from(allowed);
|
|
413
|
-
const supportsDisable =
|
|
417
|
+
const supportsDisable =
|
|
418
|
+
allowed.has("none") || !!toggleOpt || Boolean(budgetOpt && (budgetOpt.min === undefined || budgetOpt.min <= 0));
|
|
414
419
|
|
|
415
420
|
return {
|
|
416
421
|
supportsThinking: true,
|
|
@@ -421,8 +426,8 @@ export function getModelThinkingInfo(provider: string, modelId: string): ModelTh
|
|
|
421
426
|
};
|
|
422
427
|
}
|
|
423
428
|
|
|
424
|
-
/** Thrown when a requested ThinkingLevel is unsupported by the selected model
|
|
425
|
-
export class ThinkingLevelError extends
|
|
429
|
+
/** Thrown when a requested ThinkingLevel is unsupported by the selected model: a `ConfigError` (one line, never retried) with `allowedLevels`. */
|
|
430
|
+
export class ThinkingLevelError extends ConfigError {
|
|
426
431
|
readonly provider: string;
|
|
427
432
|
readonly modelId: string;
|
|
428
433
|
readonly requestedLevel: string;
|
|
@@ -443,31 +448,19 @@ export class ThinkingLevelError extends Error {
|
|
|
443
448
|
reason: string;
|
|
444
449
|
remedy?: string;
|
|
445
450
|
}) {
|
|
446
|
-
const
|
|
447
|
-
?
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
:
|
|
452
|
-
|
|
453
|
-
const formattedMessage =
|
|
454
|
-
`[Agent Accelerator] ThinkingLevel Mismatch for "${opts.provider}/${opts.modelId}":\n` +
|
|
455
|
-
` Requested Level: "${opts.requestedLevel}"\n` +
|
|
456
|
-
` Issue: ${opts.reason}` +
|
|
457
|
-
allowedLine +
|
|
458
|
-
remedyLines;
|
|
459
|
-
|
|
460
|
-
super(formattedMessage);
|
|
451
|
+
const allowed =
|
|
452
|
+
opts.allowedLevels.length > 0 ? ` Allowed levels: ${opts.allowedLevels.map((l) => `"${l}"`).join(", ")}.` : "";
|
|
453
|
+
const remedy = opts.remedy ? ` How to fix: ${opts.remedy}` : "";
|
|
454
|
+
super(
|
|
455
|
+
`[Agent Accelerator] Thinking level "${opts.requestedLevel}" does not fit "${opts.provider}/${opts.modelId}": ${opts.reason}${allowed}${remedy}`,
|
|
456
|
+
{ provider: opts.provider, model: opts.modelId }
|
|
457
|
+
);
|
|
461
458
|
this.name = "ThinkingLevelError";
|
|
462
459
|
this.provider = opts.provider;
|
|
463
460
|
this.modelId = opts.modelId;
|
|
464
461
|
this.requestedLevel = opts.requestedLevel;
|
|
465
462
|
this.allowedLevels = opts.allowedLevels;
|
|
466
463
|
this.supportsThinking = opts.supportsThinking;
|
|
467
|
-
|
|
468
|
-
if (Error.captureStackTrace) {
|
|
469
|
-
Error.captureStackTrace(this, ThinkingLevelError);
|
|
470
|
-
}
|
|
471
464
|
}
|
|
472
465
|
}
|
|
473
466
|
|
|
@@ -476,7 +469,11 @@ export class ThinkingLevelError extends Error {
|
|
|
476
469
|
*
|
|
477
470
|
* @example `validateModelThinking("google", "gemini-3.5-flash-lite", "medium");`
|
|
478
471
|
*/
|
|
479
|
-
export function validateModelThinking(
|
|
472
|
+
export function validateModelThinking(
|
|
473
|
+
provider: string,
|
|
474
|
+
modelId: string,
|
|
475
|
+
requestedLevel?: ThinkingLevel | string
|
|
476
|
+
): void {
|
|
480
477
|
if (!requestedLevel) return;
|
|
481
478
|
const level = String(requestedLevel).toLowerCase().trim();
|
|
482
479
|
const info = getModelThinkingInfo(provider, modelId);
|
|
@@ -2,19 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All HTTP and wire parsing. Canonical mappers (thinking, cache, tool choice, structured output) live in `../providers.ts`, never here.
|
|
4
4
|
|
|
5
|
-
| File
|
|
6
|
-
|
|
|
7
|
-
| `google.ts`
|
|
8
|
-
| `openai.ts`
|
|
9
|
-
| `openrouter.ts`
|
|
10
|
-
| `openai-compat.ts` | OpenAI-compatible Chat Completions | Serves every custom prefix; strict subset of fields; no default base URL.
|
|
11
|
-
| `registry.ts`
|
|
12
|
-
| `shared.ts`
|
|
5
|
+
| File | Wire | Notes |
|
|
6
|
+
| :----------------- | :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
7
|
+
| `google.ts` | Google Interactions | Stateful chaining (TTL gate, self-heal on `not_found`), exact replay of raw `steps`, media consts at the top, Gemini 4 long decode as background interactions. |
|
|
8
|
+
| `openai.ts` | OpenAI Responses | Stateless (`store:false`), full history every turn. |
|
|
9
|
+
| `openrouter.ts` | OpenRouter Chat Completions | Every request streams (so it can be cancelled); `generate()` collects its own stream. |
|
|
10
|
+
| `openai-compat.ts` | OpenAI-compatible Chat Completions | Serves every custom prefix; strict subset of fields; no default base URL. |
|
|
11
|
+
| `registry.ts` | - | Routes `provider/model` to an adapter; the registry is immutable; `ModelProvider` helpers. |
|
|
12
|
+
| `shared.ts` | - | Helpers every adapter uses: signals and timers, error payloads, ids, retry-after, SSE scaffold (`linkStreamAbort`, `withAbortTimer`, `readSseData`, `incompleteStreamCheck`, `failStream`). |
|
|
13
13
|
|
|
14
14
|
## Contract (`Provider`, in `../types/model.ts`)
|
|
15
|
+
|
|
15
16
|
`generate(model, context, options)` returns a `ProviderGenerateResult`; `stream(...)` returns an `AssistantMessageEventStream` that ends with an `AgentResponse`; optional `isLongRunning()` tells the loop to skip its whole-call timeouts.
|
|
16
17
|
|
|
17
18
|
## Invariants
|
|
19
|
+
|
|
18
20
|
- Fix shared streaming behaviour in `shared.ts`, never per adapter. Event parsing and request building stay in each adapter.
|
|
19
21
|
- OpenRouter and compat chat chunk handlers are deliberately not merged.
|
|
20
22
|
- A custom prefix never gets another provider's key or base URL.
|