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.
- package/README.md +149 -57
- package/bunfig.toml +1 -0
- package/package.json +2 -3
- package/src/agent/agent.ts +401 -120
- package/src/agent/context.ts +11 -16
- package/src/agent/delegation.ts +111 -274
- package/src/agent/loop.ts +253 -98
- package/src/agent/subagent.ts +4 -12
- package/src/agent/worker.ts +435 -0
- package/src/index.ts +39 -5
- package/src/models/catalog-cache.ts +65 -25
- package/src/models/catalog.ts +21 -2
- package/src/providers/google.ts +282 -74
- package/src/providers/openai-compat.ts +61 -41
- package/src/providers/openai.ts +46 -31
- package/src/providers/openrouter.ts +42 -31
- package/src/providers/shared.ts +110 -16
- package/src/providers.ts +119 -6
- package/src/scheduler/scheduled-actions.md +41 -0
- package/src/scheduler/scheduler.ts +814 -0
- package/src/scheduler/store.ts +263 -0
- package/src/scheduler/time.ts +148 -0
- package/src/scheduler/tool.ts +151 -0
- package/src/session/store.ts +211 -104
- package/src/streaming/event-stream.ts +6 -4
- package/src/telemetry-file.ts +31 -0
- package/src/telemetry.ts +65 -0
- package/src/tools/executor.ts +163 -38
- package/src/tools/subagent-result.ts +60 -0
- package/src/types/agent.ts +57 -20
- package/src/types/model.ts +18 -0
- package/src/types/response.ts +2 -0
- package/src/types/scheduler.ts +81 -0
- package/src/types/tool.ts +15 -0
- package/src/utils/cache.ts +14 -5
- package/src/utils/documents.ts +112 -52
- package/src/utils/errors.ts +11 -0
- package/src/utils/headers.ts +15 -15
- package/src/utils/home.ts +30 -0
- package/src/utils/media.ts +57 -12
- package/src/utils/raw-capture.ts +76 -0
- package/src/utils/retry.ts +11 -3
- package/src/utils/safe-io.ts +252 -0
- package/src/utils/session.ts +7 -8
- package/src/data/README.md +0 -84
package/src/agent/subagent.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
80
|
-
|
|
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 {
|
|
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,
|