agent-accelerator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1220 -0
  3. package/SYSTEM_PROMPT.md +14 -0
  4. package/SYSTEM_PROMPT_AGENT.md +34 -0
  5. package/SYSTEM_PROMPT_TOOLS.md +9 -0
  6. package/bunfig.toml +2 -0
  7. package/package.json +59 -0
  8. package/src/agent/agent.ts +615 -0
  9. package/src/agent/context.ts +161 -0
  10. package/src/agent/delegation.ts +481 -0
  11. package/src/agent/loop.ts +569 -0
  12. package/src/agent/subagent.ts +83 -0
  13. package/src/ai-sdk/converters.ts +342 -0
  14. package/src/ai-sdk/errors.ts +122 -0
  15. package/src/ai-sdk/executor.ts +454 -0
  16. package/src/ai-sdk/index.ts +55 -0
  17. package/src/ai-sdk/model-provider.ts +303 -0
  18. package/src/ai-sdk/options.ts +306 -0
  19. package/src/ai-sdk/provider.ts +415 -0
  20. package/src/ai-sdk/registry.ts +416 -0
  21. package/src/data/README.md +84 -0
  22. package/src/index.ts +190 -0
  23. package/src/models/catalog-cache.ts +273 -0
  24. package/src/models/catalog.ts +503 -0
  25. package/src/streaming/event-stream.ts +211 -0
  26. package/src/streaming/sse-parser.ts +97 -0
  27. package/src/tokens/counter.ts +136 -0
  28. package/src/tools/executor.ts +365 -0
  29. package/src/tools/schema.ts +221 -0
  30. package/src/tools/tool.ts +101 -0
  31. package/src/types/agent.ts +87 -0
  32. package/src/types/core.ts +86 -0
  33. package/src/types/message.ts +115 -0
  34. package/src/types/model.ts +212 -0
  35. package/src/types/provider-payloads.ts +434 -0
  36. package/src/types/response.ts +158 -0
  37. package/src/types/tool.ts +61 -0
  38. package/src/utils/base64.ts +27 -0
  39. package/src/utils/cache.ts +146 -0
  40. package/src/utils/env.ts +78 -0
  41. package/src/utils/headers.ts +110 -0
  42. package/src/utils/media.ts +137 -0
  43. package/src/utils/serialization.ts +91 -0
  44. package/src/utils/session.ts +26 -0
  45. package/src/utils/thought-signature.ts +27 -0
  46. package/tsconfig.json +31 -0
@@ -0,0 +1,273 @@
1
+ import * as path from "node:path";
2
+ import * as fs from "node:fs";
3
+
4
+ /** Default TTL: 12 Hours (in milliseconds). */
5
+ export const DEFAULT_CATALOG_TTL_MS = 12 * 60 * 60 * 1000;
6
+
7
+ let globalCatalogTtlMs = DEFAULT_CATALOG_TTL_MS;
8
+
9
+ /** Sets the global model catalog cache TTL in milliseconds. */
10
+ export function setCatalogTTL(ttlMs: number): void {
11
+ if (Number.isFinite(ttlMs) && ttlMs > 0) {
12
+ globalCatalogTtlMs = ttlMs;
13
+ }
14
+ }
15
+
16
+ /** Gets the currently configured global catalog TTL in milliseconds. */
17
+ export function getCatalogTTL(): number {
18
+ return globalCatalogTtlMs;
19
+ }
20
+
21
+ export interface CatalogCacheFile {
22
+ fetchedAt: number;
23
+ ttlMs: number;
24
+ source: string;
25
+ version: string;
26
+ data: Record<string, any>;
27
+ }
28
+
29
+ export interface CatalogStatus {
30
+ loaded: boolean;
31
+ fromCache: boolean;
32
+ isExpired: boolean;
33
+ fetchedAt?: number;
34
+ ttlMs: number;
35
+ expiresAt?: number;
36
+ cachePath: string;
37
+ providerCount: number;
38
+ modelCount: number;
39
+ }
40
+
41
+ export interface RefreshCatalogOptions {
42
+ /** Force re-download even if current cache is still within TTL. Default: false */
43
+ force?: boolean;
44
+ /** Custom TTL in milliseconds for this cache entry. Default: 12 hours */
45
+ ttlMs?: number;
46
+ /** Custom models endpoint URL. Default: "https://models.dev/api.json" */
47
+ apiUrl?: string;
48
+ /** Fetch timeout in milliseconds. Default: 30000 */
49
+ timeoutMs?: number;
50
+ }
51
+
52
+ type CatalogUpdateListener = () => void;
53
+ const updateListeners: Set<CatalogUpdateListener> = new Set();
54
+
55
+ export function registerCatalogUpdateListener(listener: CatalogUpdateListener): () => void {
56
+ updateListeners.add(listener);
57
+ return () => updateListeners.delete(listener);
58
+ }
59
+
60
+ function notifyUpdateListeners(): void {
61
+ for (const listener of updateListeners) {
62
+ try {
63
+ listener();
64
+ } catch {}
65
+ }
66
+ }
67
+
68
+ /** Non-configurable cache directory: src/data */
69
+ export function getCacheDir(): string {
70
+ return path.resolve(process.cwd(), "src/data");
71
+ }
72
+
73
+ /** Non-configurable cache file path: src/data/models-cache.json */
74
+ export function getCacheFilePath(): string {
75
+ return path.resolve(process.cwd(), "src/data/models-cache.json");
76
+ }
77
+
78
+ function readJsonFileSync(filePath: string): any {
79
+ try {
80
+ if (!fs.existsSync(filePath)) return null;
81
+ const content = fs.readFileSync(filePath, "utf-8");
82
+ return JSON.parse(content);
83
+ } catch {
84
+ return null;
85
+ }
86
+ }
87
+
88
+ function writeJsonFileSync(filePath: string, data: any): void {
89
+ const dir = path.dirname(filePath);
90
+ if (!fs.existsSync(dir)) {
91
+ fs.mkdirSync(dir, { recursive: true });
92
+ }
93
+ fs.writeFileSync(filePath, JSON.stringify(data), "utf-8");
94
+ }
95
+
96
+ // In-memory catalog state
97
+ let activeCatalog: Record<string, any> = {};
98
+ let activeFetchedAt: number | undefined = undefined;
99
+ let activeTtlMs: number = globalCatalogTtlMs;
100
+ let activeFromCache = false;
101
+
102
+ // Synchronous bootstrap: load from src/data/models-cache.json if present
103
+ function initializeCatalogSync(): void {
104
+ const cachePath = getCacheFilePath();
105
+ const cached = readJsonFileSync(cachePath);
106
+ if (cached && typeof cached === "object") {
107
+ if (cached.data && typeof cached.data === "object") {
108
+ activeCatalog = cached.data;
109
+ activeFetchedAt = cached.fetchedAt;
110
+ activeTtlMs = cached.ttlMs || globalCatalogTtlMs;
111
+ activeFromCache = true;
112
+ return;
113
+ }
114
+ if (cached.google || cached.openai) {
115
+ activeCatalog = cached;
116
+ activeFetchedAt = fs.statSync(cachePath).mtimeMs || Date.now();
117
+ activeTtlMs = globalCatalogTtlMs;
118
+ activeFromCache = true;
119
+ return;
120
+ }
121
+ }
122
+
123
+ activeCatalog = {};
124
+ activeFetchedAt = undefined;
125
+ activeTtlMs = globalCatalogTtlMs;
126
+ activeFromCache = false;
127
+ }
128
+
129
+ initializeCatalogSync();
130
+
131
+ /** Returns the active in-memory catalog data. */
132
+ export function getActiveCatalog(): Record<string, any> {
133
+ return activeCatalog;
134
+ }
135
+
136
+ /** Checks whether the active model catalog has expired based on its TTL. */
137
+ export function isCatalogExpired(): boolean {
138
+ if (!activeFetchedAt) return true;
139
+ const age = Date.now() - activeFetchedAt;
140
+ return age < 0 || age >= activeTtlMs;
141
+ }
142
+
143
+ /** Returns diagnostic metadata about the current model catalog status. */
144
+ export function getCatalogStatus(): CatalogStatus {
145
+ const providerCount = Object.keys(activeCatalog).length;
146
+ let modelCount = 0;
147
+ for (const prov of Object.values(activeCatalog)) {
148
+ if (prov && typeof prov === "object" && prov.models) {
149
+ modelCount += Object.keys(prov.models).length;
150
+ }
151
+ }
152
+
153
+ const isExpired = !activeFetchedAt || Date.now() - activeFetchedAt >= activeTtlMs;
154
+
155
+ return {
156
+ loaded: providerCount > 0,
157
+ fromCache: activeFromCache,
158
+ isExpired,
159
+ fetchedAt: activeFetchedAt,
160
+ ttlMs: activeTtlMs,
161
+ expiresAt: activeFetchedAt ? activeFetchedAt + activeTtlMs : undefined,
162
+ cachePath: getCacheFilePath(),
163
+ providerCount,
164
+ modelCount,
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Refreshes the model catalog by downloading from models.dev
170
+ * directly into src/data/models-cache.json with a 12-hour TTL.
171
+ */
172
+ export async function refreshModelCatalog(options: RefreshCatalogOptions = {}): Promise<CatalogStatus> {
173
+ const effectiveTtl = options.ttlMs ?? globalCatalogTtlMs;
174
+ const apiUrl = options.apiUrl || "https://models.dev/api.json";
175
+ const timeoutMs = options.timeoutMs ?? 30000;
176
+ const cachePath = getCacheFilePath();
177
+
178
+ // Return existing cache if still within TTL and not forcing
179
+ if (!options.force) {
180
+ const cached = readJsonFileSync(cachePath);
181
+ if (cached && typeof cached === "object" && cached.data) {
182
+ const age = Date.now() - (cached.fetchedAt || 0);
183
+ const fileTtl = cached.ttlMs || effectiveTtl;
184
+ if (age >= 0 && age < fileTtl) {
185
+ activeCatalog = cached.data;
186
+ activeFetchedAt = cached.fetchedAt;
187
+ activeTtlMs = fileTtl;
188
+ activeFromCache = true;
189
+ notifyUpdateListeners();
190
+ return getCatalogStatus();
191
+ }
192
+ }
193
+ }
194
+
195
+ // Fetch from models.dev
196
+ const controller = new AbortController();
197
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
198
+ let freshData: Record<string, any>;
199
+
200
+ try {
201
+ const res = await fetch(apiUrl, {
202
+ headers: {
203
+ Accept: "application/json",
204
+ "User-Agent": "agent-accelerator/1.0",
205
+ },
206
+ signal: controller.signal,
207
+ });
208
+
209
+ if (!res.ok) {
210
+ throw new Error(`HTTP ${res.status} ${res.statusText}`);
211
+ }
212
+
213
+ freshData = (await res.json()) as Record<string, any>;
214
+ } catch (err: any) {
215
+ clearTimeout(timer);
216
+ // If download fails, retain disk cache if available
217
+ const existing = readJsonFileSync(cachePath);
218
+ if (existing && existing.data) {
219
+ activeCatalog = existing.data;
220
+ activeFetchedAt = existing.fetchedAt;
221
+ activeTtlMs = existing.ttlMs || effectiveTtl;
222
+ activeFromCache = true;
223
+ notifyUpdateListeners();
224
+ }
225
+ return getCatalogStatus();
226
+ } finally {
227
+ clearTimeout(timer);
228
+ }
229
+
230
+ const fetchedAt = Date.now();
231
+ const cachePayload: CatalogCacheFile = {
232
+ fetchedAt,
233
+ ttlMs: effectiveTtl,
234
+ source: apiUrl,
235
+ version: "1.0",
236
+ data: freshData,
237
+ };
238
+
239
+ // Save to src/data/models-cache.json
240
+ writeJsonFileSync(cachePath, cachePayload);
241
+
242
+ // Update in-memory state
243
+ activeCatalog = freshData;
244
+ activeFetchedAt = fetchedAt;
245
+ activeTtlMs = effectiveTtl;
246
+ activeFromCache = true;
247
+
248
+ notifyUpdateListeners();
249
+ return getCatalogStatus();
250
+ }
251
+
252
+ let inFlightRefresh: Promise<CatalogStatus> | null = null;
253
+
254
+ /**
255
+ * Ensures that the model catalog is loaded and fresh.
256
+ * Called automatically before Agent runs. If the cache is expired, triggers a refresh.
257
+ */
258
+ export async function ensureModelCatalogFresh(options: RefreshCatalogOptions = {}): Promise<CatalogStatus> {
259
+ const current = getCatalogStatus();
260
+ if (!current.isExpired && current.loaded && !options.force) {
261
+ return current;
262
+ }
263
+
264
+ if (inFlightRefresh) {
265
+ return inFlightRefresh;
266
+ }
267
+
268
+ inFlightRefresh = refreshModelCatalog(options).finally(() => {
269
+ inFlightRefresh = null;
270
+ });
271
+
272
+ return inFlightRefresh;
273
+ }