agent-accelerator 0.4.5 → 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 (45) 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 +401 -120
  5. package/src/agent/context.ts +11 -16
  6. package/src/agent/delegation.ts +111 -274
  7. package/src/agent/loop.ts +253 -98
  8. package/src/agent/subagent.ts +4 -12
  9. package/src/agent/worker.ts +435 -0
  10. package/src/index.ts +39 -5
  11. package/src/models/catalog-cache.ts +65 -25
  12. package/src/models/catalog.ts +21 -2
  13. package/src/providers/google.ts +282 -74
  14. package/src/providers/openai-compat.ts +61 -41
  15. package/src/providers/openai.ts +46 -31
  16. package/src/providers/openrouter.ts +42 -31
  17. package/src/providers/shared.ts +110 -16
  18. package/src/providers.ts +119 -6
  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 +211 -104
  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 +57 -20
  31. package/src/types/model.ts +18 -0
  32. package/src/types/response.ts +2 -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/errors.ts +11 -0
  38. package/src/utils/headers.ts +15 -15
  39. package/src/utils/home.ts +30 -0
  40. package/src/utils/media.ts +57 -12
  41. package/src/utils/raw-capture.ts +76 -0
  42. package/src/utils/retry.ts +11 -3
  43. package/src/utils/safe-io.ts +252 -0
  44. package/src/utils/session.ts +7 -8
  45. 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,7 @@ export {
210
237
  mapToolChoiceToOpenAI,
211
238
  normalizeToolChoice,
212
239
  parseStreamedToolArguments,
240
+ selectRawArguments,
213
241
  normalizeStructuredOutput,
214
242
  extractOutputValidator,
215
243
  parseStructuredOutput,
@@ -291,6 +319,10 @@ export type { AgentAccelErrorCode, AgentAccelErrorContext } from "./types/errors
291
319
  // Normalized run limits (Q-22; sub-agents inherit unless overridden)
292
320
  export type { AgentRunLimits } from "./agent/agent.ts";
293
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
+
294
326
  // Session persistence & telemetry (conversation history + cost rollup)
295
327
  export {
296
328
  SessionTelemetry,
@@ -306,6 +338,8 @@ export {
306
338
  buildSessionData,
307
339
  serializeSession,
308
340
  deserializeSession,
341
+ inspectSessionPayload,
342
+ findInvalidMessage,
309
343
  getSessionContextWindow,
310
344
  computeSessionTurnCost,
311
345
  formatSessionTokens,
@@ -313,7 +347,7 @@ export {
313
347
  formatSessionBanner,
314
348
  emptyTotals,
315
349
  } from "./session/store.ts";
316
- 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";
317
351
 
318
352
  // Utilities
319
353
  /**
@@ -328,8 +362,9 @@ export type { ParsedModelRef, ResolvedAgentConfig } from "./utils/session.ts";
328
362
  * `/internal` entry (Q-50).
329
363
  */
330
364
  export { getApiKey, getEnv } from "./utils/env.ts";
331
- export { buildSessionHeaders } from "./utils/headers.ts";
332
- 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";
333
368
  export { base64ToBytes, bytesToBase64 } from "./utils/base64.ts";
334
369
  export { toJsonSafe, safeStringify, escapeXml } from "./utils/serialization.ts";
335
370
 
@@ -407,7 +442,6 @@ export type {
407
442
  ModelRef,
408
443
  ThinkingOption,
409
444
  StructuredOutputOption,
410
- SamplingConfig,
411
445
  RetryPolicyConfig,
412
446
  AgentLogger,
413
447
  ToolChoiceOption,