agent-accelerator 0.1.0 → 0.3.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 +174 -123
- package/{SYSTEM_PROMPT_AGENT.md → examples/prompts/SYSTEM_PROMPT_AGENT.md} +1 -1
- package/package.json +14 -9
- package/src/agent/agent.ts +385 -73
- package/src/agent/context.ts +1 -0
- package/src/agent/delegation.ts +443 -30
- package/src/agent/loop.ts +183 -53
- package/src/data/README.md +6 -6
- package/src/index.ts +163 -46
- package/src/models/catalog-cache.ts +60 -9
- package/src/models/catalog.ts +55 -8
- package/src/providers/google.ts +926 -0
- package/src/providers/openai-compat.ts +1147 -0
- package/src/providers/openai.ts +959 -0
- package/src/providers/openrouter-responses.ts +949 -0
- package/src/providers/openrouter.ts +1037 -0
- package/src/{ai-sdk → providers}/registry.ts +43 -55
- package/src/providers.ts +492 -0
- package/src/session/store.ts +806 -0
- package/src/streaming/sse-parser.ts +6 -4
- package/src/tools/executor.ts +23 -7
- package/src/tools/schema.ts +21 -11
- package/src/types/agent.ts +33 -2
- package/src/types/core.ts +3 -2
- package/src/types/message.ts +5 -0
- package/src/types/model.ts +3 -7
- package/src/types/provider-payloads.ts +2 -84
- package/src/types/response.ts +24 -0
- package/src/types/tool.ts +8 -0
- package/src/update-models.ts +56 -0
- package/src/utils/cache.ts +1 -1
- package/src/utils/documents.ts +517 -0
- package/src/utils/env.ts +0 -7
- package/src/{ai-sdk → utils}/errors.ts +61 -2
- package/src/utils/headers.ts +10 -20
- package/src/utils/media.ts +5 -2
- package/src/utils/retry.ts +89 -0
- package/src/utils/serialization.ts +15 -0
- package/src/utils/session.ts +36 -0
- package/src/ai-sdk/converters.ts +0 -342
- package/src/ai-sdk/executor.ts +0 -454
- package/src/ai-sdk/index.ts +0 -55
- package/src/ai-sdk/model-provider.ts +0 -303
- package/src/ai-sdk/options.ts +0 -306
- package/src/ai-sdk/provider.ts +0 -415
- package/src/tokens/counter.ts +0 -136
- /package/{SYSTEM_PROMPT.md → examples/prompts/SYSTEM_PROMPT.md} +0 -0
- /package/{SYSTEM_PROMPT_TOOLS.md → examples/prompts/SYSTEM_PROMPT_TOOLS.md} +0 -0
|
@@ -56,8 +56,9 @@ export class SSEParser {
|
|
|
56
56
|
// Comment / ping
|
|
57
57
|
continue;
|
|
58
58
|
} else if (line.startsWith("data:")) {
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
// Per WHATWG SSE, strip exactly one leading space — never tabs or
|
|
60
|
+
// further whitespace, so indented code/JSON payloads survive intact.
|
|
61
|
+
this.currentData.push(line.startsWith("data: ") ? line.slice(6) : line.slice(5));
|
|
61
62
|
} else if (line.startsWith("event:")) {
|
|
62
63
|
const val = line.slice(6);
|
|
63
64
|
this.currentEvent = (val.startsWith(" ") ? val.slice(1) : val).trim();
|
|
@@ -76,8 +77,9 @@ export class SSEParser {
|
|
|
76
77
|
const messages: SSEMessage[] = [];
|
|
77
78
|
// S7: flush pending currentData first, then treat remaining buffer as final lines (avoid duplication)
|
|
78
79
|
if (this.buffer.trim() !== "") {
|
|
79
|
-
//
|
|
80
|
-
|
|
80
|
+
// Terminate the leftover buffer in place: feed() appends its argument
|
|
81
|
+
// to this.buffer, so re-feeding the buffer itself would duplicate it.
|
|
82
|
+
const pending = this.feed("\n\n");
|
|
81
83
|
messages.push(...pending);
|
|
82
84
|
this.buffer = "";
|
|
83
85
|
}
|
package/src/tools/executor.ts
CHANGED
|
@@ -13,6 +13,8 @@ export interface ExecuteToolsOptions {
|
|
|
13
13
|
parallel?: boolean;
|
|
14
14
|
signal?: AbortSignal;
|
|
15
15
|
sessionId?: string;
|
|
16
|
+
/** Forwards live sub-agent deltas (realtime worker thinking/text) to the caller. */
|
|
17
|
+
onSubagentEvent?: (event: { trackingId: string; delta?: string; thinkingDelta?: string; partialText?: string; partialThinking?: string }) => void;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
const DEFAULT_TOOL_CONCURRENCY = 8;
|
|
@@ -36,7 +38,8 @@ function integerOption(value: number | string | undefined): number | undefined {
|
|
|
36
38
|
return parsed === undefined ? undefined : Math.floor(parsed);
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
/** Normalizes a tool name for case/format-insensitive matching (shared with delegation grants). */
|
|
42
|
+
export function normalizeToolName(name: string): string {
|
|
40
43
|
return name
|
|
41
44
|
.trim()
|
|
42
45
|
.replace(/^(?:functions?|tools?)\./i, "")
|
|
@@ -99,6 +102,7 @@ class Semaphore {
|
|
|
99
102
|
resolve: (release: () => void) => void;
|
|
100
103
|
reject: (error: Error) => void;
|
|
101
104
|
signal?: AbortSignal;
|
|
105
|
+
onAbort?: () => void;
|
|
102
106
|
}> = [];
|
|
103
107
|
|
|
104
108
|
constructor(private readonly limit: number) {}
|
|
@@ -110,13 +114,19 @@ class Semaphore {
|
|
|
110
114
|
return Promise.resolve(() => this.release());
|
|
111
115
|
}
|
|
112
116
|
return new Promise((resolve, reject) => {
|
|
113
|
-
const waiter
|
|
114
|
-
|
|
117
|
+
const waiter: {
|
|
118
|
+
resolve: (release: () => void) => void;
|
|
119
|
+
reject: (error: Error) => void;
|
|
120
|
+
signal?: AbortSignal;
|
|
121
|
+
onAbort?: () => void;
|
|
122
|
+
} = { resolve, reject, signal };
|
|
115
123
|
const onAbort = () => {
|
|
116
124
|
const index = this.waiters.indexOf(waiter);
|
|
117
125
|
if (index >= 0) this.waiters.splice(index, 1);
|
|
118
126
|
reject(new Error("Tool execution aborted"));
|
|
119
127
|
};
|
|
128
|
+
waiter.onAbort = onAbort;
|
|
129
|
+
this.waiters.push(waiter);
|
|
120
130
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
121
131
|
});
|
|
122
132
|
}
|
|
@@ -130,6 +140,9 @@ class Semaphore {
|
|
|
130
140
|
continue;
|
|
131
141
|
}
|
|
132
142
|
this.active++;
|
|
143
|
+
// The waiter is leaving the queue: drop its abort listener so long-lived
|
|
144
|
+
// session signals don't accumulate one listener per queued tool call.
|
|
145
|
+
if (waiter.onAbort) waiter.signal?.removeEventListener("abort", waiter.onAbort);
|
|
133
146
|
waiter.resolve(() => this.release());
|
|
134
147
|
return;
|
|
135
148
|
}
|
|
@@ -222,7 +235,7 @@ async function executeAttempt(
|
|
|
222
235
|
export async function executeToolCalls(
|
|
223
236
|
options: ExecuteToolsOptions
|
|
224
237
|
): Promise<ToolResultRecord[]> {
|
|
225
|
-
const { tools, toolCalls, agentName, parallel = true, signal, sessionId } = options;
|
|
238
|
+
const { tools, toolCalls, agentName, parallel = true, signal, sessionId, onSubagentEvent } = options;
|
|
226
239
|
const globalSemaphore = new Semaphore(DEFAULT_TOOL_CONCURRENCY);
|
|
227
240
|
const perToolSemaphores = new Map<string, Semaphore>();
|
|
228
241
|
|
|
@@ -271,6 +284,7 @@ export async function executeToolCalls(
|
|
|
271
284
|
agentName,
|
|
272
285
|
signal,
|
|
273
286
|
sessionId,
|
|
287
|
+
onSubagentEvent,
|
|
274
288
|
};
|
|
275
289
|
|
|
276
290
|
// Validate input if Zod schema is provided
|
|
@@ -306,9 +320,11 @@ export async function executeToolCalls(
|
|
|
306
320
|
}
|
|
307
321
|
|
|
308
322
|
const configuredTries = integerOption(toolDef.maxTries);
|
|
309
|
-
// A positive value is the total attempt count.
|
|
310
|
-
//
|
|
311
|
-
|
|
323
|
+
// A positive value is the total attempt count. 0/omitted falls back to
|
|
324
|
+
// a bounded default: an unbounded retry loop on a persistently failing
|
|
325
|
+
// dependency (e.g. steady 503/429) would hang the agent loop forever,
|
|
326
|
+
// so callers opt into more attempts explicitly via maxTries.
|
|
327
|
+
const maxAttempts = configuredTries && configuredTries > 0 ? configuredTries : 3;
|
|
312
328
|
const timeoutMs = numericOption(toolDef.timeoutMs);
|
|
313
329
|
// Telemetry starts when the tool body is about to run, excluding queue
|
|
314
330
|
// wait and schema validation. Retries/backoff remain part of this call.
|
package/src/tools/schema.ts
CHANGED
|
@@ -65,7 +65,7 @@ export function zodToJsonSchema(schema: unknown): Record<string, unknown> {
|
|
|
65
65
|
*
|
|
66
66
|
* @example `const clean = cleanJsonSchema(rawSchema);`
|
|
67
67
|
*/
|
|
68
|
-
export function cleanJsonSchema(schema: any, rootDefs?: Record<string, any>): Record<string, unknown> {
|
|
68
|
+
export function cleanJsonSchema(schema: any, rootDefs?: Record<string, any>, seenRefs: Set<string> = new Set()): Record<string, unknown> {
|
|
69
69
|
if (typeof schema !== "object" || schema === null) {
|
|
70
70
|
return schema;
|
|
71
71
|
}
|
|
@@ -78,8 +78,18 @@ export function cleanJsonSchema(schema: any, rootDefs?: Record<string, any>): Re
|
|
|
78
78
|
if (target) {
|
|
79
79
|
// Merge sibling props (e.g. description) with target
|
|
80
80
|
const { $ref, ...siblings } = schema;
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
if (seenRefs.has(refName)) {
|
|
82
|
+
// Recursive schema (AST nodes, trees, nested categories): stop
|
|
83
|
+
// expanding to avoid overflowing the stack; keep siblings.
|
|
84
|
+
return { type: "object", ...cleanJsonSchema(siblings, defs, seenRefs) } as any;
|
|
85
|
+
}
|
|
86
|
+
seenRefs.add(refName);
|
|
87
|
+
try {
|
|
88
|
+
const resolved = cleanJsonSchema(target, defs, seenRefs);
|
|
89
|
+
return { ...resolved, ...cleanJsonSchema(siblings, defs, seenRefs) } as any;
|
|
90
|
+
} finally {
|
|
91
|
+
seenRefs.delete(refName);
|
|
92
|
+
}
|
|
83
93
|
}
|
|
84
94
|
}
|
|
85
95
|
const { $schema, $defs, definitions, ...rest } = schema;
|
|
@@ -94,22 +104,22 @@ export function cleanJsonSchema(schema: any, rootDefs?: Record<string, any>): Re
|
|
|
94
104
|
if (rest.properties && typeof rest.properties === "object") {
|
|
95
105
|
const cleanedProps: Record<string, unknown> = {};
|
|
96
106
|
for (const [key, value] of Object.entries(rest.properties)) {
|
|
97
|
-
cleanedProps[key] = cleanJsonSchema(value, defs);
|
|
107
|
+
cleanedProps[key] = cleanJsonSchema(value, defs, seenRefs);
|
|
98
108
|
}
|
|
99
109
|
rest.properties = cleanedProps;
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
if (rest.items) {
|
|
103
|
-
rest.items = cleanJsonSchema(rest.items, defs);
|
|
113
|
+
rest.items = cleanJsonSchema(rest.items, defs, seenRefs);
|
|
104
114
|
}
|
|
105
|
-
if (rest.anyOf) rest.anyOf = (rest.anyOf as any[]).map((v: any) => cleanJsonSchema(v, defs));
|
|
106
|
-
if (rest.oneOf) rest.oneOf = (rest.oneOf as any[]).map((v: any) => cleanJsonSchema(v, defs));
|
|
107
|
-
if (rest.allOf) rest.allOf = (rest.allOf as any[]).map((v: any) => cleanJsonSchema(v, defs));
|
|
108
|
-
if (rest.prefixItems) rest.prefixItems = (rest.prefixItems as any[]).map((v: any) => cleanJsonSchema(v, defs));
|
|
115
|
+
if (rest.anyOf) rest.anyOf = (rest.anyOf as any[]).map((v: any) => cleanJsonSchema(v, defs, seenRefs));
|
|
116
|
+
if (rest.oneOf) rest.oneOf = (rest.oneOf as any[]).map((v: any) => cleanJsonSchema(v, defs, seenRefs));
|
|
117
|
+
if (rest.allOf) rest.allOf = (rest.allOf as any[]).map((v: any) => cleanJsonSchema(v, defs, seenRefs));
|
|
118
|
+
if (rest.prefixItems) rest.prefixItems = (rest.prefixItems as any[]).map((v: any) => cleanJsonSchema(v, defs, seenRefs));
|
|
109
119
|
// Recursively clean nested $ref inside properties that were not top-level
|
|
110
120
|
for (const k of Object.keys(rest)) {
|
|
111
121
|
if (rest[k] && typeof rest[k] === "object" && !Array.isArray(rest[k]) && (rest[k] as any).$ref) {
|
|
112
|
-
rest[k] = cleanJsonSchema(rest[k], defs);
|
|
122
|
+
rest[k] = cleanJsonSchema(rest[k], defs, seenRefs);
|
|
113
123
|
}
|
|
114
124
|
}
|
|
115
125
|
return rest;
|
|
@@ -167,7 +177,7 @@ function inferZodPropertyType(prop: any): Record<string, unknown> {
|
|
|
167
177
|
}
|
|
168
178
|
if (tn.includes("literal")) {
|
|
169
179
|
const val = unwrapped._def?.value;
|
|
170
|
-
return { type: typeof val, enum: [val], ...(description ? { description } : {}) };
|
|
180
|
+
return { type: val === null ? "null" : typeof val, enum: [val], ...(description ? { description } : {}) };
|
|
171
181
|
}
|
|
172
182
|
if (tn.includes("array") || tn === "zodarray") {
|
|
173
183
|
const elem = unwrapped._def?.type || unwrapped._def?.element || unwrapped._def?.valueType || {};
|
package/src/types/agent.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
CacheConfig,
|
|
6
6
|
ServiceTier,
|
|
7
7
|
} from "./core.ts";
|
|
8
|
-
import type { ModelProviderInstance } from "../
|
|
8
|
+
import type { ModelProviderInstance } from "../providers/registry.ts";
|
|
9
9
|
import type { Agent } from "../agent/agent.ts";
|
|
10
10
|
|
|
11
11
|
/** Configuration used to construct an {@link Agent}. */
|
|
@@ -34,16 +34,31 @@ export interface AgentConfig {
|
|
|
34
34
|
cache?: CacheConfig;
|
|
35
35
|
/** Provider service tier, when supported. */
|
|
36
36
|
serviceTier?: ServiceTier;
|
|
37
|
+
/**
|
|
38
|
+
* When true, `file` parts are converted client-side to `<Document>` Markdown
|
|
39
|
+
* for models lacking native support (capable models still receive files
|
|
40
|
+
* natively; unknown models count as capable). Also auto-registers the
|
|
41
|
+
* `convert_document_to_markdown` tool for path/URL mentions in plain text.
|
|
42
|
+
*/
|
|
43
|
+
bypassInputFileModality?: boolean;
|
|
37
44
|
/** Stable conversation/cache session ID. */
|
|
38
45
|
sessionId?: string;
|
|
39
46
|
/** Headers merged into every provider request. */
|
|
40
47
|
headers?: Record<string, string>;
|
|
41
|
-
/** Maximum model/tool turns per run.
|
|
48
|
+
/** Maximum model/tool turns per run. `0` or omitted means infinite. */
|
|
42
49
|
maxTurns?: number;
|
|
43
50
|
/** Fixed worker agents exposed as delegation tools. */
|
|
44
51
|
subagents?: (Agent | { name: string; description: string; agent: Agent })[];
|
|
45
52
|
/** Clears conversation state before and after every run. */
|
|
46
53
|
stateless?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Durable session persistence: when set, the session file is rewritten
|
|
56
|
+
* after every step (user turn, assistant turn, tool batch, sub-agent
|
|
57
|
+
* step) so a crash loses at most the in-flight step. Pass `{ dir }` for
|
|
58
|
+
* `sessions/<sessionId>/session.json` layout or `{ file }` for a single
|
|
59
|
+
* pretty `.session.json` file. Writes are atomic and never fail a turn.
|
|
60
|
+
*/
|
|
61
|
+
persist?: { dir?: string; file?: string };
|
|
47
62
|
}
|
|
48
63
|
|
|
49
64
|
/** Developer-configured policy for LLM-spawned dynamic sub-agents. */
|
|
@@ -84,4 +99,20 @@ export interface AgentRunOptions {
|
|
|
84
99
|
onEvent?: (event: import("./response.ts").StreamEvent) => void;
|
|
85
100
|
/** When true, wraps reasoning stream as <think>\n...\n</think>\n\n — no manual isThinking needed. */
|
|
86
101
|
wrapThinking?: boolean;
|
|
102
|
+
/** Called after every model/tool turn with a turn summary (also drives sub-agent step logs + durable persistence). */
|
|
103
|
+
onTurn?: (turn: AgentTurnEvent) => void;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Turn summary emitted to `AgentRunOptions.onTurn` after each loop turn. */
|
|
107
|
+
export interface AgentTurnEvent {
|
|
108
|
+
/** 1-based turn count within this run. */
|
|
109
|
+
turns: number;
|
|
110
|
+
/** Assistant text produced this turn, if any. */
|
|
111
|
+
text?: string;
|
|
112
|
+
/** Reasoning produced this turn, if any. */
|
|
113
|
+
thinking?: string;
|
|
114
|
+
/** Tool calls requested this turn, if any. */
|
|
115
|
+
toolCalls?: Array<{ id: string; name: string }>;
|
|
116
|
+
/** Tool results executed this turn, if any. */
|
|
117
|
+
toolResults?: Array<{ id: string; name: string; isError?: boolean }>;
|
|
87
118
|
}
|
package/src/types/core.ts
CHANGED
|
@@ -9,7 +9,8 @@ export type ThinkingLevel =
|
|
|
9
9
|
| "low"
|
|
10
10
|
| "medium"
|
|
11
11
|
| "high"
|
|
12
|
-
| "xhigh"
|
|
12
|
+
| "xhigh"
|
|
13
|
+
| "max";
|
|
13
14
|
|
|
14
15
|
// Internal normalized config (SDK-internal, not exposed as Agent flag)
|
|
15
16
|
/** Internal normalized reasoning configuration passed to providers. */
|
|
@@ -49,7 +50,7 @@ export interface CacheConfig {
|
|
|
49
50
|
*/
|
|
50
51
|
cachedContentId?: string;
|
|
51
52
|
/**
|
|
52
|
-
* Session ID for cache affinity routing (e.g. x-session-id
|
|
53
|
+
* Session ID for cache affinity routing (e.g. x-session-id)
|
|
53
54
|
*/
|
|
54
55
|
sessionId?: string;
|
|
55
56
|
/**
|
package/src/types/message.ts
CHANGED
|
@@ -64,6 +64,11 @@ export interface ToolCallPart {
|
|
|
64
64
|
* Optional Gemini thought signature associated with this tool call
|
|
65
65
|
*/
|
|
66
66
|
thoughtSignature?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Provider pairing identifier (e.g. Responses `call_id`), preserved so
|
|
69
|
+
* history builders can pair results without inventing ids.
|
|
70
|
+
*/
|
|
71
|
+
callId?: string;
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
/** Tool execution result returned to the model context. */
|
package/src/types/model.ts
CHANGED
|
@@ -5,10 +5,10 @@ import type { AssistantMessageEventStream } from "../streaming/event-stream.ts";
|
|
|
5
5
|
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
7
|
// Provider identity — battle-tested: matches models.dev provider keys
|
|
8
|
-
// Known first-class: google |
|
|
8
|
+
// Known first-class: google | openrouter | openai, plus aliases. Allow string for future.
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
/** Supported provider IDs plus arbitrary custom prefixes. */
|
|
11
|
-
export type ProviderId = "google" | "
|
|
11
|
+
export type ProviderId = "google" | "openrouter" | "openai" | (string & {});
|
|
12
12
|
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
// Raw models.dev shapes — battle-tested, 1:1 with https://models.dev/api.json
|
|
@@ -189,7 +189,7 @@ export interface ProviderGenerateResult {
|
|
|
189
189
|
durationMs: number;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
/** Provider contract implemented by
|
|
192
|
+
/** Provider contract implemented by native REST transports. */
|
|
193
193
|
export interface Provider {
|
|
194
194
|
id: ProviderId;
|
|
195
195
|
name: string;
|
|
@@ -205,8 +205,4 @@ export interface Provider {
|
|
|
205
205
|
context: ProviderContext,
|
|
206
206
|
options?: ProviderRequestOptions
|
|
207
207
|
): AssistantMessageEventStream;
|
|
208
|
-
countTokens(
|
|
209
|
-
model: string | ModelSpec,
|
|
210
|
-
context: ProviderContext
|
|
211
|
-
): Promise<number>;
|
|
212
208
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provider wire payload and schema types for complete backwards compatibility.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* These types allow existing applications and tests to type-check requests
|
|
4
|
+
* and responses seamlessly.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
// ============================================================================
|
|
@@ -105,88 +105,6 @@ export interface GoogleGenerateContentResponse {
|
|
|
105
105
|
modelVersion?: string;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
// ============================================================================
|
|
109
|
-
// OpenCode Payload Types
|
|
110
|
-
// ============================================================================
|
|
111
|
-
|
|
112
|
-
export type OpenCodeReasoningEffort = "low" | "medium" | "high";
|
|
113
|
-
export type OpenCodePromptCacheRetention = "none" | "1h" | "24h";
|
|
114
|
-
|
|
115
|
-
export interface OpenCodeCacheControl {
|
|
116
|
-
type: "ephemeral";
|
|
117
|
-
ttl?: "1h";
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export interface OpenCodeUsage {
|
|
121
|
-
prompt_tokens?: number;
|
|
122
|
-
completion_tokens?: number;
|
|
123
|
-
total_tokens?: number;
|
|
124
|
-
prompt_tokens_details?: {
|
|
125
|
-
cached_tokens?: number;
|
|
126
|
-
cache_read?: number;
|
|
127
|
-
cache_creation?: number;
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export interface OpenCodeChatRequest {
|
|
132
|
-
model: string;
|
|
133
|
-
messages: any[];
|
|
134
|
-
tools?: any[];
|
|
135
|
-
tool_choice?: any;
|
|
136
|
-
temperature?: number;
|
|
137
|
-
top_p?: number;
|
|
138
|
-
max_tokens?: number;
|
|
139
|
-
stop?: string | string[];
|
|
140
|
-
stream?: boolean;
|
|
141
|
-
reasoning_effort?: OpenCodeReasoningEffort;
|
|
142
|
-
prompt_cache_retention?: OpenCodePromptCacheRetention;
|
|
143
|
-
session_id?: string;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export interface OpenCodeChatResponse {
|
|
147
|
-
id?: string;
|
|
148
|
-
object?: string;
|
|
149
|
-
created?: number;
|
|
150
|
-
model?: string;
|
|
151
|
-
choices?: Array<{
|
|
152
|
-
index?: number;
|
|
153
|
-
message?: any;
|
|
154
|
-
delta?: any;
|
|
155
|
-
finish_reason?: string;
|
|
156
|
-
}>;
|
|
157
|
-
usage?: OpenCodeUsage;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export interface OpenCodeResponsesInputItem {
|
|
161
|
-
role: string;
|
|
162
|
-
content: any;
|
|
163
|
-
cache_control?: OpenCodeCacheControl;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export interface OpenCodeResponsesOutputItem {
|
|
167
|
-
id?: string;
|
|
168
|
-
type?: string;
|
|
169
|
-
status?: string;
|
|
170
|
-
content?: any;
|
|
171
|
-
name?: string;
|
|
172
|
-
call_id?: string;
|
|
173
|
-
arguments?: string;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export interface OpenCodeResponsesRequest {
|
|
177
|
-
model: string;
|
|
178
|
-
input: OpenCodeResponsesInputItem[];
|
|
179
|
-
tools?: any[];
|
|
180
|
-
parameters?: Record<string, unknown>;
|
|
181
|
-
session_id?: string;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export interface OpenCodeResponsesResponse {
|
|
185
|
-
id?: string;
|
|
186
|
-
output?: OpenCodeResponsesOutputItem[];
|
|
187
|
-
usage?: OpenCodeUsage;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
108
|
// ============================================================================
|
|
191
109
|
// OpenRouter Payload Types
|
|
192
110
|
// ============================================================================
|
package/src/types/response.ts
CHANGED
|
@@ -2,6 +2,19 @@ import type { TokenUsage } from "./core.ts";
|
|
|
2
2
|
import type { ToolCallRecord, ToolResultRecord } from "./tool.ts";
|
|
3
3
|
import type { ProviderRawData, ProviderId } from "./model.ts";
|
|
4
4
|
|
|
5
|
+
/** One logged step inside a sub-agent worker run (surfaced via `agent.track(id)`). */
|
|
6
|
+
export interface SubAgentStep {
|
|
7
|
+
turn: number;
|
|
8
|
+
type: "assistant" | "tool_call" | "tool_result";
|
|
9
|
+
name?: string;
|
|
10
|
+
text?: string;
|
|
11
|
+
thinking?: string;
|
|
12
|
+
isError?: boolean;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
/** True while the worker is still streaming this step; finalized on turn end. */
|
|
15
|
+
partial?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
/** Per-worker result and usage metadata attached to an AgentResponse. */
|
|
6
19
|
export interface SubAgentExecutionMetadata {
|
|
7
20
|
name: string;
|
|
@@ -20,6 +33,14 @@ export interface SubAgentExecutionMetadata {
|
|
|
20
33
|
raw?: ProviderRawData;
|
|
21
34
|
isError?: boolean;
|
|
22
35
|
error?: string;
|
|
36
|
+
/** Stable display id: `SUBAGENT-NAME-{TrackingID}` (32-char hex, see `createTrackingId`). */
|
|
37
|
+
trackingId?: string;
|
|
38
|
+
/** Provider session id used by this worker (≤64 chars, affinity-safe). */
|
|
39
|
+
sessionId?: string;
|
|
40
|
+
/** Parent session this worker was spawned from. */
|
|
41
|
+
parentSessionId?: string;
|
|
42
|
+
/** Ordered step log for this worker (also persisted in the session file). */
|
|
43
|
+
steps?: SubAgentStep[];
|
|
23
44
|
}
|
|
24
45
|
|
|
25
46
|
/** JSON representation returned by AgentResponse.toJSON(). */
|
|
@@ -136,6 +157,7 @@ export type StreamEventType =
|
|
|
136
157
|
| "tool_call_complete"
|
|
137
158
|
| "tool_result"
|
|
138
159
|
| "subagent_complete"
|
|
160
|
+
| "subagent_delta"
|
|
139
161
|
| "usage"
|
|
140
162
|
| "done"
|
|
141
163
|
| "error";
|
|
@@ -148,6 +170,8 @@ export interface StreamEvent {
|
|
|
148
170
|
toolCall?: ToolCallRecord;
|
|
149
171
|
toolResult?: ToolResultRecord;
|
|
150
172
|
subagent?: SubAgentExecutionMetadata;
|
|
173
|
+
/** Tracking id of the worker emitting a `subagent_delta` event. */
|
|
174
|
+
subagentTrackingId?: string;
|
|
151
175
|
usage?: TokenUsage;
|
|
152
176
|
responseId?: string;
|
|
153
177
|
finishReason?: string;
|
package/src/types/tool.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface ToolExecutionContext {
|
|
|
9
9
|
signal?: AbortSignal;
|
|
10
10
|
/** Session identifier associated with this invocation. */
|
|
11
11
|
sessionId?: string;
|
|
12
|
+
/** Forwards live sub-agent deltas (used by `spawn_subagents` for realtime tracking). */
|
|
13
|
+
onSubagentEvent?: (event: { trackingId: string; delta?: string; thinkingDelta?: string; partialText?: string; partialThinking?: string }) => void;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export type ToolExecuteFn<TInput = any, TOutput = any> = (
|
|
@@ -50,6 +52,12 @@ export interface ToolCallRecord {
|
|
|
50
52
|
arguments: Record<string, unknown>;
|
|
51
53
|
rawArguments?: string;
|
|
52
54
|
thoughtSignature?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Provider pairing identifier, distinct from the item `id` when the
|
|
57
|
+
* provider uses both (e.g. Responses `call_id` alongside the `fc_…` item
|
|
58
|
+
* id). Tool results pair against this when present.
|
|
59
|
+
*/
|
|
60
|
+
callId?: string;
|
|
53
61
|
}
|
|
54
62
|
|
|
55
63
|
export interface ToolResultRecord {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Developer script to manually download and refresh the models.dev catalog.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bun src/update-models.ts
|
|
7
|
+
* bun src/update-models.ts --force
|
|
8
|
+
* bun src/update-models.ts --ttl=24h
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { refreshModelCatalog, getCatalogStatus } from "./index.ts";
|
|
12
|
+
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const force = args.includes("--force") || !args.some((a) => a.startsWith("--ttl"));
|
|
15
|
+
|
|
16
|
+
let customTtlMs: number | undefined;
|
|
17
|
+
for (const arg of args) {
|
|
18
|
+
if (arg.startsWith("--ttl=")) {
|
|
19
|
+
const val = arg.slice(6);
|
|
20
|
+
if (val.endsWith("h")) {
|
|
21
|
+
customTtlMs = parseFloat(val) * 60 * 60 * 1000;
|
|
22
|
+
} else if (val.endsWith("m")) {
|
|
23
|
+
customTtlMs = parseFloat(val) * 60 * 1000;
|
|
24
|
+
} else {
|
|
25
|
+
customTtlMs = parseFloat(val);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log("\x1b[36m[Agent Accelerator]\x1b[0m Checking models catalog from https://models.dev/api.json...");
|
|
31
|
+
const startTime = Date.now();
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const result = await refreshModelCatalog({ force, ttlMs: customTtlMs });
|
|
35
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
36
|
+
const ttlHours = (result.ttlMs / (1000 * 60 * 60)).toFixed(1);
|
|
37
|
+
|
|
38
|
+
if (result.fromCache) {
|
|
39
|
+
console.log(
|
|
40
|
+
`\x1b[32m✓\x1b[0m Models catalog is already fresh (within ${ttlHours}h TTL).`
|
|
41
|
+
);
|
|
42
|
+
} else {
|
|
43
|
+
console.log(
|
|
44
|
+
`\x1b[32m✓\x1b[0m Successfully downloaded and cached \x1b[1m${result.modelCount.toLocaleString()}\x1b[0m models ` +
|
|
45
|
+
`across \x1b[1m${result.providerCount}\x1b[0m providers in ${duration}s.`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
console.log(` \x1b[90mCache location: ${result.cachePath}\x1b[0m`);
|
|
49
|
+
console.log(` \x1b[90mTTL: ${ttlHours} hours\x1b[0m`);
|
|
50
|
+
if (result.expiresAt) {
|
|
51
|
+
console.log(` \x1b[90mNext refresh after: ${new Date(result.expiresAt).toLocaleString()}\x1b[0m`);
|
|
52
|
+
}
|
|
53
|
+
} catch (error: any) {
|
|
54
|
+
console.error(`\x1b[31m✖ Failed to refresh model catalog:\x1b[0m`, error.message || error);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
package/src/utils/cache.ts
CHANGED
|
@@ -31,7 +31,7 @@ export function retentionToTtlSeconds(retention?: CacheRetention, ttlSeconds?: n
|
|
|
31
31
|
return undefined;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/** Maps retention settings to
|
|
34
|
+
/** Maps retention settings to OpenRouter prompt-cache TTL values. */
|
|
35
35
|
export function getPromptCacheRetention(retention?: CacheRetention, supportsLong = true): "24h" | "1h" | undefined {
|
|
36
36
|
if (!retention || retention === "implicit") return undefined;
|
|
37
37
|
if (retention === "long" && supportsLong) return "24h";
|