agent-accelerator 0.5.9 → 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.
Files changed (67) hide show
  1. package/README.md +3 -1
  2. package/examples/prompts/SYSTEM_PROMPT.md +2 -0
  3. package/examples/prompts/SYSTEM_PROMPT_AGENT.md +4 -0
  4. package/examples/prompts/SYSTEM_PROMPT_TOOLS.md +1 -0
  5. package/package.json +4 -1
  6. package/src/agent/agent.ts +366 -161
  7. package/src/agent/architecture.md +13 -12
  8. package/src/agent/config-blocks.ts +39 -12
  9. package/src/agent/context.ts +8 -3
  10. package/src/agent/delegation.ts +82 -29
  11. package/src/agent/loop.ts +444 -365
  12. package/src/agent/subagent.ts +1 -3
  13. package/src/agent/subagents-tool.ts +45 -7
  14. package/src/agent/subagents.md +3 -0
  15. package/src/agent/turns.ts +3 -1
  16. package/src/agent/worker.ts +56 -27
  17. package/src/agent/workers.ts +113 -20
  18. package/src/architecture.md +16 -15
  19. package/src/index.ts +34 -10
  20. package/src/models/architecture.md +4 -3
  21. package/src/models/catalog-cache.ts +6 -5
  22. package/src/models/catalog.ts +39 -30
  23. package/src/providers/architecture.md +10 -8
  24. package/src/providers/google.ts +147 -59
  25. package/src/providers/openai-compat.ts +57 -49
  26. package/src/providers/openai.ts +54 -37
  27. package/src/providers/openrouter.ts +85 -30
  28. package/src/providers/registry.ts +6 -21
  29. package/src/providers/shared.ts +87 -33
  30. package/src/providers.ts +56 -115
  31. package/src/scheduler/architecture.md +7 -6
  32. package/src/scheduler/scheduled-actions.md +4 -0
  33. package/src/scheduler/scheduler.ts +186 -60
  34. package/src/scheduler/store.ts +23 -10
  35. package/src/scheduler/time.ts +17 -3
  36. package/src/scheduler/tool.ts +33 -7
  37. package/src/session/architecture.md +7 -6
  38. package/src/session/journal.ts +38 -12
  39. package/src/session/store.ts +122 -61
  40. package/src/session/usage.ts +58 -9
  41. package/src/streaming/architecture.md +4 -3
  42. package/src/streaming/event-stream.ts +26 -12
  43. package/src/telemetry.ts +6 -2
  44. package/src/tools/architecture.md +8 -7
  45. package/src/tools/executor.ts +50 -48
  46. package/src/tools/schema.ts +10 -6
  47. package/src/tools/subagent-result.ts +6 -1
  48. package/src/tools/tool.ts +9 -11
  49. package/src/types/agent.ts +2 -21
  50. package/src/types/architecture.md +11 -10
  51. package/src/types/core.ts +1 -9
  52. package/src/types/errors.ts +26 -4
  53. package/src/types/model.ts +0 -4
  54. package/src/types/tool.ts +12 -2
  55. package/src/update-models.ts +5 -5
  56. package/src/utils/architecture.md +16 -15
  57. package/src/utils/cache.ts +1 -3
  58. package/src/utils/durable.ts +17 -5
  59. package/src/utils/env.ts +9 -7
  60. package/src/utils/errors.ts +6 -14
  61. package/src/utils/headers.ts +3 -10
  62. package/src/utils/media.ts +23 -8
  63. package/src/utils/raw-capture.ts +9 -2
  64. package/src/utils/retry.ts +15 -3
  65. package/src/utils/safe-io.ts +29 -7
  66. package/src/utils/serialization.ts +1 -4
  67. 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 { assertPathAllowed, enforceFileAccess, fetchRemoteBytes, isPrivateAddress, RemoteFetchError, MAX_REMOTE_FETCH_BYTES } from "./utils/safe-io.ts";
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
- OpenRouterChatCompletionsProvider,
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 { SessionLimits, SessionPayloadStatus, PersistedAgentSession, SessionTotals, SessionAgentLike, LoadedSessionDir, PersistedSubAgentTrace, SessionRunMark, SessionRunEnd, SessionPending, SessionPendingPrompt, SessionToolRecord } from "./session/store.ts";
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 { createSessionId, hashSessionPart, createTrackingId, nowMs, newRunId, newTurnId, previewPrompt, parseModelRef } from "./utils/session.ts";
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 | What it is |
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` | 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). |
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 (err: any) {
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
- inFlightRefresh = null;
393
- });
390
+ inFlightRefresh = refreshModelCatalog({ ...options, ...(timeoutMs !== undefined ? { timeoutMs } : {}) }).finally(
391
+ () => {
392
+ inFlightRefresh = null;
393
+ }
394
+ );
394
395
  }
395
396
  return inFlightRefresh;
396
397
  };
@@ -5,21 +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";
22
- import { ConfigError } from "../types/errors.ts";
11
+ import { ConfigError, ValidationError } from "../types/errors.ts";
23
12
 
24
13
  // ---------------------------------------------------------------------------
25
14
  // Provider aliases — maps our internal ids to models.dev keys
@@ -49,8 +38,8 @@ function applyModalityOverrides(provider: string, modelId: string, base: ModelMo
49
38
  MODALITY_OVERRIDES[`${provider}/${normalizeModelIdForLookup(modelId)}`];
50
39
  if (!hit) return base;
51
40
  return {
52
- input: ((hit.input ?? base.input) as ModelModalities["input"]),
53
- output: ((hit.output ?? base.output) as ModelModalities["output"]),
41
+ input: (hit.input ?? base.input) as ModelModalities["input"],
42
+ output: (hit.output ?? base.output) as ModelModalities["output"],
54
43
  };
55
44
  }
56
45
 
@@ -107,7 +96,7 @@ function customRegistryKey(provider: string, modelId: string): string {
107
96
  */
108
97
  export function registerModel(spec: ModelSpec): void {
109
98
  if (!spec || typeof spec.id !== "string" || typeof spec.provider !== "string") {
110
- throw new Error("[Agent Accelerator] registerModel requires spec.id and spec.provider strings");
99
+ throw new ValidationError("[Agent Accelerator] registerModel requires spec.id and spec.provider strings");
111
100
  }
112
101
  customModelRegistry.set(customRegistryKey(String(spec.provider), String(spec.id)), { ...spec });
113
102
  // Invalidate derived caches so the custom model is visible immediately.
@@ -136,30 +125,38 @@ export function getModelSource(provider: string, modelId: string): "catalog" | "
136
125
  if (customModelRegistry.has(customRegistryKey(p, m)) || customModelRegistry.has(customRegistryKey(p, norm))) {
137
126
  return "override";
138
127
  }
139
- if (
140
- MODALITY_OVERRIDES[`${p}/${m}`] !== undefined ||
141
- MODALITY_OVERRIDES[`${p}/${norm}`] !== undefined
142
- ) {
128
+ if (MODALITY_OVERRIDES[`${p}/${m}`] !== undefined || MODALITY_OVERRIDES[`${p}/${norm}`] !== undefined) {
143
129
  return "override";
144
130
  }
145
131
  return getModelFromCatalog(provider, modelId) ? "catalog" : "unknown";
146
132
  }
147
133
 
148
134
  // Derives capability flags (thinking, caching, tools, structured output, modalities) from a raw catalog entry; in: mapModelSpec, out: ModelCapabilities.
149
- function mapCapabilities(raw: any, cost: ModelCost, reasoning: boolean, toolCall: boolean, modalities: ModelModalities) {
135
+ function mapCapabilities(
136
+ raw: any,
137
+ cost: ModelCost,
138
+ reasoning: boolean,
139
+ toolCall: boolean,
140
+ modalities: ModelModalities
141
+ ) {
150
142
  return {
151
143
  supportsThinking: reasoning,
152
144
  supportsThinkingLevel: !!raw.reasoning_options,
153
- supportsThinkingBudget: Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "budget_tokens"),
145
+ supportsThinkingBudget:
146
+ Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "budget_tokens"),
154
147
  supportsImplicitCaching: cost.cache_read !== undefined || cost.cache_write !== undefined,
155
148
  supportsLongCacheRetention: cost.cache_read !== undefined,
156
149
  supportsExplicitCaching: cost.cache_read !== undefined,
157
150
  supportsParallelToolCalls: toolCall,
158
151
  supportsStreaming: true,
159
152
  supportsStructuredOutput: raw.structured_output === undefined ? true : !!raw.structured_output,
160
- supportsReasoningToggle: Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "toggle"),
161
- supportsReasoningEffort: Array.isArray(raw.reasoning_options) && raw.reasoning_options.some((o: any) => o.type === "effort"),
162
- modalities: [...(modalities.input || []), ...(modalities.output || [])].filter((v: any, i: any, a: any) => a.indexOf(v) === i),
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
+ ),
163
160
  };
164
161
  }
165
162
 
@@ -268,7 +265,9 @@ export function getModelFromCatalog(providerInput: string, modelIdInput: string)
268
265
  }
269
266
 
270
267
  const aliasProviders = PROVIDER_ALIASES[provider] || [provider];
271
- const tryProviders = [...new Set([...aliasProviders, provider, provider.replace("-zen", ""), provider.replace("-go", "")])];
268
+ const tryProviders = [
269
+ ...new Set([...aliasProviders, provider, provider.replace("-zen", ""), provider.replace("-go", "")]),
270
+ ];
272
271
  const catalog = getActiveCatalog();
273
272
 
274
273
  for (const p of tryProviders) {
@@ -292,7 +291,11 @@ export function getModelFromCatalog(providerInput: string, modelIdInput: string)
292
291
  // or normalized equality. The old loose `modelId.endsWith(key)` is removed
293
292
  // (e.g. "gpt-4o-mini-extra" must not match "mini").
294
293
  for (const [key, raw] of Object.entries(providerData.models as Record<string, any>)) {
295
- if (key === modelId || key.endsWith(`/${modelId}`) || normalizeModelIdForLookup(key) === normalizeModelIdForLookup(modelId)) {
294
+ if (
295
+ key === modelId ||
296
+ key.endsWith(`/${modelId}`) ||
297
+ normalizeModelIdForLookup(key) === normalizeModelIdForLookup(modelId)
298
+ ) {
296
299
  result = mapModelSpec(p as ProviderId, key, raw as any);
297
300
  setLookupCache(cacheKey, result);
298
301
  return result;
@@ -411,7 +414,8 @@ export function getModelThinkingInfo(provider: string, modelId: string): ModelTh
411
414
  allowed.add("dynamic");
412
415
 
413
416
  const allowedLevels = Array.from(allowed);
414
- const supportsDisable = allowed.has("none") || !!toggleOpt || Boolean(budgetOpt && (budgetOpt.min === undefined || budgetOpt.min <= 0));
417
+ const supportsDisable =
418
+ allowed.has("none") || !!toggleOpt || Boolean(budgetOpt && (budgetOpt.min === undefined || budgetOpt.min <= 0));
415
419
 
416
420
  return {
417
421
  supportsThinking: true,
@@ -444,7 +448,8 @@ export class ThinkingLevelError extends ConfigError {
444
448
  reason: string;
445
449
  remedy?: string;
446
450
  }) {
447
- const allowed = opts.allowedLevels.length > 0 ? ` Allowed levels: ${opts.allowedLevels.map((l) => `"${l}"`).join(", ")}.` : "";
451
+ const allowed =
452
+ opts.allowedLevels.length > 0 ? ` Allowed levels: ${opts.allowedLevels.map((l) => `"${l}"`).join(", ")}.` : "";
448
453
  const remedy = opts.remedy ? ` How to fix: ${opts.remedy}` : "";
449
454
  super(
450
455
  `[Agent Accelerator] Thinking level "${opts.requestedLevel}" does not fit "${opts.provider}/${opts.modelId}": ${opts.reason}${allowed}${remedy}`,
@@ -464,7 +469,11 @@ export class ThinkingLevelError extends ConfigError {
464
469
  *
465
470
  * @example `validateModelThinking("google", "gemini-3.5-flash-lite", "medium");`
466
471
  */
467
- export function validateModelThinking(provider: string, modelId: string, requestedLevel?: ThinkingLevel | string): void {
472
+ export function validateModelThinking(
473
+ provider: string,
474
+ modelId: string,
475
+ requestedLevel?: ThinkingLevel | string
476
+ ): void {
468
477
  if (!requestedLevel) return;
469
478
  const level = String(requestedLevel).toLowerCase().trim();
470
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 | 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`). |
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.