agent-accelerator 0.4.0 → 0.5.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 (46) hide show
  1. package/README.md +149 -57
  2. package/bunfig.toml +1 -0
  3. package/package.json +2 -3
  4. package/src/agent/agent.ts +414 -118
  5. package/src/agent/context.ts +11 -16
  6. package/src/agent/delegation.ts +111 -274
  7. package/src/agent/loop.ts +327 -99
  8. package/src/agent/subagent.ts +4 -12
  9. package/src/agent/worker.ts +435 -0
  10. package/src/index.ts +58 -5
  11. package/src/models/catalog-cache.ts +65 -25
  12. package/src/models/catalog.ts +47 -2
  13. package/src/providers/google.ts +338 -81
  14. package/src/providers/openai-compat.ts +171 -61
  15. package/src/providers/openai.ts +174 -44
  16. package/src/providers/openrouter.ts +129 -44
  17. package/src/providers/shared.ts +169 -17
  18. package/src/providers.ts +375 -11
  19. package/src/scheduler/scheduled-actions.md +41 -0
  20. package/src/scheduler/scheduler.ts +814 -0
  21. package/src/scheduler/store.ts +263 -0
  22. package/src/scheduler/time.ts +148 -0
  23. package/src/scheduler/tool.ts +151 -0
  24. package/src/session/store.ts +229 -113
  25. package/src/streaming/event-stream.ts +6 -4
  26. package/src/telemetry-file.ts +31 -0
  27. package/src/telemetry.ts +65 -0
  28. package/src/tools/executor.ts +163 -38
  29. package/src/tools/subagent-result.ts +60 -0
  30. package/src/types/agent.ts +74 -22
  31. package/src/types/model.ts +39 -0
  32. package/src/types/response.ts +9 -0
  33. package/src/types/scheduler.ts +81 -0
  34. package/src/types/tool.ts +15 -0
  35. package/src/utils/cache.ts +14 -5
  36. package/src/utils/documents.ts +112 -52
  37. package/src/utils/env.ts +21 -7
  38. package/src/utils/errors.ts +26 -1
  39. package/src/utils/headers.ts +15 -15
  40. package/src/utils/home.ts +30 -0
  41. package/src/utils/media.ts +57 -12
  42. package/src/utils/raw-capture.ts +76 -0
  43. package/src/utils/retry.ts +11 -3
  44. package/src/utils/safe-io.ts +252 -0
  45. package/src/utils/session.ts +7 -8
  46. package/src/data/README.md +0 -84
@@ -2,7 +2,8 @@ import { Agent } from "./agent.ts";
2
2
  import type { AgentConfig } from "../types/agent.ts";
3
3
  import type { ToolDefinition } from "../types/tool.ts";
4
4
  import { agentToTool } from "./delegation.ts";
5
- import { getSubModel, getModel } from "../utils/env.ts";
5
+ import { resolveSubAgentModel } from "./worker.ts";
6
+ import { getModel, getSubModel } from "../utils/env.ts";
6
7
 
7
8
  /** AgentConfig alias for a delegated worker agent. */
8
9
  export interface SubAgentConfig extends AgentConfig {}
@@ -76,17 +77,8 @@ export class SubAgent extends Agent {
76
77
  * ```
77
78
  */
78
79
  constructor(config: SubAgentConfig) {
79
- const resolvedModel =
80
- config.model ??
81
- getSubModel() ??
82
- getModel();
83
-
84
- if (!resolvedModel) {
85
- throw new Error(
86
- `[Agent Accelerator] SubAgent '${config.name || "subagent"}' requires a model. ` +
87
- `Specify 'model' in SubAgent config, or set SUB_AGENT_MODEL or MODEL in environment variables.`
88
- );
89
- }
80
+ // Unified model chain (shared with Agent): explicit > SUB_AGENT_MODEL > MODEL.
81
+ const resolvedModel = resolveSubAgentModel(config);
90
82
 
91
83
  super({
92
84
  ...config,
@@ -0,0 +1,435 @@
1
+ import type { ToolDefinition } from "../types/tool.ts";
2
+ import type { SubAgentStep } from "../types/response.ts";
3
+ import type { AgentConfig } from "../types/agent.ts";
4
+ import type { Agent } from "./agent.ts";
5
+ import { normalizeToolName } from "../tools/executor.ts";
6
+ import { escapeXml } from "../utils/serialization.ts";
7
+ import { getModel, getSubModel } from "../utils/env.ts";
8
+ import { resolveModel } from "../providers/registry.ts";
9
+
10
+ export { escapeXml };
11
+
12
+ /** Minimal live-trace shape shared by fixed + dynamic workers (structural subset of `SubAgentTrace`). */
13
+ export interface LiveWorkerTrace {
14
+ trackingId: string;
15
+ name: string;
16
+ sessionId: string;
17
+ parentSessionId: string;
18
+ status: "running" | "done" | "error";
19
+ task: string;
20
+ role?: string;
21
+ turns: number;
22
+ steps: SubAgentStep[];
23
+ text?: string;
24
+ error?: string;
25
+ }
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Shared identity helpers (single home for Agent / SubAgent / Dynamic workers)
29
+ // ---------------------------------------------------------------------------
30
+
31
+ /** Sanitizes a worker name into an XML tag (`UPPER-KEBAB`, 64 chars). */
32
+ export function sanitizeXmlTag(raw: string): string {
33
+ let s = raw.toUpperCase().replace(/[^A-Z0-9.-]/g, "-");
34
+ if (!/^[A-Z]/.test(s)) s = `AGENT-${s}`;
35
+ s = s.replace(/[.]/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
36
+ return s.slice(0, 64) || "SUBAGENT";
37
+ }
38
+
39
+ /** Sanitizes a worker name into a tool name (`lower-kebab`, 64 chars). */
40
+ export function sanitizeToolName(raw: string): string {
41
+ const tag = sanitizeXmlTag(raw);
42
+ return tag.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-{2,}/g, "-").slice(0, 64) || "sub-agent";
43
+ }
44
+
45
+ /** Stable worker ids: `{sanitizedName}-{32hex}` display id + raw tracking id. */
46
+ export function makeWorkerIds(sanitizedName: string, trackingId: string): { trackingId: string; displayId: string } {
47
+ return { trackingId, displayId: `${sanitizedName}-${trackingId}` };
48
+ }
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Shared Agent / SubAgent config normalization (behavior-preserving)
52
+ // ---------------------------------------------------------------------------
53
+
54
+ /**
55
+ * Unwraps `config.model` exactly like `Agent` ctor:
56
+ * `ModelProviderInstance ({model})` -> inner `{model,apiKey,...}`,
57
+ * otherwise the value as-is.
58
+ */
59
+ function unwrapModelInput(configModel: unknown): unknown {
60
+ const raw = configModel as { model?: unknown } | null | undefined;
61
+ if (raw && typeof raw === "object" && "model" in (raw as Record<string, unknown>)) {
62
+ return raw;
63
+ }
64
+ return configModel;
65
+ }
66
+
67
+ /**
68
+ * Resolves the main model exactly like `Agent` ctor.
69
+ * Returns both the stored `modelStringOrSpec` and the `rawModel` used for
70
+ * `apiKey` / `baseUrl` / `thinkingLevel` inheritance.
71
+ */
72
+ export function resolveMainModel(configModel: AgentConfig["model"]): {
73
+ modelStringOrSpec: string | Record<string, unknown> | undefined;
74
+ rawModel: unknown;
75
+ } {
76
+ const rawModel = unwrapModelInput(configModel) as
77
+ | { model?: unknown; id?: unknown; provider?: unknown }
78
+ | string
79
+ | undefined;
80
+ let modelStringOrSpec: string | Record<string, unknown> | undefined;
81
+ if (rawModel && typeof rawModel === "object" && "model" in (rawModel as Record<string, unknown>)) {
82
+ modelStringOrSpec = (rawModel as { model: string }).model as string;
83
+ } else if (
84
+ rawModel &&
85
+ typeof rawModel === "object" &&
86
+ "id" in (rawModel as Record<string, unknown>) &&
87
+ "provider" in (rawModel as Record<string, unknown>)
88
+ ) {
89
+ modelStringOrSpec = rawModel as Record<string, unknown>;
90
+ } else {
91
+ modelStringOrSpec = (rawModel as string | undefined) || getModel();
92
+ }
93
+ return { modelStringOrSpec: modelStringOrSpec as string | Record<string, unknown> | undefined, rawModel };
94
+ }
95
+
96
+ /**
97
+ * Resolves the dynamic worker model exactly like `Agent` ctor:
98
+ * `dynamicSubagents.model ?? subagentModel ?? SUB_AGENT_MODEL`.
99
+ * Unwraps `ModelProviderInstance` / `ModelSpec` the same way.
100
+ */
101
+ export function resolveDynamicWorkerModel(
102
+ dynModelRawInput: unknown,
103
+ subagentModelInput: unknown
104
+ ): string | Record<string, unknown> | undefined {
105
+ const dynModelRaw = dynModelRawInput as
106
+ | { model?: unknown; id?: unknown; provider?: unknown }
107
+ | string
108
+ | undefined;
109
+ let dynModel: string | Record<string, unknown> | undefined;
110
+ if (dynModelRaw && typeof dynModelRaw === "object" && "model" in (dynModelRaw as Record<string, unknown>)) {
111
+ dynModel = (dynModelRaw as { model: string }).model as unknown as string | Record<string, unknown>;
112
+ } else if (
113
+ dynModelRaw &&
114
+ typeof dynModelRaw === "object" &&
115
+ "id" in (dynModelRaw as Record<string, unknown>) &&
116
+ "provider" in (dynModelRaw as Record<string, unknown>)
117
+ ) {
118
+ dynModel = dynModelRaw as Record<string, unknown>;
119
+ } else {
120
+ dynModel = (dynModelRaw as string | undefined) || getSubModel() as string | undefined;
121
+ }
122
+ void subagentModelInput;
123
+ return dynModel;
124
+ }
125
+
126
+ /**
127
+ * Resolves the combined dynamic worker model input
128
+ * (`dynamicSubagents.model ?? subagentModel`) exactly like `Agent` ctor,
129
+ * including the `SUB_AGENT_MODEL` fallback.
130
+ */
131
+ export function resolveDynamicModelInput(
132
+ dynamicSubagents: unknown,
133
+ subagentModel: unknown
134
+ ): string | Record<string, unknown> | undefined {
135
+ const dynModelRaw = (dynamicSubagents as { model?: unknown } | undefined)?.model ?? subagentModel;
136
+ return resolveDynamicWorkerModel(dynModelRaw, subagentModel);
137
+ }
138
+
139
+ /**
140
+ * Resolves a `SubAgent` model exactly like `SubAgent` ctor:
141
+ * `config.model ?? SUB_AGENT_MODEL ?? MODEL`, throwing with the same message
142
+ * when nothing resolves. Kept here so `Agent` / `SubAgent` share one chain.
143
+ */
144
+ export function resolveSubAgentModel(config: { name?: string; model?: AgentConfig["model"] }): NonNullable<AgentConfig["model"]> {
145
+ const resolvedModel = config.model ?? getSubModel() ?? getModel();
146
+ if (!resolvedModel) {
147
+ throw new Error(
148
+ `[Agent Accelerator] SubAgent '${config.name || "subagent"}' requires a model. ` +
149
+ `Specify 'model' in SubAgent config, or set SUB_AGENT_MODEL or MODEL in environment variables.`
150
+ );
151
+ }
152
+ return resolvedModel as NonNullable<AgentConfig["model"]>;
153
+ }
154
+
155
+ /**
156
+ * Normalizes a `tools` record|array into a record with `name` defaults,
157
+ * exactly like `Agent` ctor (both main tools and dynamic pool).
158
+ */
159
+ export function normalizeToolMap(
160
+ rawTools: Record<string, ToolDefinition> | ToolDefinition[] | undefined
161
+ ): Record<string, ToolDefinition> {
162
+ const out: Record<string, ToolDefinition> = {};
163
+ if (!rawTools) return out;
164
+ if (Array.isArray(rawTools)) {
165
+ for (const t of rawTools) {
166
+ const tName = (t as { name?: string })?.name || `tool_${Object.keys(out).length}`;
167
+ out[tName] = { ...(t as ToolDefinition), name: tName } as ToolDefinition;
168
+ }
169
+ } else {
170
+ for (const [key, def] of Object.entries(rawTools)) {
171
+ out[key] = { ...(def as ToolDefinition), name: (def as { name?: string })?.name || key } as ToolDefinition;
172
+ }
173
+ }
174
+ return out;
175
+ }
176
+
177
+ // ---------------------------------------------------------------------------
178
+ // Shared dynamic-worker helpers (pure, no store access)
179
+ // ---------------------------------------------------------------------------
180
+
181
+ /** Resolves per-worker timeout: `>0` fixed, `0` unlimited, `-1` per-task. */
182
+ export function normalizeWorkerTimeoutValue(cfgTimeout: number, taskTimeoutMs: unknown): number {
183
+ return cfgTimeout > 0 ? cfgTimeout : cfgTimeout === -1 && (taskTimeoutMs as number) > 0 ? Math.floor(taskTimeoutMs as number) : 0;
184
+ }
185
+
186
+ /**
187
+ * Grants ONLY the Main Agent-selected subset from the developer pool.
188
+ * Unknown names are dropped. Matching is case/format-insensitive.
189
+ */
190
+ export function grantWorkerTools(
191
+ toolPool: Record<string, ToolDefinition>,
192
+ requested: unknown
193
+ ): Record<string, ToolDefinition> {
194
+ const poolByNormalized = new Map<string, ToolDefinition>();
195
+ for (const [key, def] of Object.entries(toolPool)) {
196
+ poolByNormalized.set(normalizeToolName(key), def);
197
+ const declared = (def as ToolDefinition)?.name;
198
+ if (declared) poolByNormalized.set(normalizeToolName(declared), def);
199
+ }
200
+ const grantedTools: Record<string, ToolDefinition> = {};
201
+ for (const toolName of ((requested as string[] | undefined) ?? [])) {
202
+ const pooled = (toolPool as Record<string, ToolDefinition>)[toolName] ?? poolByNormalized.get(normalizeToolName(String(toolName)));
203
+ if (pooled) grantedTools[(pooled as ToolDefinition).name || toolName] = pooled;
204
+ }
205
+ return grantedTools;
206
+ }
207
+
208
+ /** Resolves a provider id for credential reuse / error metadata (shared). */
209
+ export function providerOfWorkerModel(modelStr: string | unknown | undefined): string | undefined {
210
+ return providerOf(modelStr);
211
+ }
212
+
213
+ function providerOf(modelStr: string | unknown | undefined): string | undefined {
214
+ if (!modelStr) return undefined;
215
+ try {
216
+ const m =
217
+ typeof modelStr === "string"
218
+ ? modelStr
219
+ : (modelStr as { model?: unknown; id?: unknown })?.model ??
220
+ (modelStr as { id?: unknown })?.id ??
221
+ String(modelStr);
222
+ return resolveModel(m as string).provider.id;
223
+ } catch {
224
+ return undefined;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Resolves worker credentials: reuses parent `apiKey`/`baseUrl` ONLY when the
230
+ * worker provider matches the parent provider (otherwise env resolves).
231
+ */
232
+ export function resolveWorkerCredentials(
233
+ parentAgent: { modelStringOrSpec?: unknown; apiKey?: string; baseUrl?: string },
234
+ chosenModel: unknown
235
+ ): { apiKeyToUse: string | undefined; baseUrlToUse: string | undefined; attemptedProvider: string | undefined } {
236
+ const chosenModelStrForProvider =
237
+ typeof chosenModel === "string"
238
+ ? chosenModel
239
+ : (chosenModel as { model?: unknown; id?: unknown })?.model ??
240
+ (chosenModel as { id?: unknown })?.id ??
241
+ String(chosenModel);
242
+ const attemptedProvider = providerOf(chosenModelStrForProvider);
243
+ const parentProvider = providerOf(parentAgent.modelStringOrSpec as string);
244
+ const targetProviderFinal = providerOf(chosenModelStrForProvider);
245
+ if (targetProviderFinal === parentProvider) {
246
+ return { apiKeyToUse: parentAgent.apiKey, baseUrlToUse: parentAgent.baseUrl, attemptedProvider };
247
+ }
248
+ return { apiKeyToUse: undefined, baseUrlToUse: undefined, attemptedProvider };
249
+ }
250
+
251
+ /**
252
+ * Config of a stateless dynamic worker (`spawn_subagents` and scheduled
253
+ * actions): the developer's fixed worker model, level and limits, never the
254
+ * LLM's choice. Credentials carry over only when the providers match, and
255
+ * `inheritableConfig()` keeps the worker no less constrained than its parent.
256
+ */
257
+ export function buildDynamicWorkerConfig(
258
+ parent: Agent,
259
+ worker: { name: string; role?: string; instructions: string; sessionId: string; tools: Record<string, ToolDefinition> }
260
+ ): AgentConfig {
261
+ const model = parent.subagentModel;
262
+ const { apiKeyToUse, baseUrlToUse } = resolveWorkerCredentials(parent, model);
263
+ return {
264
+ name: worker.name,
265
+ description: worker.role || `Sub-agent ${worker.name}`,
266
+ instructions: worker.instructions,
267
+ model,
268
+ apiKey: apiKeyToUse,
269
+ baseUrl: baseUrlToUse,
270
+ thinkingLevel: parent.dynamicSubagents?.thinkingLevel ?? parent.thinkingConfig?.level,
271
+ sessionId: worker.sessionId,
272
+ tools: worker.tools,
273
+ // Stateless by design: one task in, one result out, then shut down. No history, no recursion.
274
+ stateless: true,
275
+ serviceTier: parent.serviceTier,
276
+ headers: parent.customHeaders,
277
+ maxTurns: parent.maxTurns,
278
+ ...parent.inheritableConfig(),
279
+ };
280
+ }
281
+
282
+ /** Builds one `<NAME>...text...</NAME>` block inside `<SUB-AGENTS-RESPONSE>`. */
283
+ export function buildWorkerXmlBlock(name: string, text: string): string {
284
+ const escaped = escapeXml(text);
285
+ const indentedText = escaped.split("\n").map((line) => ` ${line}`).join("\n");
286
+ return ` <${name}>\n${indentedText}\n </${name}>`;
287
+ }
288
+
289
+ /** Builds the `<SUB-AGENTS-RESPONSE>` envelope for dynamic workers. */
290
+ export function buildSubagentsXml(results: Array<{ name: string; text: string }>): string {
291
+ return `<SUB-AGENTS-RESPONSE>\n${results.map((r) => buildWorkerXmlBlock(r.name, r.text)).join("\n")}\n</SUB-AGENTS-RESPONSE>`;
292
+ }
293
+
294
+ // ---------------------------------------------------------------------------
295
+ // Shared live worker state machine (single home for fixed + dynamic workers)
296
+ // ---------------------------------------------------------------------------
297
+
298
+ /** Turn summary fed into the live worker trace (mirrors `AgentRunOptions.onTurn`). */
299
+ export interface LiveWorkerTurn {
300
+ turns: number;
301
+ text?: string;
302
+ thinking?: string;
303
+ toolCalls?: Array<{ name: string }>;
304
+ toolResults?: Array<{ name: string; isError?: boolean }>;
305
+ }
306
+
307
+ /** Deltas forwarded to the parent stream via `onSubagentEvent`. */
308
+ export interface LiveWorkerEvent {
309
+ trackingId: string;
310
+ delta?: string;
311
+ thinkingDelta?: string;
312
+ partialText?: string;
313
+ partialThinking?: string;
314
+ }
315
+
316
+ /** Hooks owned by the trace store (`delegation.ts`); worker core stays pure. */
317
+ export interface LiveWorkerHooks {
318
+ trim?: (trace: LiveWorkerTrace) => void;
319
+ touch?: (trackingId: string) => void;
320
+ notify?: (trackingId: string) => void;
321
+ persist?: () => void;
322
+ emit?: (event: LiveWorkerEvent) => void;
323
+ }
324
+
325
+ /**
326
+ * Single live worker state machine shared by fixed (`agentToTool`) and
327
+ * dynamic (`spawn_subagents`) workers. Owns `liveTurn` / `partialText` /
328
+ * `partialThinking`, finalizes partial streaming entries on `onTurn`, and
329
+ * forwards deltas to the parent stream. The trace object is mutated in place;
330
+ * trimming / touching / notifying / persisting are delegated to hooks so this
331
+ * module never touches the trace store directly.
332
+ */
333
+ export function createLiveWorker(
334
+ trace: LiveWorkerTrace,
335
+ trackingId: string,
336
+ displayId: string,
337
+ hooks: LiveWorkerHooks = {}
338
+ ): {
339
+ onWorkerTurn: (turn: LiveWorkerTurn) => void;
340
+ onWorkerDelta: (delta?: string, thinkingDelta?: string) => void;
341
+ attachToStream: (workerStream: {
342
+ on: (event: string, listener: (e: { delta?: string; thinkingDelta?: string }) => void) => void;
343
+ off: (event: string, listener: (e: { delta?: string; thinkingDelta?: string }) => void) => void;
344
+ }) => () => void;
345
+ } {
346
+ let liveTurn = 1;
347
+ let partialText = "";
348
+ let partialThinking = "";
349
+
350
+ const findPartial = (turn: number): SubAgentStep | undefined => {
351
+ for (let i = trace.steps.length - 1; i >= 0; i--) {
352
+ const s = trace.steps[i]!;
353
+ if (s.turn === turn && s.partial) return s;
354
+ }
355
+ return undefined;
356
+ };
357
+
358
+ const onWorkerTurn = (turn: LiveWorkerTurn): void => {
359
+ try {
360
+ liveTurn = turn.turns;
361
+ const timestamp = Date.now();
362
+ const partialEntry = findPartial(liveTurn);
363
+ if (partialEntry) {
364
+ if (turn.text) partialEntry.text = turn.text;
365
+ if (turn.thinking) partialEntry.thinking = turn.thinking;
366
+ delete (partialEntry as { partial?: boolean }).partial;
367
+ } else if (turn.text) {
368
+ trace.steps.push({ turn: turn.turns, type: "assistant", text: turn.text.slice(0, 2000), timestamp });
369
+ }
370
+ for (const tc of turn.toolCalls ?? []) {
371
+ trace.steps.push({ turn: turn.turns, type: "tool_call", name: tc.name, timestamp });
372
+ }
373
+ for (const tr of turn.toolResults ?? []) {
374
+ trace.steps.push({ turn: turn.turns, type: "tool_result", name: tr.name, isError: tr.isError, timestamp });
375
+ }
376
+ trace.turns = Math.max(trace.turns, turn.turns);
377
+ try { hooks.trim?.(trace); } catch {}
378
+ try { hooks.touch?.(trace.trackingId); } catch {}
379
+ try { hooks.notify?.(trackingId); } catch {}
380
+ try { hooks.persist?.(); } catch {}
381
+ } catch {}
382
+ };
383
+
384
+ const onWorkerDelta = (delta?: string, thinkingDelta?: string): void => {
385
+ try {
386
+ if (delta) {
387
+ partialText = (partialText + delta).slice(-10000);
388
+ let entry = findPartial(liveTurn);
389
+ if (!entry) {
390
+ entry = { turn: liveTurn, type: "assistant", text: "", partial: true, timestamp: Date.now() };
391
+ trace.steps.push(entry);
392
+ }
393
+ entry.text = partialText;
394
+ entry.timestamp = Date.now();
395
+ }
396
+ if (thinkingDelta) {
397
+ partialThinking = (partialThinking + thinkingDelta).slice(-10000);
398
+ let entry = findPartial(liveTurn);
399
+ if (!entry) {
400
+ entry = { turn: liveTurn, type: "assistant", text: "", partial: true, timestamp: Date.now() };
401
+ trace.steps.push(entry);
402
+ }
403
+ entry.thinking = partialThinking;
404
+ entry.timestamp = Date.now();
405
+ }
406
+ try { hooks.trim?.(trace); } catch {}
407
+ try { hooks.notify?.(trackingId); } catch {}
408
+ try {
409
+ hooks.emit?.({
410
+ trackingId: displayId,
411
+ delta,
412
+ thinkingDelta,
413
+ partialText: partialText || undefined,
414
+ partialThinking: partialThinking || undefined,
415
+ });
416
+ } catch {}
417
+ } catch {}
418
+ };
419
+
420
+ const attachToStream = (workerStream: {
421
+ on: (event: string, listener: (e: { delta?: string; thinkingDelta?: string }) => void) => void;
422
+ off: (event: string, listener: (e: { delta?: string; thinkingDelta?: string }) => void) => void;
423
+ }): (() => void) => {
424
+ const textListener = (e: { delta?: string }): void => onWorkerDelta(e.delta, undefined);
425
+ const thinkingListener = (e: { thinkingDelta?: string }): void => onWorkerDelta(undefined, e.thinkingDelta);
426
+ try { workerStream.on("text_delta", textListener as (e: { delta?: string; thinkingDelta?: string }) => void); } catch {}
427
+ try { workerStream.on("thinking_delta", thinkingListener as (e: { delta?: string; thinkingDelta?: string }) => void); } catch {}
428
+ return () => {
429
+ try { workerStream.off("text_delta", textListener as (e: { delta?: string; thinkingDelta?: string }) => void); } catch {}
430
+ try { workerStream.off("thinking_delta", thinkingListener as (e: { delta?: string; thinkingDelta?: string }) => void); } catch {}
431
+ };
432
+ };
433
+
434
+ return { onWorkerTurn, onWorkerDelta, attachToStream };
435
+ }
package/src/index.ts CHANGED
@@ -61,6 +61,7 @@ export {
61
61
  convertDocumentToMarkdown,
62
62
  convertDocumentInput,
63
63
  convert_document_to_markdown,
64
+ createConvertDocumentTool,
64
65
  isAnydocAvailable,
65
66
  resolveDocumentFormat,
66
67
  truncateMarkdown,
@@ -71,7 +72,8 @@ export {
71
72
  DEFAULT_DOCUMENT_MAX_CHARS,
72
73
  MAX_DOCUMENT_FETCH_BYTES,
73
74
  } from "./utils/documents.ts";
74
- export type { ConvertDocumentOptions, ConvertedDocument } from "./utils/documents.ts";
75
+ export type { ConvertDocumentOptions, ConvertedDocument, ConvertDocumentToolOptions } from "./utils/documents.ts";
76
+ export { assertPathAllowed, enforceFileAccess, fetchRemoteBytes, isPrivateAddress, RemoteFetchError, MAX_REMOTE_FETCH_BYTES } from "./utils/safe-io.ts";
75
77
 
76
78
  // Multi-Agent Delegation & Tools
77
79
  export {
@@ -83,8 +85,11 @@ export {
83
85
  isSessionDescendant,
84
86
  getSubAgentTrace,
85
87
  getSubAgentTraceScoped,
88
+ getSubAgentTraceForSessions,
86
89
  listSubAgentTraceIds,
90
+ listSubAgentTraceIdsForSessions,
87
91
  subscribeToSubAgent,
92
+ subscribeToSubAgentForSessions,
88
93
  appendSubAgentStep,
89
94
  pruneTraces,
90
95
  trimTraceSteps,
@@ -100,6 +105,28 @@ export {
100
105
  } from "./agent/delegation.ts";
101
106
  export type { DynamicSubagentTask, AgentAsToolTarget, SubAgentTrace, StreamingLoopHandle } from "./agent/delegation.ts";
102
107
 
108
+ // Scheduled actions: dynamic sub-agents that run later and save their answer to a file
109
+ export {
110
+ createScheduledAction,
111
+ listScheduledActions,
112
+ updateScheduledAction,
113
+ runScheduledAction,
114
+ skipScheduledAction,
115
+ killScheduledAction,
116
+ } from "./scheduler/scheduler.ts";
117
+ export { getDatetimeTool } from "./scheduler/tool.ts";
118
+ export { setHome, resolveHome } from "./utils/home.ts";
119
+ export type {
120
+ ScheduledActionInput,
121
+ ScheduledActionPatch,
122
+ ScheduledActionRecord,
123
+ ScheduledDelivery,
124
+ ScheduledState,
125
+ ScheduledRunStatus,
126
+ ScheduleSpec,
127
+ KillResult,
128
+ } from "./types/scheduler.ts";
129
+
103
130
  // Providers & Registry — unified multi-provider layer, all native REST
104
131
  export {
105
132
  getProvider,
@@ -210,6 +237,16 @@ export {
210
237
  mapToolChoiceToOpenAI,
211
238
  normalizeToolChoice,
212
239
  parseStreamedToolArguments,
240
+ selectRawArguments,
241
+ normalizeStructuredOutput,
242
+ extractOutputValidator,
243
+ parseStructuredOutput,
244
+ mapOutputToOpenAI,
245
+ mapOutputToGoogle,
246
+ mapOutputToOpenRouterChat,
247
+ mapOutputToCompatChat,
248
+ buildStructuredOutputInstruction,
249
+ augmentSystemPromptWithStructuredOutput,
213
250
  } from "./providers.ts";
214
251
  export type {
215
252
  ProviderCapabilityStatus,
@@ -217,13 +254,21 @@ export type {
217
254
  OpenRouterToolChoice,
218
255
  OpenRouterChatToolChoice,
219
256
  OpenAIToolChoice,
257
+ ChatCompletionsResponseFormat,
220
258
  } from "./providers.ts";
221
259
 
222
260
  export {
223
261
  GOOGLE_MODELS,
224
262
  OPENAI_MODELS,
225
263
  OPENROUTER_MODELS,
264
+ modelSupportsStructuredOutput,
226
265
  } from "./models/catalog.ts";
266
+ export {
267
+ hasAuthHeader,
268
+ clampToolCallId,
269
+ hasTruncatedToolArguments,
270
+ shortHashId,
271
+ } from "./providers/shared.ts";
227
272
 
228
273
  export {
229
274
  isValidThoughtSignature,
@@ -274,6 +319,10 @@ export type { AgentAccelErrorCode, AgentAccelErrorContext } from "./types/errors
274
319
  // Normalized run limits (Q-22; sub-agents inherit unless overridden)
275
320
  export type { AgentRunLimits } from "./agent/agent.ts";
276
321
 
322
+ // Telemetry bus: warnings, anomalies and per-turn thinking traces (attach with `telemetryBus.onEvent`)
323
+ export { telemetryBus } from "./telemetry.ts";
324
+ export type { TelemetryEvent, TelemetryListener } from "./telemetry.ts";
325
+
277
326
  // Session persistence & telemetry (conversation history + cost rollup)
278
327
  export {
279
328
  SessionTelemetry,
@@ -289,6 +338,8 @@ export {
289
338
  buildSessionData,
290
339
  serializeSession,
291
340
  deserializeSession,
341
+ inspectSessionPayload,
342
+ findInvalidMessage,
292
343
  getSessionContextWindow,
293
344
  computeSessionTurnCost,
294
345
  formatSessionTokens,
@@ -296,7 +347,7 @@ export {
296
347
  formatSessionBanner,
297
348
  emptyTotals,
298
349
  } from "./session/store.ts";
299
- export type { PersistedAgentSession, SessionTotals, SessionAgentLike, LoadedSessionDir, PersistedSubAgentTrace } from "./session/store.ts";
350
+ export type { SessionLimits, SessionPayloadStatus, PersistedAgentSession, SessionTotals, SessionAgentLike, LoadedSessionDir, PersistedSubAgentTrace } from "./session/store.ts";
300
351
 
301
352
  // Utilities
302
353
  /**
@@ -311,8 +362,9 @@ export type { ParsedModelRef, ResolvedAgentConfig } from "./utils/session.ts";
311
362
  * `/internal` entry (Q-50).
312
363
  */
313
364
  export { getApiKey, getEnv } from "./utils/env.ts";
314
- export { buildSessionHeaders } from "./utils/headers.ts";
315
- export { normalizeMediaInput, inferMimeType } from "./utils/media.ts";
365
+ export { buildSessionHeaders, setAttribution, getAttribution } from "./utils/headers.ts";
366
+ export type { AttributionConfig } from "./utils/headers.ts";
367
+ export { normalizeMediaInput, inferMimeType, sniffMimeType } from "./utils/media.ts";
316
368
  export { base64ToBytes, bytesToBase64 } from "./utils/base64.ts";
317
369
  export { toJsonSafe, safeStringify, escapeXml } from "./utils/serialization.ts";
318
370
 
@@ -368,6 +420,7 @@ export type {
368
420
  ProviderRequestOptions,
369
421
  ProviderGenerateResult,
370
422
  ProviderRawData,
423
+ StructuredOutputSpec,
371
424
  } from "./types/model.ts";
372
425
 
373
426
  export type {
@@ -388,7 +441,7 @@ export type {
388
441
  WhenBusyMode,
389
442
  ModelRef,
390
443
  ThinkingOption,
391
- SamplingConfig,
444
+ StructuredOutputOption,
392
445
  RetryPolicyConfig,
393
446
  AgentLogger,
394
447
  ToolChoiceOption,