agent-accelerator 0.5.5 → 0.5.7
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 +16 -17
- package/package.json +1 -1
- package/src/agent/agent.ts +16 -18
- package/src/agent/architecture.md +25 -0
- package/src/agent/context.ts +4 -0
- package/src/agent/delegation.ts +18 -84
- package/src/agent/loop.ts +14 -16
- package/src/agent/worker.ts +1 -3
- package/src/architecture.md +28 -0
- package/src/index.ts +6 -73
- package/src/models/architecture.md +14 -0
- package/src/models/catalog-cache.ts +3 -0
- package/src/models/catalog.ts +7 -0
- package/src/providers/architecture.md +24 -0
- package/src/providers/google.ts +26 -39
- package/src/providers/openai-compat.ts +53 -104
- package/src/providers/openai.ts +47 -104
- package/src/providers/openrouter.ts +114 -175
- package/src/providers/registry.ts +1 -0
- package/src/providers/shared.ts +134 -5
- package/src/providers.ts +30 -59
- package/src/scheduler/architecture.md +19 -0
- package/src/scheduler/scheduler.ts +21 -0
- package/src/scheduler/store.ts +6 -0
- package/src/scheduler/time.ts +2 -0
- package/src/session/architecture.md +19 -0
- package/src/session/store.ts +18 -3
- package/src/session/usage.ts +1 -0
- package/src/streaming/architecture.md +13 -0
- package/src/streaming/event-stream.ts +5 -0
- package/src/telemetry.ts +1 -0
- package/src/tools/architecture.md +19 -0
- package/src/tools/executor.ts +13 -0
- package/src/tools/schema.ts +1 -0
- package/src/tools/subagent-result.ts +1 -0
- package/src/types/architecture.md +20 -0
- package/src/types/message.ts +1 -1
- package/src/utils/architecture.md +25 -0
- package/src/utils/base64.ts +2 -0
- package/src/utils/cache.ts +2 -0
- package/src/utils/errors.ts +8 -1
- package/src/utils/headers.ts +4 -1
- package/src/utils/media.ts +11 -1
- package/src/utils/raw-capture.ts +3 -0
- package/src/utils/retry.ts +1 -0
- package/src/utils/safe-io.ts +4 -0
- package/src/utils/session.ts +5 -161
package/src/utils/session.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { clampCacheKey } from "./cache.ts";
|
|
2
2
|
import type { ThinkingLevel } from "../types/core.ts";
|
|
3
|
-
import type { MidSessionMode } from "../types/agent.ts";
|
|
4
|
-
import type { AgentConfig } from "../types/agent.ts";
|
|
5
3
|
import type { ModelSpec } from "../types/model.ts";
|
|
6
4
|
import type { ModelProviderInstance } from "../providers/registry.ts";
|
|
7
5
|
import type { ContentPart } from "../types/message.ts";
|
|
@@ -83,6 +81,7 @@ export function nowMs(): number {
|
|
|
83
81
|
return Date.now();
|
|
84
82
|
}
|
|
85
83
|
|
|
84
|
+
// 32 random lowercase hex characters; in: newRunId/newTurnId, out: a string.
|
|
86
85
|
function random32Hex(): string {
|
|
87
86
|
try {
|
|
88
87
|
const c = (globalThis as any)?.crypto;
|
|
@@ -111,14 +110,7 @@ export function newTurnId(): string {
|
|
|
111
110
|
return random32Hex();
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
/**
|
|
115
|
-
* Single canonical prompt preview (Q-49): merges the former
|
|
116
|
-
* `previewPrompt` (agent.ts) / `steerPreview` (loop.ts) duplicates.
|
|
117
|
-
* Bounded to 500 chars, display-only. Other modules own their copies;
|
|
118
|
-
* new code should import this one.
|
|
119
|
-
*
|
|
120
|
-
* @example `previewPrompt("hello")`
|
|
121
|
-
*/
|
|
113
|
+
/** Bounded (500 chars), display-only preview of a prompt, used in `queued`/`steer_injected` events, turn lists and `interrupted()`. */
|
|
122
114
|
export function previewPrompt(prompt: string | ContentPart[]): string {
|
|
123
115
|
if (typeof prompt === "string") return prompt.slice(0, 500);
|
|
124
116
|
try {
|
|
@@ -141,6 +133,7 @@ export interface ParsedModelRef {
|
|
|
141
133
|
thinking?: ThinkingLevel;
|
|
142
134
|
}
|
|
143
135
|
|
|
136
|
+
// Splits `provider/model` on the first slash (bare ids default to openrouter); in: parseModelRef, out: provider and model id.
|
|
144
137
|
function splitModelString(raw: string): { provider: string; modelId: string } {
|
|
145
138
|
const trimmed = raw.trim();
|
|
146
139
|
const slash = trimmed.indexOf("/");
|
|
@@ -153,12 +146,14 @@ function splitModelString(raw: string): { provider: string; modelId: string } {
|
|
|
153
146
|
};
|
|
154
147
|
}
|
|
155
148
|
|
|
149
|
+
// Type guard for a `{ model: string }` helper result; in: parseModelRef, out: a boolean.
|
|
156
150
|
function isModelProviderInstance(v: unknown): v is ModelProviderInstance {
|
|
157
151
|
if (!v || typeof v !== "object" || Array.isArray(v)) return false;
|
|
158
152
|
const r = v as Record<string, unknown>;
|
|
159
153
|
return typeof r["model"] === "string" && (r["model"] as string).length > 0;
|
|
160
154
|
}
|
|
161
155
|
|
|
156
|
+
// Type guard for a catalog spec (`provider` and `id` strings); in: parseModelRef, out: a boolean.
|
|
162
157
|
function isModelSpec(v: unknown): v is ModelSpec {
|
|
163
158
|
if (!v || typeof v !== "object" || Array.isArray(v)) return false;
|
|
164
159
|
const r = v as Record<string, unknown>;
|
|
@@ -192,154 +187,3 @@ export function parseModelRef(input: string | ModelSpec | ModelProviderInstance)
|
|
|
192
187
|
}
|
|
193
188
|
throw new ConfigError("parseModelRef: expected string | ModelSpec | ModelProviderInstance.");
|
|
194
189
|
}
|
|
195
|
-
|
|
196
|
-
/** Validated, readonly agent configuration returned by {@link resolveAgentConfig} (Q-32). */
|
|
197
|
-
export interface ResolvedAgentConfig {
|
|
198
|
-
readonly model: NonNullable<AgentConfig["model"]>;
|
|
199
|
-
readonly thinkingLevel?: ThinkingLevel;
|
|
200
|
-
readonly maxTurns: number;
|
|
201
|
-
readonly midSession: { readonly mode: MidSessionMode; readonly maxQueued: number };
|
|
202
|
-
readonly toolNames: readonly string[];
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const VALID_THINKING_LEVELS: readonly ThinkingLevel[] = [
|
|
206
|
-
"none",
|
|
207
|
-
"dynamic",
|
|
208
|
-
"minimal",
|
|
209
|
-
"low",
|
|
210
|
-
"medium",
|
|
211
|
-
"high",
|
|
212
|
-
"xhigh",
|
|
213
|
-
"max",
|
|
214
|
-
];
|
|
215
|
-
|
|
216
|
-
const VALID_MIDSESSION_MODES: readonly MidSessionMode[] = ["steer", "queue", "auto"];
|
|
217
|
-
|
|
218
|
-
/** Local name normalization mirroring `normalizeToolName` (no import cycle, Q-32). */
|
|
219
|
-
function normalizeConfigToolName(name: string): string {
|
|
220
|
-
return name
|
|
221
|
-
.trim()
|
|
222
|
-
.replace(/^(?:functions?|tools?)\./i, "")
|
|
223
|
-
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
|
224
|
-
.replace(/[\s-]+/g, "_")
|
|
225
|
-
.toLowerCase();
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Single config resolution (Q-32). Validates `model`, `thinkingLevel`,
|
|
230
|
-
* `maxTurns`, `midSession`, and tool-name collisions, collecting EVERY issue
|
|
231
|
-
* and throwing one `ConfigError` listing all of them. Not wired into `Agent`
|
|
232
|
-
* (other owner) — call it from setup paths that want fail-fast validation.
|
|
233
|
-
*
|
|
234
|
-
* @example `const cfg = resolveAgentConfig({ model, maxTurns: 10 });`
|
|
235
|
-
*/
|
|
236
|
-
export function resolveAgentConfig(config: AgentConfig): Readonly<ResolvedAgentConfig> {
|
|
237
|
-
const issues: string[] = [];
|
|
238
|
-
const cfg = (config ?? {}) as AgentConfig;
|
|
239
|
-
|
|
240
|
-
let model: NonNullable<AgentConfig["model"]> | undefined;
|
|
241
|
-
const rawModel = cfg.model;
|
|
242
|
-
if (typeof rawModel === "string") {
|
|
243
|
-
if (rawModel.trim()) model = rawModel;
|
|
244
|
-
else issues.push("model: must be a non-empty string | ModelSpec | ModelProviderInstance.");
|
|
245
|
-
} else if (rawModel && typeof rawModel === "object") {
|
|
246
|
-
model = rawModel as NonNullable<AgentConfig["model"]>;
|
|
247
|
-
} else {
|
|
248
|
-
issues.push("model: is required (string | ModelSpec | ModelProviderInstance).");
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
let thinkingLevel: ThinkingLevel | undefined;
|
|
252
|
-
if (cfg.thinkingLevel !== undefined) {
|
|
253
|
-
if ((VALID_THINKING_LEVELS as readonly string[]).includes(cfg.thinkingLevel)) {
|
|
254
|
-
thinkingLevel = cfg.thinkingLevel;
|
|
255
|
-
} else {
|
|
256
|
-
issues.push(
|
|
257
|
-
`thinkingLevel: invalid '${cfg.thinkingLevel}'. Expected one of: ${VALID_THINKING_LEVELS.join(", ")}.`
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
let maxTurns = Infinity;
|
|
263
|
-
if (cfg.maxTurns !== undefined) {
|
|
264
|
-
const m = cfg.maxTurns;
|
|
265
|
-
if (m === 0) {
|
|
266
|
-
maxTurns = Infinity;
|
|
267
|
-
} else if (m === Infinity) {
|
|
268
|
-
maxTurns = Infinity;
|
|
269
|
-
} else if (typeof m === "number" && Number.isFinite(m) && m >= 1) {
|
|
270
|
-
maxTurns = Math.floor(m);
|
|
271
|
-
} else {
|
|
272
|
-
issues.push(`maxTurns: must be finite >= 1 or Infinity (0/omitted means unlimited). Received: ${String(m)}.`);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
let mode: MidSessionMode = "auto";
|
|
277
|
-
if (cfg.midSession?.mode !== undefined) {
|
|
278
|
-
if ((VALID_MIDSESSION_MODES as readonly string[]).includes(cfg.midSession.mode as string)) {
|
|
279
|
-
mode = cfg.midSession.mode as MidSessionMode;
|
|
280
|
-
} else {
|
|
281
|
-
issues.push(
|
|
282
|
-
`midSession.mode: invalid '${String(cfg.midSession.mode)}'. Expected one of: ${VALID_MIDSESSION_MODES.join(", ")}.`
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
let maxQueued = 20;
|
|
287
|
-
if (cfg.midSession?.maxQueued !== undefined) {
|
|
288
|
-
const q = cfg.midSession.maxQueued;
|
|
289
|
-
if (typeof q === "number" && Number.isFinite(q) && Math.floor(q) === q && q >= 0) {
|
|
290
|
-
maxQueued = q;
|
|
291
|
-
} else {
|
|
292
|
-
issues.push(`midSession.maxQueued: must be an integer >= 0. Received: ${String(q)}.`);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const toolNames: string[] = [];
|
|
297
|
-
const tools = cfg.tools;
|
|
298
|
-
if (tools !== undefined) {
|
|
299
|
-
const entries: Array<[string, string]> = [];
|
|
300
|
-
if (Array.isArray(tools)) {
|
|
301
|
-
for (const def of tools) {
|
|
302
|
-
const declared = (def as { name?: unknown })?.name;
|
|
303
|
-
if (typeof declared === "string" && declared.trim()) {
|
|
304
|
-
toolNames.push(declared);
|
|
305
|
-
entries.push([declared, declared]);
|
|
306
|
-
} else {
|
|
307
|
-
issues.push("tools: array entries must declare a non-empty `name`.");
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
} else if (tools && typeof tools === "object") {
|
|
311
|
-
for (const [key, def] of Object.entries(tools)) {
|
|
312
|
-
toolNames.push(key);
|
|
313
|
-
const declared = (def as { name?: unknown })?.name;
|
|
314
|
-
entries.push([key, typeof declared === "string" && declared ? declared : key]);
|
|
315
|
-
}
|
|
316
|
-
} else {
|
|
317
|
-
issues.push("tools: must be a record or array of ToolDefinitions.");
|
|
318
|
-
}
|
|
319
|
-
const seen = new Map<string, string>();
|
|
320
|
-
for (const [key, declared] of entries) {
|
|
321
|
-
for (const candidate of new Set([key, declared])) {
|
|
322
|
-
const normalized = normalizeConfigToolName(String(candidate));
|
|
323
|
-
const prev = seen.get(normalized);
|
|
324
|
-
if (prev !== undefined && prev !== key) {
|
|
325
|
-
issues.push(
|
|
326
|
-
`tools: name collision: '${prev}' and '${key}' both normalize to '${normalized}'. Rename one tool.`
|
|
327
|
-
);
|
|
328
|
-
} else if (prev === undefined) {
|
|
329
|
-
seen.set(normalized, key);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (issues.length > 0) {
|
|
336
|
-
throw new ConfigError(`Invalid Agent config (${issues.length} issue${issues.length === 1 ? "" : "s"}): ${issues.join(" ")}`);
|
|
337
|
-
}
|
|
338
|
-
return {
|
|
339
|
-
model: model as NonNullable<AgentConfig["model"]>,
|
|
340
|
-
...(thinkingLevel !== undefined ? { thinkingLevel } : {}),
|
|
341
|
-
maxTurns,
|
|
342
|
-
midSession: { mode, maxQueued },
|
|
343
|
-
toolNames,
|
|
344
|
-
};
|
|
345
|
-
}
|