@dudko.dev/agent-web 0.0.1
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/LICENSE +21 -0
- package/README.md +176 -0
- package/dist/chunk-R7H4HSYT.js +22 -0
- package/dist/chunk-R7H4HSYT.js.map +1 -0
- package/dist/index.cjs +1407 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +835 -0
- package/dist/index.d.ts +835 -0
- package/dist/index.js +1298 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.cjs +111 -0
- package/dist/mcp.cjs.map +1 -0
- package/dist/mcp.d.cts +47 -0
- package/dist/mcp.d.ts +47 -0
- package/dist/mcp.js +83 -0
- package/dist/mcp.js.map +1 -0
- package/dist/webllm-IM3UD6YI.js +9 -0
- package/dist/webllm-IM3UD6YI.js.map +1 -0
- package/package.json +152 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1407 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// src/providers/webllm.ts
|
|
34
|
+
var webllm_exports = {};
|
|
35
|
+
__export(webllm_exports, {
|
|
36
|
+
createWebLLMModel: () => createWebLLMModel,
|
|
37
|
+
isWebGPUAvailable: () => isWebGPUAvailable
|
|
38
|
+
});
|
|
39
|
+
var isWebGPUAvailable, createWebLLMModel;
|
|
40
|
+
var init_webllm = __esm({
|
|
41
|
+
"src/providers/webllm.ts"() {
|
|
42
|
+
"use strict";
|
|
43
|
+
isWebGPUAvailable = () => typeof navigator !== "undefined" && "gpu" in navigator;
|
|
44
|
+
createWebLLMModel = async (model, options) => {
|
|
45
|
+
let mod;
|
|
46
|
+
try {
|
|
47
|
+
mod = await import("@browser-ai/web-llm");
|
|
48
|
+
} catch {
|
|
49
|
+
throw new Error(
|
|
50
|
+
'Local models require "@browser-ai/web-llm" and its peer "@mlc-ai/web-llm". Install: npm install @browser-ai/web-llm @mlc-ai/web-llm'
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return mod.webLLM(
|
|
54
|
+
model,
|
|
55
|
+
options
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// src/index.ts
|
|
62
|
+
var src_exports = {};
|
|
63
|
+
__export(src_exports, {
|
|
64
|
+
IndexedDBStore: () => IndexedDBStore,
|
|
65
|
+
IndexedDBVault: () => IndexedDBVault,
|
|
66
|
+
KEYS_STORE: () => KEYS_STORE,
|
|
67
|
+
MemoryCredentialStore: () => MemoryCredentialStore,
|
|
68
|
+
MemoryStore: () => MemoryStore,
|
|
69
|
+
PlanSchema: () => PlanSchema,
|
|
70
|
+
PlanStepSchema: () => PlanStepSchema,
|
|
71
|
+
ReplanSchema: () => ReplanSchema,
|
|
72
|
+
SECRETS_STORE: () => SECRETS_STORE,
|
|
73
|
+
SESSIONS_STORE: () => SESSIONS_STORE,
|
|
74
|
+
VaultCredentialStore: () => VaultCredentialStore,
|
|
75
|
+
buildModelFromStage: () => buildModelFromStage,
|
|
76
|
+
compressHistory: () => compressHistory,
|
|
77
|
+
createAgent: () => createAgent,
|
|
78
|
+
createPlan: () => createPlan,
|
|
79
|
+
createWebLLMModel: () => createWebLLMModel,
|
|
80
|
+
decideReplan: () => decideReplan,
|
|
81
|
+
decryptJSON: () => decryptJSON,
|
|
82
|
+
defaultPrompts: () => defaultPrompts,
|
|
83
|
+
defineTool: () => defineTool,
|
|
84
|
+
directBrowserOk: () => directBrowserOk,
|
|
85
|
+
dispatch: () => dispatch,
|
|
86
|
+
encryptJSON: () => encryptJSON,
|
|
87
|
+
executeStep: () => executeStep,
|
|
88
|
+
generate: () => generate,
|
|
89
|
+
generateStructured: () => generateStructured,
|
|
90
|
+
getOrCreateVaultKey: () => getOrCreateVaultKey,
|
|
91
|
+
isDirectModel: () => isDirectModel,
|
|
92
|
+
isProviderSpec: () => isProviderSpec,
|
|
93
|
+
isWebGPUAvailable: () => isWebGPUAvailable,
|
|
94
|
+
looksLikeJson: () => looksLikeJson,
|
|
95
|
+
normalizeSteps: () => normalizeSteps,
|
|
96
|
+
normalizeUsage: () => normalizeUsage,
|
|
97
|
+
openAgentWebDB: () => openAgentWebDB,
|
|
98
|
+
parseExecutorResponse: () => parseExecutorResponse,
|
|
99
|
+
parsePlainText: () => parsePlainText,
|
|
100
|
+
parsePlannerResponse: () => parsePlannerResponse,
|
|
101
|
+
parseReplannerResponse: () => parseReplannerResponse,
|
|
102
|
+
promptHintOf: () => promptHintOf,
|
|
103
|
+
renderCatalog: () => renderCatalog,
|
|
104
|
+
resolveConfig: () => resolveConfig,
|
|
105
|
+
resolveModel: () => resolveModel,
|
|
106
|
+
resolveStage: () => resolveStage,
|
|
107
|
+
runToolLoop: () => runToolLoop,
|
|
108
|
+
selectToolMode: () => selectToolMode,
|
|
109
|
+
shouldReplan: () => shouldReplan,
|
|
110
|
+
splitBlocker: () => splitBlocker,
|
|
111
|
+
stream: () => stream,
|
|
112
|
+
supportsNativeTools: () => supportsNativeTools,
|
|
113
|
+
supportsStructuredOutput: () => supportsStructuredOutput,
|
|
114
|
+
synthesizeAnswer: () => synthesizeAnswer,
|
|
115
|
+
withSystem: () => withSystem
|
|
116
|
+
});
|
|
117
|
+
module.exports = __toCommonJS(src_exports);
|
|
118
|
+
|
|
119
|
+
// src/config.ts
|
|
120
|
+
var resolveConfig = (c) => ({
|
|
121
|
+
clientName: c.clientName ?? "agent-web",
|
|
122
|
+
sessionId: c.sessionId ?? "default",
|
|
123
|
+
toolMode: c.toolMode ?? "auto",
|
|
124
|
+
toolSelectionStrategy: c.toolSelectionStrategy ?? "all",
|
|
125
|
+
maxIterations: c.maxIterations ?? 8,
|
|
126
|
+
maxStepsPerTask: c.maxStepsPerTask ?? 4,
|
|
127
|
+
maxRevisions: c.maxRevisions ?? 2,
|
|
128
|
+
chatTimeoutMs: c.chatTimeoutMs ?? 12e4,
|
|
129
|
+
budgets: {
|
|
130
|
+
planner: c.budgets?.planner ?? 800,
|
|
131
|
+
executor: c.budgets?.executor ?? 1200,
|
|
132
|
+
replanner: c.budgets?.replanner ?? 400,
|
|
133
|
+
synthesizer: c.budgets?.synthesizer ?? 400
|
|
134
|
+
},
|
|
135
|
+
temperature: c.temperature,
|
|
136
|
+
replan: c.replan ?? true,
|
|
137
|
+
synthesize: c.synthesize ?? true,
|
|
138
|
+
compressAfterChars: c.compressAfterChars ?? 12e3
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// src/llm/generate.ts
|
|
142
|
+
var import_ai = require("ai");
|
|
143
|
+
|
|
144
|
+
// src/llm/util.ts
|
|
145
|
+
var promptOf = (opts) => opts.messages ? { messages: opts.messages } : { prompt: opts.prompt ?? "" };
|
|
146
|
+
var normalizeUsage = (u) => {
|
|
147
|
+
const inputTokens = u?.inputTokens ?? 0;
|
|
148
|
+
const outputTokens = u?.outputTokens ?? 0;
|
|
149
|
+
return {
|
|
150
|
+
inputTokens,
|
|
151
|
+
outputTokens,
|
|
152
|
+
totalTokens: u?.totalTokens ?? inputTokens + outputTokens
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
var timeoutSignal = (signal, ms) => {
|
|
156
|
+
if (!ms || ms <= 0) return signal;
|
|
157
|
+
const timeout = AbortSignal.timeout(ms);
|
|
158
|
+
if (!signal) return timeout;
|
|
159
|
+
return typeof AbortSignal.any === "function" ? AbortSignal.any([signal, timeout]) : signal;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// src/llm/generate.ts
|
|
163
|
+
var generate = (model, opts = {}) => (0, import_ai.generateText)({
|
|
164
|
+
model,
|
|
165
|
+
system: opts.system,
|
|
166
|
+
...promptOf(opts),
|
|
167
|
+
tools: opts.tools,
|
|
168
|
+
maxOutputTokens: opts.maxOutputTokens,
|
|
169
|
+
temperature: opts.temperature,
|
|
170
|
+
abortSignal: timeoutSignal(opts.abortSignal, opts.timeoutMs)
|
|
171
|
+
});
|
|
172
|
+
var stream = (model, opts = {}) => (0, import_ai.streamText)({
|
|
173
|
+
model,
|
|
174
|
+
system: opts.system,
|
|
175
|
+
...promptOf(opts),
|
|
176
|
+
tools: opts.tools,
|
|
177
|
+
maxOutputTokens: opts.maxOutputTokens,
|
|
178
|
+
temperature: opts.temperature,
|
|
179
|
+
abortSignal: timeoutSignal(opts.abortSignal, opts.timeoutMs)
|
|
180
|
+
});
|
|
181
|
+
var generateStructured = (model, schema, opts = {}) => (0, import_ai.generateObject)({
|
|
182
|
+
model,
|
|
183
|
+
schema,
|
|
184
|
+
system: opts.system,
|
|
185
|
+
...promptOf(opts),
|
|
186
|
+
maxOutputTokens: opts.maxOutputTokens,
|
|
187
|
+
temperature: opts.temperature,
|
|
188
|
+
abortSignal: timeoutSignal(opts.abortSignal, opts.timeoutMs)
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// src/memory/compress.ts
|
|
192
|
+
var totalChars = (msgs) => msgs.reduce((n, m) => n + m.content.length, 0);
|
|
193
|
+
var compressHistory = async (messages, model, opts = {}) => {
|
|
194
|
+
const maxChars = opts.maxChars ?? 12e3;
|
|
195
|
+
const keepRecent = opts.keepRecent ?? 6;
|
|
196
|
+
if (maxChars <= 0 || messages.length <= keepRecent + 1 || totalChars(messages) <= maxChars) {
|
|
197
|
+
return messages;
|
|
198
|
+
}
|
|
199
|
+
const head = messages.slice(0, messages.length - keepRecent);
|
|
200
|
+
const tail = messages.slice(messages.length - keepRecent);
|
|
201
|
+
const transcript = head.map((m) => `${m.role}: ${m.content}`).join("\n").slice(0, 8e3);
|
|
202
|
+
try {
|
|
203
|
+
const result = await generate(model, {
|
|
204
|
+
system: "Summarize the following conversation into a compact set of durable facts and decisions. Output plain text only.",
|
|
205
|
+
prompt: transcript,
|
|
206
|
+
maxOutputTokens: 400
|
|
207
|
+
});
|
|
208
|
+
const clean = (result.text || "").trim();
|
|
209
|
+
if (!clean) return messages;
|
|
210
|
+
const summaryMsg = {
|
|
211
|
+
role: "system",
|
|
212
|
+
content: `Summary of earlier conversation:
|
|
213
|
+
${clean}`,
|
|
214
|
+
ts: head[0]?.ts
|
|
215
|
+
};
|
|
216
|
+
return [summaryMsg, ...tail];
|
|
217
|
+
} catch {
|
|
218
|
+
return messages;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/providers/types.ts
|
|
223
|
+
var isDirectModel = (m) => typeof m === "string" || typeof m === "object" && m !== null && "specificationVersion" in m;
|
|
224
|
+
var isProviderSpec = (m) => typeof m === "object" && m !== null && !isDirectModel(m) && typeof m.providerType === "string";
|
|
225
|
+
|
|
226
|
+
// src/providers/registry.ts
|
|
227
|
+
var REQUIRES_BASE_URL = /* @__PURE__ */ new Set(["openai-compatible"]);
|
|
228
|
+
var NO_KEY = /* @__PURE__ */ new Set(["web-llm"]);
|
|
229
|
+
var PROVIDER_PACKAGE = {
|
|
230
|
+
openai: "@ai-sdk/openai",
|
|
231
|
+
anthropic: "@ai-sdk/anthropic",
|
|
232
|
+
google: "@ai-sdk/google",
|
|
233
|
+
"openai-compatible": "@ai-sdk/openai-compatible",
|
|
234
|
+
xai: "@ai-sdk/xai",
|
|
235
|
+
deepseek: "@ai-sdk/deepseek"
|
|
236
|
+
};
|
|
237
|
+
var loadProvider = async (pkg) => {
|
|
238
|
+
try {
|
|
239
|
+
return await import(
|
|
240
|
+
/* @vite-ignore */
|
|
241
|
+
pkg
|
|
242
|
+
);
|
|
243
|
+
} catch {
|
|
244
|
+
throw new Error(
|
|
245
|
+
`Provider package "${pkg}" is not installed. Add it to your app: npm install ${pkg}`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
var normalizeSpec = (spec) => {
|
|
250
|
+
if (REQUIRES_BASE_URL.has(spec.providerType) && !spec.baseURL) {
|
|
251
|
+
throw new Error(`provider "${spec.providerType}" requires a baseURL (point it at your server)`);
|
|
252
|
+
}
|
|
253
|
+
return spec;
|
|
254
|
+
};
|
|
255
|
+
var resolveStage = (base, override, stageName) => {
|
|
256
|
+
if (override !== void 0 && isDirectModel(override)) {
|
|
257
|
+
return { type: "direct", model: override };
|
|
258
|
+
}
|
|
259
|
+
if (override !== void 0 && isProviderSpec(override)) {
|
|
260
|
+
if (isProviderSpec(base) && override.providerType !== base.providerType && !override.credentialRef && !override.apiKey && !NO_KEY.has(override.providerType)) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`${stageName}: provider "${override.providerType}" differs from the base "${base.providerType}"; set ${stageName}.credentialRef or apiKey (cross-provider key inheritance is unsafe)`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
return { type: "spec", spec: normalizeSpec(override) };
|
|
266
|
+
}
|
|
267
|
+
if (isDirectModel(base)) {
|
|
268
|
+
if (override && Object.keys(override).length > 0) {
|
|
269
|
+
throw new Error(
|
|
270
|
+
`${stageName}: cannot layer a partial override onto a direct model \u2014 pass a full model or ProviderModelSpec for ${stageName}`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
return { type: "direct", model: base };
|
|
274
|
+
}
|
|
275
|
+
if (!isProviderSpec(base)) {
|
|
276
|
+
throw new Error(`${stageName}: base model must be a LanguageModel or a ProviderModelSpec`);
|
|
277
|
+
}
|
|
278
|
+
const ov = override ?? {};
|
|
279
|
+
const merged = {
|
|
280
|
+
providerType: base.providerType,
|
|
281
|
+
model: ov.model ?? base.model,
|
|
282
|
+
baseURL: ov.baseURL ?? base.baseURL,
|
|
283
|
+
credentialRef: ov.credentialRef ?? base.credentialRef,
|
|
284
|
+
apiKey: ov.apiKey ?? base.apiKey,
|
|
285
|
+
headers: ov.headers ?? base.headers,
|
|
286
|
+
providerOptions: ov.providerOptions ?? base.providerOptions
|
|
287
|
+
};
|
|
288
|
+
return { type: "spec", spec: normalizeSpec(merged) };
|
|
289
|
+
};
|
|
290
|
+
var resolveApiKey = async (spec, credentials) => {
|
|
291
|
+
if (spec.apiKey) {
|
|
292
|
+
console.warn(
|
|
293
|
+
`[agent-web] using an inline apiKey for provider "${spec.providerType}"; prefer credentialRef so the key stays in the encrypted vault`
|
|
294
|
+
);
|
|
295
|
+
return spec.apiKey;
|
|
296
|
+
}
|
|
297
|
+
if (spec.credentialRef) {
|
|
298
|
+
if (!credentials) {
|
|
299
|
+
throw new Error(
|
|
300
|
+
`credentialRef "${spec.credentialRef}" is set but no CredentialStore was provided to the agent`
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
const key = await credentials.getApiKey(spec.credentialRef);
|
|
304
|
+
if (!key) {
|
|
305
|
+
throw new Error(
|
|
306
|
+
`no API key found in the CredentialStore for credentialRef "${spec.credentialRef}"`
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
return key;
|
|
310
|
+
}
|
|
311
|
+
return void 0;
|
|
312
|
+
};
|
|
313
|
+
var buildModelFromStage = async (resolved, credentials, clientName = "agent-web") => {
|
|
314
|
+
if (resolved.type === "direct") return resolved.model;
|
|
315
|
+
const spec = resolved.spec;
|
|
316
|
+
const providerOptions = spec.providerOptions ?? {};
|
|
317
|
+
const apiKey = await resolveApiKey(spec, credentials);
|
|
318
|
+
switch (spec.providerType) {
|
|
319
|
+
case "web-llm": {
|
|
320
|
+
const { createWebLLMModel: createWebLLMModel2 } = await Promise.resolve().then(() => (init_webllm(), webllm_exports));
|
|
321
|
+
return createWebLLMModel2(spec.model, providerOptions);
|
|
322
|
+
}
|
|
323
|
+
case "gateway": {
|
|
324
|
+
const { createGateway } = await import("ai");
|
|
325
|
+
const provider = createGateway({
|
|
326
|
+
baseURL: spec.baseURL || void 0,
|
|
327
|
+
apiKey,
|
|
328
|
+
headers: spec.headers,
|
|
329
|
+
...providerOptions
|
|
330
|
+
});
|
|
331
|
+
return provider(spec.model);
|
|
332
|
+
}
|
|
333
|
+
case "openai": {
|
|
334
|
+
const { createOpenAI } = await loadProvider(
|
|
335
|
+
PROVIDER_PACKAGE.openai
|
|
336
|
+
);
|
|
337
|
+
const provider = createOpenAI({
|
|
338
|
+
baseURL: spec.baseURL || void 0,
|
|
339
|
+
apiKey,
|
|
340
|
+
headers: spec.headers,
|
|
341
|
+
...providerOptions
|
|
342
|
+
});
|
|
343
|
+
return provider(spec.model);
|
|
344
|
+
}
|
|
345
|
+
case "anthropic": {
|
|
346
|
+
const { createAnthropic } = await loadProvider(
|
|
347
|
+
PROVIDER_PACKAGE.anthropic
|
|
348
|
+
);
|
|
349
|
+
const provider = createAnthropic({
|
|
350
|
+
baseURL: spec.baseURL || void 0,
|
|
351
|
+
apiKey,
|
|
352
|
+
// Anthropic's SDK refuses browser calls unless this opt-in header is
|
|
353
|
+
// set; host headers may override it.
|
|
354
|
+
headers: { "anthropic-dangerous-direct-browser-access": "true", ...spec.headers },
|
|
355
|
+
...providerOptions
|
|
356
|
+
});
|
|
357
|
+
return provider(spec.model);
|
|
358
|
+
}
|
|
359
|
+
case "google": {
|
|
360
|
+
const { createGoogleGenerativeAI } = await loadProvider(
|
|
361
|
+
PROVIDER_PACKAGE.google
|
|
362
|
+
);
|
|
363
|
+
const provider = createGoogleGenerativeAI({
|
|
364
|
+
baseURL: spec.baseURL || void 0,
|
|
365
|
+
apiKey,
|
|
366
|
+
headers: spec.headers,
|
|
367
|
+
...providerOptions
|
|
368
|
+
});
|
|
369
|
+
return provider(spec.model);
|
|
370
|
+
}
|
|
371
|
+
case "openai-compatible": {
|
|
372
|
+
const { createOpenAICompatible } = await loadProvider(PROVIDER_PACKAGE["openai-compatible"]);
|
|
373
|
+
const provider = createOpenAICompatible({
|
|
374
|
+
name: clientName,
|
|
375
|
+
baseURL: spec.baseURL,
|
|
376
|
+
apiKey,
|
|
377
|
+
headers: spec.headers,
|
|
378
|
+
...providerOptions
|
|
379
|
+
});
|
|
380
|
+
return provider(spec.model);
|
|
381
|
+
}
|
|
382
|
+
case "xai": {
|
|
383
|
+
const { createXai } = await loadProvider(PROVIDER_PACKAGE.xai);
|
|
384
|
+
const provider = createXai({
|
|
385
|
+
baseURL: spec.baseURL || void 0,
|
|
386
|
+
apiKey,
|
|
387
|
+
headers: spec.headers,
|
|
388
|
+
...providerOptions
|
|
389
|
+
});
|
|
390
|
+
return provider(spec.model);
|
|
391
|
+
}
|
|
392
|
+
case "deepseek": {
|
|
393
|
+
const { createDeepSeek } = await loadProvider(
|
|
394
|
+
PROVIDER_PACKAGE.deepseek
|
|
395
|
+
);
|
|
396
|
+
const provider = createDeepSeek({
|
|
397
|
+
baseURL: spec.baseURL || void 0,
|
|
398
|
+
apiKey,
|
|
399
|
+
headers: spec.headers,
|
|
400
|
+
...providerOptions
|
|
401
|
+
});
|
|
402
|
+
return provider(spec.model);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
var resolveModel = (input, credentials, clientName) => buildModelFromStage(resolveStage(input, void 0, "model"), credentials, clientName);
|
|
407
|
+
|
|
408
|
+
// src/prompts.ts
|
|
409
|
+
var numbered = (items, empty) => items.length ? items.map((s, i) => `${i + 1}. ${s}`).join("\n") : empty;
|
|
410
|
+
var stateBlock = (state) => state && state.trim() ? `
|
|
411
|
+
|
|
412
|
+
STATE:
|
|
413
|
+
${state}` : "";
|
|
414
|
+
var PLANNER_NATIVE = `You are the PLANNER of a tool-using agent that changes a workspace step by step.
|
|
415
|
+
Produce a brief "thought" and an ordered "steps" list (1\u20136 DISTINCT, self-contained steps) grounded in the current STATE and the available TOOLS.
|
|
416
|
+
If the user only greets, thanks, makes small talk, asks a question, or is unclear: return an EMPTY steps list and put a short, friendly answer in "thought".
|
|
417
|
+
Never repeat or pad steps.`;
|
|
418
|
+
var PLANNER_PROMPTED = `You are the PLANNER of a tool-using agent that changes a workspace step by step.
|
|
419
|
+
|
|
420
|
+
Reply with a single JSON object, nothing else:
|
|
421
|
+
{ "reply": string, "plan": string[] }
|
|
422
|
+
- Only produce a "plan" when the user CLEARLY asks to build, do, or change something. For a greeting, small talk, thanks, a question, or an unclear/empty request: set "plan": [] and put a short, friendly "reply".
|
|
423
|
+
- For a real goal: "reply" is one short sentence; "plan" is 1\u20136 DISTINCT, self-contained steps. Never repeat or pad steps.
|
|
424
|
+
- Plan realistic steps the available TOOLS can perform, grounded in the current STATE.`;
|
|
425
|
+
var EXECUTOR_NATIVE = `You are the EXECUTOR of a tool-using agent. Carry out ONLY the current step by calling the provided tools.
|
|
426
|
+
Build on the current STATE \u2014 do not repeat work that is already there.
|
|
427
|
+
When the step is done, reply with one short human sentence describing what you did (no JSON).
|
|
428
|
+
If you CANNOT complete the step (a needed tool is missing or an input is unavailable), explain why in one sentence and include the token [BLOCKER].`;
|
|
429
|
+
var EXECUTOR_PROMPTED = `You are the EXECUTOR of a tool-using agent. Carry out ONLY the current step by emitting tool calls.
|
|
430
|
+
|
|
431
|
+
Reply with a single JSON object, nothing else:
|
|
432
|
+
{ "reply": string, "actions": [ { "tool": string, "args": object } ] }
|
|
433
|
+
- "actions" are the tool calls for THIS step ([] if none are needed). Use ONLY tools from the TOOLS list; "args" must match the tool's parameters.
|
|
434
|
+
- Build on the current STATE \u2014 do not repeat work that is already there.
|
|
435
|
+
- "reply" is one short human sentence (no JSON) describing what you did.
|
|
436
|
+
- If you CANNOT complete the step, set "actions": [] and put the token [BLOCKER] in "reply" with a short reason.`;
|
|
437
|
+
var REPLANNER_NATIVE = `You are the REPLANNER. After a step that was blocked or had a failed tool call, decide whether to keep going, revise the remaining steps, or finish.
|
|
438
|
+
"continue": the remaining steps still fit. "revise": provide a better "plan" for the REMAINING work (never repeat done work). "finish": the goal is already met.
|
|
439
|
+
Judge from the current STATE vs the goal. Prefer "continue".`;
|
|
440
|
+
var REPLANNER_PROMPTED = `You are the REPLANNER. After each executed step you decide whether to keep going, revise the remaining steps, or finish.
|
|
441
|
+
|
|
442
|
+
Reply with a single JSON object, nothing else:
|
|
443
|
+
{ "decision": "continue" | "revise" | "finish", "reason": string, "plan": string[] }
|
|
444
|
+
- "continue": remaining steps still fit \u2014 proceed (omit "plan").
|
|
445
|
+
- "revise": replace the REMAINING steps with a better list in "plan"; never repeat done work.
|
|
446
|
+
- "finish": the goal is already met \u2014 stop (omit "plan").
|
|
447
|
+
Judge from the current STATE vs the goal. Prefer "continue".`;
|
|
448
|
+
var SYNTHESIZER_SYSTEM = `You are the SYNTHESIZER. In 1\u20133 short, friendly sentences, tell the user what was done to meet their goal. Be concrete. Output plain text only \u2014 no JSON, no code.`;
|
|
449
|
+
var defaultPrompts = {
|
|
450
|
+
planner: (ctx) => ({
|
|
451
|
+
system: ctx.mode === "prompted" ? PLANNER_PROMPTED : PLANNER_NATIVE,
|
|
452
|
+
prompt: `GOAL: ${ctx.goal}${stateBlock(ctx.state)}
|
|
453
|
+
|
|
454
|
+
TOOLS:
|
|
455
|
+
${ctx.toolCatalog}`
|
|
456
|
+
}),
|
|
457
|
+
executor: (ctx) => ({
|
|
458
|
+
system: ctx.mode === "prompted" ? EXECUTOR_PROMPTED : EXECUTOR_NATIVE,
|
|
459
|
+
prompt: [
|
|
460
|
+
`GOAL: ${ctx.goal}`,
|
|
461
|
+
stateBlock(ctx.state).trimStart(),
|
|
462
|
+
ctx.mode === "prompted" ? `TOOLS:
|
|
463
|
+
${ctx.toolCatalog}` : "",
|
|
464
|
+
ctx.done.length ? `Already done:
|
|
465
|
+
${numbered(ctx.done, "")}` : "",
|
|
466
|
+
`STEP ${ctx.index}/${ctx.total}: ${ctx.step}`
|
|
467
|
+
].filter(Boolean).join("\n\n")
|
|
468
|
+
}),
|
|
469
|
+
replanner: (ctx) => ({
|
|
470
|
+
system: ctx.mode === "prompted" ? REPLANNER_PROMPTED : REPLANNER_NATIVE,
|
|
471
|
+
prompt: [
|
|
472
|
+
`GOAL: ${ctx.goal}`,
|
|
473
|
+
stateBlock(ctx.state).trimStart(),
|
|
474
|
+
`Already done:
|
|
475
|
+
${numbered(ctx.done, "(nothing yet)")}`,
|
|
476
|
+
`Remaining plan:
|
|
477
|
+
${numbered(ctx.remaining, "(none)")}`,
|
|
478
|
+
"Decide: continue, revise, or finish."
|
|
479
|
+
].filter(Boolean).join("\n\n")
|
|
480
|
+
}),
|
|
481
|
+
synthesizer: (ctx) => ({
|
|
482
|
+
system: SYNTHESIZER_SYSTEM,
|
|
483
|
+
prompt: [
|
|
484
|
+
`GOAL: ${ctx.goal}`,
|
|
485
|
+
stateBlock(ctx.state).trimStart(),
|
|
486
|
+
`What was done:
|
|
487
|
+
${numbered(ctx.done, "(no changes)")}`,
|
|
488
|
+
"Write the final summary for the user."
|
|
489
|
+
].filter(Boolean).join("\n\n")
|
|
490
|
+
})
|
|
491
|
+
};
|
|
492
|
+
var withSystem = (base, extra) => extra && extra.trim() ? `${extra.trim()}
|
|
493
|
+
|
|
494
|
+
${base}` : base;
|
|
495
|
+
|
|
496
|
+
// src/tools/mode.ts
|
|
497
|
+
var modelProviderId = (model) => typeof model === "string" ? model : model.provider ?? "";
|
|
498
|
+
var LOCAL_RE = /web-?llm|mlc|browser-ai|transformers|nano|built-?in/i;
|
|
499
|
+
var selectToolMode = (model, mode = "auto") => {
|
|
500
|
+
if (mode === "native" || mode === "prompted") return mode;
|
|
501
|
+
return LOCAL_RE.test(modelProviderId(model)) ? "prompted" : "native";
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
// src/tools/types.ts
|
|
505
|
+
var promptHintOf = (tool2) => typeof tool2.promptHint === "string" ? tool2.promptHint : void 0;
|
|
506
|
+
|
|
507
|
+
// src/tools/prompted.ts
|
|
508
|
+
var renderCatalog = (tools) => {
|
|
509
|
+
const names = Object.keys(tools);
|
|
510
|
+
if (names.length === 0) return "(no tools)";
|
|
511
|
+
return names.map((name) => {
|
|
512
|
+
const t = tools[name];
|
|
513
|
+
const hint = promptHintOf(t) ?? "";
|
|
514
|
+
const desc = typeof t.description === "string" ? t.description : "";
|
|
515
|
+
return `- ${name}(${hint}): ${desc}`;
|
|
516
|
+
}).join("\n");
|
|
517
|
+
};
|
|
518
|
+
var dispatch = async (action, tools, ctx = {}) => {
|
|
519
|
+
const t = tools[action.tool];
|
|
520
|
+
if (!t) {
|
|
521
|
+
return {
|
|
522
|
+
name: action.tool,
|
|
523
|
+
input: action.args,
|
|
524
|
+
output: `unknown tool "${action.tool}"`,
|
|
525
|
+
ok: false
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
let input = action.args;
|
|
529
|
+
const schema = t.inputSchema;
|
|
530
|
+
if (schema && typeof schema.safeParse === "function") {
|
|
531
|
+
const parsed = schema.safeParse(input);
|
|
532
|
+
if (!parsed.success) {
|
|
533
|
+
return {
|
|
534
|
+
name: action.tool,
|
|
535
|
+
input,
|
|
536
|
+
output: `invalid args for "${action.tool}": ${parsed.error?.message ?? "schema validation failed"}`,
|
|
537
|
+
ok: false
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
input = parsed.data;
|
|
541
|
+
}
|
|
542
|
+
const execute = t.execute;
|
|
543
|
+
if (typeof execute !== "function") {
|
|
544
|
+
return { name: action.tool, input, output: `tool "${action.tool}" has no execute`, ok: false };
|
|
545
|
+
}
|
|
546
|
+
try {
|
|
547
|
+
const output = await execute(input, {
|
|
548
|
+
toolCallId: ctx.toolCallId ?? `prompted-${action.tool}`,
|
|
549
|
+
messages: ctx.messages ?? [],
|
|
550
|
+
abortSignal: ctx.abortSignal
|
|
551
|
+
});
|
|
552
|
+
return { name: action.tool, input, output, ok: true };
|
|
553
|
+
} catch (err) {
|
|
554
|
+
return {
|
|
555
|
+
name: action.tool,
|
|
556
|
+
input,
|
|
557
|
+
output: err instanceof Error ? err.message : "tool error",
|
|
558
|
+
ok: false
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
// src/llm/tool-loop.ts
|
|
564
|
+
var import_ai2 = require("ai");
|
|
565
|
+
|
|
566
|
+
// src/parse.ts
|
|
567
|
+
var stripFences = (text) => (text || "").replace(/```(?:json)?/gi, "").trim();
|
|
568
|
+
var extractBalanced = (text, open) => {
|
|
569
|
+
const close = open === "{" ? "}" : "]";
|
|
570
|
+
const start = text.indexOf(open);
|
|
571
|
+
if (start < 0) return void 0;
|
|
572
|
+
let depth = 0;
|
|
573
|
+
let inStr = false;
|
|
574
|
+
let esc = false;
|
|
575
|
+
for (let i = start; i < text.length; i += 1) {
|
|
576
|
+
const c = text[i];
|
|
577
|
+
if (inStr) {
|
|
578
|
+
if (esc) esc = false;
|
|
579
|
+
else if (c === "\\") esc = true;
|
|
580
|
+
else if (c === '"') inStr = false;
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
if (c === '"') inStr = true;
|
|
584
|
+
else if (c === open) depth += 1;
|
|
585
|
+
else if (c === close) {
|
|
586
|
+
depth -= 1;
|
|
587
|
+
if (depth === 0) return text.slice(start, i + 1);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return void 0;
|
|
591
|
+
};
|
|
592
|
+
var tryParse = (s) => {
|
|
593
|
+
if (!s) return void 0;
|
|
594
|
+
try {
|
|
595
|
+
return JSON.parse(s);
|
|
596
|
+
} catch {
|
|
597
|
+
return void 0;
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
var looksLikeJson = (s) => {
|
|
601
|
+
const t = (s || "").trim();
|
|
602
|
+
return t.startsWith("{") || t.startsWith("[") || /"(tool|args|plan|reply|actions|decision)"\s*:/.test(t);
|
|
603
|
+
};
|
|
604
|
+
var asObject = (text) => {
|
|
605
|
+
const v = tryParse(text) ?? tryParse(extractBalanced(text, "{"));
|
|
606
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
607
|
+
};
|
|
608
|
+
var normalizeSteps = (arr, cap = 12) => {
|
|
609
|
+
if (!Array.isArray(arr)) return [];
|
|
610
|
+
const seen = {};
|
|
611
|
+
const out = [];
|
|
612
|
+
for (const s of arr) {
|
|
613
|
+
if (out.length >= cap || typeof s !== "string") continue;
|
|
614
|
+
const step = s.trim();
|
|
615
|
+
const key = step.toLowerCase();
|
|
616
|
+
if (!step || seen[key]) continue;
|
|
617
|
+
seen[key] = true;
|
|
618
|
+
out.push(step);
|
|
619
|
+
}
|
|
620
|
+
return out;
|
|
621
|
+
};
|
|
622
|
+
var salvageStringArray = (text, field) => {
|
|
623
|
+
const at = text.search(new RegExp(`"${field}"\\s*:`));
|
|
624
|
+
if (at < 0) return [];
|
|
625
|
+
const region = text.slice(at);
|
|
626
|
+
const out = [];
|
|
627
|
+
const re = /"((?:[^"\\]|\\.)*)"/g;
|
|
628
|
+
let skippedKey = false;
|
|
629
|
+
let m = re.exec(region);
|
|
630
|
+
while (m) {
|
|
631
|
+
if (skippedKey) out.push(m[1]);
|
|
632
|
+
skippedKey = true;
|
|
633
|
+
m = re.exec(region);
|
|
634
|
+
}
|
|
635
|
+
return out;
|
|
636
|
+
};
|
|
637
|
+
var salvageString = (text, field) => {
|
|
638
|
+
const m = text.match(new RegExp(`"${field}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`));
|
|
639
|
+
return m ? m[1].trim() : "";
|
|
640
|
+
};
|
|
641
|
+
var parsePlannerResponse = (raw) => {
|
|
642
|
+
const text = stripFences(raw);
|
|
643
|
+
const obj = asObject(text);
|
|
644
|
+
if (obj) {
|
|
645
|
+
const reply = typeof obj.reply === "string" ? obj.reply.trim() : "";
|
|
646
|
+
return { reply, plan: normalizeSteps(obj.plan) };
|
|
647
|
+
}
|
|
648
|
+
if (/"plan"\s*:/.test(text)) {
|
|
649
|
+
return {
|
|
650
|
+
reply: salvageString(text, "reply"),
|
|
651
|
+
plan: normalizeSteps(salvageStringArray(text, "plan"))
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
return { reply: looksLikeJson(text) ? "" : text.trim(), plan: [] };
|
|
655
|
+
};
|
|
656
|
+
var toActions = (value) => {
|
|
657
|
+
if (!Array.isArray(value)) return [];
|
|
658
|
+
const out = [];
|
|
659
|
+
for (const a of value) {
|
|
660
|
+
if (a && typeof a === "object" && typeof a.tool === "string") {
|
|
661
|
+
const rec = a;
|
|
662
|
+
out.push({
|
|
663
|
+
tool: rec.tool,
|
|
664
|
+
args: rec.args && typeof rec.args === "object" && !Array.isArray(rec.args) ? rec.args : {}
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return out;
|
|
669
|
+
};
|
|
670
|
+
var parseExecutorResponse = (raw) => {
|
|
671
|
+
const text = stripFences(raw);
|
|
672
|
+
const obj = asObject(text);
|
|
673
|
+
if (obj) {
|
|
674
|
+
const reply = typeof obj.reply === "string" ? obj.reply.trim() : "";
|
|
675
|
+
return { reply, actions: toActions(obj.actions) };
|
|
676
|
+
}
|
|
677
|
+
const arr = tryParse(extractBalanced(text, "["));
|
|
678
|
+
if (Array.isArray(arr)) {
|
|
679
|
+
const actions = toActions(arr);
|
|
680
|
+
return { reply: "", actions };
|
|
681
|
+
}
|
|
682
|
+
return { reply: looksLikeJson(text) ? "" : text.trim(), actions: [] };
|
|
683
|
+
};
|
|
684
|
+
var parseReplannerResponse = (raw) => {
|
|
685
|
+
const obj = asObject(stripFences(raw));
|
|
686
|
+
const decision = obj?.decision === "revise" || obj?.decision === "finish" ? obj.decision : "continue";
|
|
687
|
+
const reason = typeof obj?.reason === "string" ? obj.reason.trim() : "";
|
|
688
|
+
return { decision, reason, plan: normalizeSteps(obj?.plan) };
|
|
689
|
+
};
|
|
690
|
+
var parsePlainText = (raw) => {
|
|
691
|
+
const text = stripFences(raw);
|
|
692
|
+
const obj = asObject(text);
|
|
693
|
+
if (obj && typeof obj.reply === "string") return obj.reply.trim();
|
|
694
|
+
return looksLikeJson(text) ? "" : text.trim();
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
// src/llm/tool-loop.ts
|
|
698
|
+
var runToolLoop = (model, opts) => opts.mode === "prompted" ? runPrompted(model, opts) : runNative(model, opts);
|
|
699
|
+
var runNative = async (model, opts) => {
|
|
700
|
+
const toolCalls = [];
|
|
701
|
+
const pending = /* @__PURE__ */ new Map();
|
|
702
|
+
const result = (0, import_ai2.streamText)({
|
|
703
|
+
model,
|
|
704
|
+
tools: opts.tools,
|
|
705
|
+
...opts.activeTools ? { activeTools: opts.activeTools } : {},
|
|
706
|
+
stopWhen: (0, import_ai2.stepCountIs)(opts.maxSteps ?? 4),
|
|
707
|
+
system: opts.system,
|
|
708
|
+
...promptOf(opts),
|
|
709
|
+
maxOutputTokens: opts.maxOutputTokens,
|
|
710
|
+
temperature: opts.temperature,
|
|
711
|
+
abortSignal: timeoutSignal(opts.abortSignal, opts.timeoutMs)
|
|
712
|
+
});
|
|
713
|
+
for await (const part of result.fullStream) {
|
|
714
|
+
switch (part.type) {
|
|
715
|
+
case "text-delta":
|
|
716
|
+
if (part.text) opts.callbacks?.onTextDelta?.(part.text);
|
|
717
|
+
break;
|
|
718
|
+
case "tool-call":
|
|
719
|
+
pending.set(part.toolCallId, { name: part.toolName, input: part.input });
|
|
720
|
+
opts.callbacks?.onToolCall?.(part.toolName, part.input);
|
|
721
|
+
break;
|
|
722
|
+
case "tool-result": {
|
|
723
|
+
const known = pending.get(part.toolCallId);
|
|
724
|
+
toolCalls.push({
|
|
725
|
+
name: part.toolName,
|
|
726
|
+
input: known?.input ?? part.input,
|
|
727
|
+
output: part.output,
|
|
728
|
+
ok: true
|
|
729
|
+
});
|
|
730
|
+
opts.callbacks?.onToolResult?.(part.toolName, part.output, true);
|
|
731
|
+
pending.delete(part.toolCallId);
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
case "tool-error": {
|
|
735
|
+
const known = pending.get(part.toolCallId);
|
|
736
|
+
toolCalls.push({
|
|
737
|
+
name: part.toolName,
|
|
738
|
+
input: known?.input ?? part.input,
|
|
739
|
+
output: part.error,
|
|
740
|
+
ok: false
|
|
741
|
+
});
|
|
742
|
+
opts.callbacks?.onToolResult?.(part.toolName, part.error, false);
|
|
743
|
+
pending.delete(part.toolCallId);
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
case "error":
|
|
747
|
+
throw part.error instanceof Error ? part.error : new Error(String(part.error));
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const [text, usage] = await Promise.all([result.text, result.usage]);
|
|
751
|
+
return { text: text.trim(), toolCalls, usage: normalizeUsage(usage) };
|
|
752
|
+
};
|
|
753
|
+
var runPrompted = async (model, opts) => {
|
|
754
|
+
const result = await (0, import_ai2.generateText)({
|
|
755
|
+
model,
|
|
756
|
+
system: opts.system,
|
|
757
|
+
...promptOf(opts),
|
|
758
|
+
maxOutputTokens: opts.maxOutputTokens,
|
|
759
|
+
temperature: opts.temperature,
|
|
760
|
+
abortSignal: timeoutSignal(opts.abortSignal, opts.timeoutMs)
|
|
761
|
+
});
|
|
762
|
+
if (result.text) opts.callbacks?.onTextDelta?.(result.text);
|
|
763
|
+
const parsed = parseExecutorResponse(result.text);
|
|
764
|
+
const toolCalls = [];
|
|
765
|
+
for (const action of parsed.actions) {
|
|
766
|
+
opts.callbacks?.onToolCall?.(action.tool, action.args);
|
|
767
|
+
const rec = await dispatch(action, opts.tools, { abortSignal: opts.abortSignal });
|
|
768
|
+
toolCalls.push(rec);
|
|
769
|
+
opts.callbacks?.onToolResult?.(rec.name, rec.output, rec.ok);
|
|
770
|
+
}
|
|
771
|
+
return { text: parsed.reply, toolCalls, usage: normalizeUsage(result.usage) };
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
// src/agent/internal.ts
|
|
775
|
+
var systemFor = (ctx, base) => withSystem(base, ctx.raw.systemPrompt);
|
|
776
|
+
|
|
777
|
+
// src/agent/executor.ts
|
|
778
|
+
var BLOCKER = "[BLOCKER]";
|
|
779
|
+
var splitBlocker = (raw) => {
|
|
780
|
+
if (!raw.includes(BLOCKER)) return { summary: raw.trim(), blocked: false };
|
|
781
|
+
return { summary: raw.split(BLOCKER).join("").trim(), blocked: true };
|
|
782
|
+
};
|
|
783
|
+
var activeToolNames = (ctx, step) => {
|
|
784
|
+
if (ctx.config.toolSelectionStrategy !== "plan-narrowed") return void 0;
|
|
785
|
+
const suggested = step.suggestedTools ?? [];
|
|
786
|
+
const known = suggested.filter((n) => ctx.tools[n]);
|
|
787
|
+
if (known.length === 0 && suggested.length > 0) return Object.keys(ctx.tools);
|
|
788
|
+
return known;
|
|
789
|
+
};
|
|
790
|
+
var summaryOf = (text, toolCallCount) => {
|
|
791
|
+
if (text.length > 0) return text;
|
|
792
|
+
return toolCallCount > 0 ? `Executed ${toolCallCount} tool call(s).` : "Step produced no output.";
|
|
793
|
+
};
|
|
794
|
+
var executeStep = async (ctx, goal, step, index, total, done) => {
|
|
795
|
+
const state = await ctx.state();
|
|
796
|
+
const parts = ctx.prompts.executor({
|
|
797
|
+
goal,
|
|
798
|
+
state,
|
|
799
|
+
step: step.description,
|
|
800
|
+
index,
|
|
801
|
+
total,
|
|
802
|
+
toolCatalog: ctx.toolCatalog,
|
|
803
|
+
done,
|
|
804
|
+
mode: ctx.executorMode
|
|
805
|
+
});
|
|
806
|
+
const loop = await runToolLoop(ctx.executorModel, {
|
|
807
|
+
mode: ctx.executorMode,
|
|
808
|
+
system: systemFor(ctx, parts.system),
|
|
809
|
+
prompt: parts.prompt,
|
|
810
|
+
tools: ctx.tools,
|
|
811
|
+
activeTools: activeToolNames(ctx, step),
|
|
812
|
+
maxSteps: ctx.config.maxStepsPerTask,
|
|
813
|
+
maxOutputTokens: ctx.config.budgets.executor,
|
|
814
|
+
temperature: ctx.config.temperature,
|
|
815
|
+
abortSignal: ctx.signal,
|
|
816
|
+
timeoutMs: ctx.config.chatTimeoutMs,
|
|
817
|
+
callbacks: {
|
|
818
|
+
onTextDelta: (delta) => ctx.emit({ type: "step.text-delta", step, delta }),
|
|
819
|
+
onToolCall: (name, input) => ctx.emit({ type: "step.tool-call", step, name, input }),
|
|
820
|
+
onToolResult: (name, output, ok) => ctx.emit({ type: "step.tool-result", step, name, output, ok })
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
const { summary, blocked } = splitBlocker(loop.text);
|
|
824
|
+
return {
|
|
825
|
+
result: {
|
|
826
|
+
step,
|
|
827
|
+
summary: summaryOf(summary, loop.toolCalls.length),
|
|
828
|
+
toolCalls: loop.toolCalls,
|
|
829
|
+
blocked
|
|
830
|
+
},
|
|
831
|
+
usage: loop.usage
|
|
832
|
+
};
|
|
833
|
+
};
|
|
834
|
+
var shouldReplan = (result) => result.blocked || result.toolCalls.some((c) => !c.ok);
|
|
835
|
+
|
|
836
|
+
// src/agent/loop-types.ts
|
|
837
|
+
var emptyUsage = () => ({ inputTokens: 0, outputTokens: 0, totalTokens: 0 });
|
|
838
|
+
var addUsage = (a, b) => ({
|
|
839
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
840
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
841
|
+
totalTokens: a.totalTokens + b.totalTokens
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
// src/agent/schemas.ts
|
|
845
|
+
var import_zod = require("zod");
|
|
846
|
+
var PlanStepSchema = import_zod.z.object({
|
|
847
|
+
description: import_zod.z.string(),
|
|
848
|
+
expectedOutcome: import_zod.z.string().optional(),
|
|
849
|
+
suggestedTools: import_zod.z.array(import_zod.z.string()).optional()
|
|
850
|
+
});
|
|
851
|
+
var PlanSchema = import_zod.z.object({
|
|
852
|
+
/** One short sentence. For a greeting/question/unclear request, holds the answer and steps is empty. */
|
|
853
|
+
thought: import_zod.z.string(),
|
|
854
|
+
steps: import_zod.z.array(PlanStepSchema)
|
|
855
|
+
});
|
|
856
|
+
var ReplanSchema = import_zod.z.object({
|
|
857
|
+
decision: import_zod.z.enum(["continue", "revise", "finish"]),
|
|
858
|
+
reason: import_zod.z.string(),
|
|
859
|
+
/** Replacement for the REMAINING steps when decision is 'revise'. */
|
|
860
|
+
plan: import_zod.z.array(import_zod.z.string()).optional()
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
// src/agent/planner.ts
|
|
864
|
+
var toSteps = (raw) => raw.filter((s) => s.description && s.description.trim()).map((s, i) => ({
|
|
865
|
+
id: `s${i + 1}`,
|
|
866
|
+
description: s.description.trim(),
|
|
867
|
+
expectedOutcome: s.expectedOutcome,
|
|
868
|
+
suggestedTools: s.suggestedTools
|
|
869
|
+
}));
|
|
870
|
+
var createPlan = async (ctx, goal) => {
|
|
871
|
+
const state = await ctx.state();
|
|
872
|
+
const parts = ctx.prompts.planner({
|
|
873
|
+
goal,
|
|
874
|
+
state,
|
|
875
|
+
toolCatalog: ctx.toolCatalog,
|
|
876
|
+
mode: ctx.plannerMode
|
|
877
|
+
});
|
|
878
|
+
const system = systemFor(ctx, parts.system);
|
|
879
|
+
const common = {
|
|
880
|
+
system,
|
|
881
|
+
prompt: parts.prompt,
|
|
882
|
+
maxOutputTokens: ctx.config.budgets.planner,
|
|
883
|
+
temperature: ctx.config.temperature,
|
|
884
|
+
abortSignal: ctx.signal,
|
|
885
|
+
timeoutMs: ctx.config.chatTimeoutMs
|
|
886
|
+
};
|
|
887
|
+
if (ctx.plannerMode === "native") {
|
|
888
|
+
try {
|
|
889
|
+
const result2 = await generateStructured(ctx.plannerModel, PlanSchema, common);
|
|
890
|
+
return {
|
|
891
|
+
plan: { thought: result2.object.thought, steps: toSteps(result2.object.steps) },
|
|
892
|
+
usage: normalizeUsage(result2.usage)
|
|
893
|
+
};
|
|
894
|
+
} catch (err) {
|
|
895
|
+
ctx.emit({ type: "retry", phase: "plan", attempt: 1, error: asMessage(err) });
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
const result = await generate(ctx.plannerModel, common);
|
|
899
|
+
const parsed = parsePlannerResponse(result.text);
|
|
900
|
+
return {
|
|
901
|
+
plan: {
|
|
902
|
+
thought: parsed.reply,
|
|
903
|
+
steps: toSteps(parsed.plan.map((description) => ({ description })))
|
|
904
|
+
},
|
|
905
|
+
usage: ctx.plannerMode === "native" ? emptyUsage() : normalizeUsage(result.usage)
|
|
906
|
+
};
|
|
907
|
+
};
|
|
908
|
+
var asMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
909
|
+
|
|
910
|
+
// src/agent/replanner.ts
|
|
911
|
+
var decideReplan = async (ctx, goal, done, remaining) => {
|
|
912
|
+
const state = await ctx.state();
|
|
913
|
+
const parts = ctx.prompts.replanner({ goal, state, done, remaining, mode: ctx.plannerMode });
|
|
914
|
+
const common = {
|
|
915
|
+
system: systemFor(ctx, parts.system),
|
|
916
|
+
prompt: parts.prompt,
|
|
917
|
+
maxOutputTokens: ctx.config.budgets.replanner,
|
|
918
|
+
temperature: ctx.config.temperature,
|
|
919
|
+
abortSignal: ctx.signal,
|
|
920
|
+
timeoutMs: ctx.config.chatTimeoutMs
|
|
921
|
+
};
|
|
922
|
+
try {
|
|
923
|
+
if (ctx.plannerMode === "native") {
|
|
924
|
+
const result2 = await generateStructured(ctx.plannerModel, ReplanSchema, common);
|
|
925
|
+
return {
|
|
926
|
+
decision: result2.object.decision,
|
|
927
|
+
reason: result2.object.reason,
|
|
928
|
+
plan: result2.object.plan ?? [],
|
|
929
|
+
usage: normalizeUsage(result2.usage)
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
const result = await generate(ctx.plannerModel, common);
|
|
933
|
+
const parsed = parseReplannerResponse(result.text);
|
|
934
|
+
return {
|
|
935
|
+
decision: parsed.decision,
|
|
936
|
+
reason: parsed.reason,
|
|
937
|
+
plan: parsed.plan,
|
|
938
|
+
usage: normalizeUsage(result.usage)
|
|
939
|
+
};
|
|
940
|
+
} catch {
|
|
941
|
+
return { decision: "continue", reason: "replanner unavailable", plan: [], usage: emptyUsage() };
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
// src/agent/synthesizer.ts
|
|
946
|
+
var synthesizeAnswer = async (ctx, goal, done) => {
|
|
947
|
+
const state = await ctx.state();
|
|
948
|
+
const parts = ctx.prompts.synthesizer({ goal, state, done });
|
|
949
|
+
const result = await generate(ctx.synthesizerModel, {
|
|
950
|
+
system: systemFor(ctx, parts.system),
|
|
951
|
+
prompt: parts.prompt,
|
|
952
|
+
maxOutputTokens: ctx.config.budgets.synthesizer,
|
|
953
|
+
temperature: ctx.config.temperature,
|
|
954
|
+
abortSignal: ctx.signal,
|
|
955
|
+
timeoutMs: ctx.config.chatTimeoutMs
|
|
956
|
+
});
|
|
957
|
+
const clean = parsePlainText(result.text);
|
|
958
|
+
return {
|
|
959
|
+
text: clean || "Done \u2014 the changes have been applied.",
|
|
960
|
+
usage: normalizeUsage(result.usage)
|
|
961
|
+
};
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
// src/agent/runner.ts
|
|
965
|
+
var errMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
966
|
+
var createAgent = async (config) => {
|
|
967
|
+
const cfg = resolveConfig(config);
|
|
968
|
+
const [plannerModel, executorModel, synthesizerModel] = await Promise.all([
|
|
969
|
+
buildModelFromStage(
|
|
970
|
+
resolveStage(config.model, config.planner, "planner"),
|
|
971
|
+
config.credentials,
|
|
972
|
+
cfg.clientName
|
|
973
|
+
),
|
|
974
|
+
buildModelFromStage(
|
|
975
|
+
resolveStage(config.model, void 0, "executor"),
|
|
976
|
+
config.credentials,
|
|
977
|
+
cfg.clientName
|
|
978
|
+
),
|
|
979
|
+
buildModelFromStage(
|
|
980
|
+
resolveStage(config.model, config.synthesizer, "synthesizer"),
|
|
981
|
+
config.credentials,
|
|
982
|
+
cfg.clientName
|
|
983
|
+
)
|
|
984
|
+
]);
|
|
985
|
+
const plannerMode = selectToolMode(plannerModel, cfg.toolMode);
|
|
986
|
+
const executorMode = selectToolMode(executorModel, cfg.toolMode);
|
|
987
|
+
const tools = config.tools ?? {};
|
|
988
|
+
const toolCatalog = renderCatalog(tools);
|
|
989
|
+
const prompts = { ...defaultPrompts, ...config.prompts };
|
|
990
|
+
const run = async (goal, opts = {}) => {
|
|
991
|
+
const emit = (event) => {
|
|
992
|
+
try {
|
|
993
|
+
opts.onEvent?.(event);
|
|
994
|
+
} catch {
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
const text = goal.trim();
|
|
998
|
+
const sessionId = opts.sessionId ?? cfg.sessionId;
|
|
999
|
+
let usage = emptyUsage();
|
|
1000
|
+
const result = {
|
|
1001
|
+
goal: text,
|
|
1002
|
+
final: "",
|
|
1003
|
+
plan: { thought: "", steps: [] },
|
|
1004
|
+
trace: [],
|
|
1005
|
+
steps: 0,
|
|
1006
|
+
applied: 0,
|
|
1007
|
+
stopped: false,
|
|
1008
|
+
usage
|
|
1009
|
+
};
|
|
1010
|
+
if (!text) return result;
|
|
1011
|
+
const state = async () => config.describeState ? config.describeState() : void 0;
|
|
1012
|
+
const ctx = {
|
|
1013
|
+
config: cfg,
|
|
1014
|
+
raw: config,
|
|
1015
|
+
plannerModel,
|
|
1016
|
+
executorModel,
|
|
1017
|
+
synthesizerModel,
|
|
1018
|
+
plannerMode,
|
|
1019
|
+
executorMode,
|
|
1020
|
+
tools,
|
|
1021
|
+
toolCatalog,
|
|
1022
|
+
prompts,
|
|
1023
|
+
emit,
|
|
1024
|
+
signal: opts.signal,
|
|
1025
|
+
state
|
|
1026
|
+
};
|
|
1027
|
+
const isAborted = () => opts.signal?.aborted === true;
|
|
1028
|
+
const bumpUsage = (u, phase) => {
|
|
1029
|
+
usage = addUsage(usage, u);
|
|
1030
|
+
result.usage = usage;
|
|
1031
|
+
emit({ type: "usage", phase, usage: u });
|
|
1032
|
+
};
|
|
1033
|
+
const remember = async (msg) => {
|
|
1034
|
+
if (!config.memory) return;
|
|
1035
|
+
try {
|
|
1036
|
+
await config.memory.append(sessionId, msg);
|
|
1037
|
+
} catch {
|
|
1038
|
+
}
|
|
1039
|
+
};
|
|
1040
|
+
emit({ type: "run.start", goal: text });
|
|
1041
|
+
await remember({ role: "user", content: text });
|
|
1042
|
+
try {
|
|
1043
|
+
const planned = await createPlan(ctx, text);
|
|
1044
|
+
bumpUsage(planned.usage, "plan");
|
|
1045
|
+
result.plan = planned.plan;
|
|
1046
|
+
if (isAborted()) {
|
|
1047
|
+
emit({ type: "stopped" });
|
|
1048
|
+
result.stopped = true;
|
|
1049
|
+
return result;
|
|
1050
|
+
}
|
|
1051
|
+
if (planned.plan.steps.length === 0) {
|
|
1052
|
+
const answer = planned.plan.thought.trim() || "Tell me what you'd like to do.";
|
|
1053
|
+
emit({ type: "final", text: answer });
|
|
1054
|
+
await remember({ role: "assistant", content: answer });
|
|
1055
|
+
result.final = answer;
|
|
1056
|
+
return result;
|
|
1057
|
+
}
|
|
1058
|
+
emit({ type: "plan.created", plan: planned.plan });
|
|
1059
|
+
planned.plan.steps.forEach((step, index) => emit({ type: "plan.step-added", step, index }));
|
|
1060
|
+
const done = [];
|
|
1061
|
+
let remaining = [...planned.plan.steps];
|
|
1062
|
+
let iter = 0;
|
|
1063
|
+
let revisions = 0;
|
|
1064
|
+
while (remaining.length > 0 && iter < cfg.maxIterations) {
|
|
1065
|
+
if (isAborted()) {
|
|
1066
|
+
emit({ type: "stopped" });
|
|
1067
|
+
result.stopped = true;
|
|
1068
|
+
return result;
|
|
1069
|
+
}
|
|
1070
|
+
iter += 1;
|
|
1071
|
+
const step = remaining.shift();
|
|
1072
|
+
const stepNo = done.length + 1;
|
|
1073
|
+
const total = stepNo + remaining.length;
|
|
1074
|
+
emit({ type: "step.start", step, index: stepNo, total });
|
|
1075
|
+
let stepResult;
|
|
1076
|
+
try {
|
|
1077
|
+
const out = await executeStep(ctx, text, step, stepNo, total, done);
|
|
1078
|
+
bumpUsage(out.usage, "execute");
|
|
1079
|
+
stepResult = out.result;
|
|
1080
|
+
} catch (err) {
|
|
1081
|
+
emit({ type: "error", phase: "execute", error: errMessage(err) });
|
|
1082
|
+
stepResult = { step, summary: errMessage(err), toolCalls: [], blocked: true };
|
|
1083
|
+
}
|
|
1084
|
+
result.trace.push(stepResult);
|
|
1085
|
+
const applied = stepResult.toolCalls.filter((c) => c.ok).length;
|
|
1086
|
+
const failed = stepResult.toolCalls.length - applied;
|
|
1087
|
+
result.applied += applied;
|
|
1088
|
+
result.steps = stepNo;
|
|
1089
|
+
emit({ type: "step.complete", step, result: stepResult });
|
|
1090
|
+
done.push(
|
|
1091
|
+
`${step.description} \u2014 ${applied} applied${failed ? `, ${failed} failed` : ""}${stepResult.blocked ? ", blocked" : ""}`
|
|
1092
|
+
);
|
|
1093
|
+
if (cfg.replan && remaining.length > 0 && iter < cfg.maxIterations && revisions < cfg.maxRevisions && !isAborted() && shouldReplan(stepResult)) {
|
|
1094
|
+
const decision = await decideReplan(
|
|
1095
|
+
ctx,
|
|
1096
|
+
text,
|
|
1097
|
+
done,
|
|
1098
|
+
remaining.map((s) => s.description)
|
|
1099
|
+
);
|
|
1100
|
+
bumpUsage(decision.usage, "replan");
|
|
1101
|
+
emit({ type: "replan.decision", mode: decision.decision, reason: decision.reason });
|
|
1102
|
+
if (decision.decision === "finish") break;
|
|
1103
|
+
if (decision.decision === "revise" && decision.plan.length > 0) {
|
|
1104
|
+
revisions += 1;
|
|
1105
|
+
remaining = decision.plan.slice(0, cfg.maxIterations - iter).map((description, i) => ({ id: `r${iter}-${i + 1}`, description }));
|
|
1106
|
+
emit({
|
|
1107
|
+
type: "plan.revised",
|
|
1108
|
+
plan: { thought: planned.plan.thought, steps: remaining },
|
|
1109
|
+
reason: decision.reason
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
if (isAborted()) {
|
|
1115
|
+
emit({ type: "stopped" });
|
|
1116
|
+
result.stopped = true;
|
|
1117
|
+
return result;
|
|
1118
|
+
}
|
|
1119
|
+
let summary = "Done \u2014 the changes have been applied.";
|
|
1120
|
+
if (cfg.synthesize) {
|
|
1121
|
+
try {
|
|
1122
|
+
const synth = await synthesizeAnswer(ctx, text, done);
|
|
1123
|
+
bumpUsage(synth.usage, "synthesize");
|
|
1124
|
+
if (synth.text) summary = synth.text;
|
|
1125
|
+
} catch {
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
emit({ type: "final", text: summary });
|
|
1129
|
+
await remember({ role: "assistant", content: summary });
|
|
1130
|
+
result.final = summary;
|
|
1131
|
+
if (config.memory && cfg.compressAfterChars > 0) {
|
|
1132
|
+
try {
|
|
1133
|
+
const history = await config.memory.load(sessionId);
|
|
1134
|
+
const compacted = await compressHistory(history, synthesizerModel, {
|
|
1135
|
+
maxChars: cfg.compressAfterChars
|
|
1136
|
+
});
|
|
1137
|
+
if (compacted !== history) await config.memory.replace(sessionId, compacted);
|
|
1138
|
+
} catch {
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
return result;
|
|
1142
|
+
} catch (err) {
|
|
1143
|
+
emit({ type: "error", phase: "run", error: errMessage(err) });
|
|
1144
|
+
result.final = errMessage(err);
|
|
1145
|
+
return result;
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
return {
|
|
1149
|
+
run,
|
|
1150
|
+
models: { planner: plannerModel, executor: executorModel, synthesizer: synthesizerModel }
|
|
1151
|
+
};
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
// src/index.ts
|
|
1155
|
+
init_webllm();
|
|
1156
|
+
|
|
1157
|
+
// src/providers/capabilities.ts
|
|
1158
|
+
var CLOUD = /* @__PURE__ */ new Set([
|
|
1159
|
+
"openai",
|
|
1160
|
+
"anthropic",
|
|
1161
|
+
"google",
|
|
1162
|
+
"openai-compatible",
|
|
1163
|
+
"xai",
|
|
1164
|
+
"deepseek",
|
|
1165
|
+
"gateway"
|
|
1166
|
+
]);
|
|
1167
|
+
var supportsNativeTools = (p) => CLOUD.has(p);
|
|
1168
|
+
var supportsStructuredOutput = (p) => CLOUD.has(p);
|
|
1169
|
+
var directBrowserOk = (p) => p === "google" || p === "gateway" || p === "openai-compatible" || p === "anthropic";
|
|
1170
|
+
|
|
1171
|
+
// src/storage/db.ts
|
|
1172
|
+
var import_idb = require("idb");
|
|
1173
|
+
var KEYS_STORE = "keys";
|
|
1174
|
+
var SECRETS_STORE = "secrets";
|
|
1175
|
+
var SESSIONS_STORE = "sessions";
|
|
1176
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1177
|
+
var openAgentWebDB = (opts = {}) => {
|
|
1178
|
+
const name = opts.dbName ?? "agent-web";
|
|
1179
|
+
let p = cache.get(name);
|
|
1180
|
+
if (!p) {
|
|
1181
|
+
p = (0, import_idb.openDB)(name, 1, {
|
|
1182
|
+
upgrade(db) {
|
|
1183
|
+
if (!db.objectStoreNames.contains(KEYS_STORE)) db.createObjectStore(KEYS_STORE);
|
|
1184
|
+
if (!db.objectStoreNames.contains(SECRETS_STORE)) db.createObjectStore(SECRETS_STORE);
|
|
1185
|
+
if (!db.objectStoreNames.contains(SESSIONS_STORE)) db.createObjectStore(SESSIONS_STORE);
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
1188
|
+
cache.set(name, p);
|
|
1189
|
+
}
|
|
1190
|
+
return p;
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
// src/secrets/crypto.ts
|
|
1194
|
+
var KEY_ID = "vault-key-v1";
|
|
1195
|
+
var webcrypto = () => {
|
|
1196
|
+
const c = globalThis.crypto;
|
|
1197
|
+
if (!c?.subtle || !c.getRandomValues) {
|
|
1198
|
+
throw new Error("WebCrypto (crypto.subtle) is unavailable in this environment");
|
|
1199
|
+
}
|
|
1200
|
+
return c;
|
|
1201
|
+
};
|
|
1202
|
+
var getOrCreateVaultKey = async (dbName) => {
|
|
1203
|
+
const db = await openAgentWebDB({ dbName });
|
|
1204
|
+
const existing = await db.get(KEYS_STORE, KEY_ID);
|
|
1205
|
+
if (existing) return existing;
|
|
1206
|
+
const key = await webcrypto().subtle.generateKey(
|
|
1207
|
+
{ name: "AES-GCM", length: 256 },
|
|
1208
|
+
/* extractable */
|
|
1209
|
+
false,
|
|
1210
|
+
["encrypt", "decrypt"]
|
|
1211
|
+
);
|
|
1212
|
+
await db.put(KEYS_STORE, key, KEY_ID);
|
|
1213
|
+
return key;
|
|
1214
|
+
};
|
|
1215
|
+
var textEncoder = new TextEncoder();
|
|
1216
|
+
var textDecoder = new TextDecoder();
|
|
1217
|
+
var encryptJSON = async (key, value) => {
|
|
1218
|
+
const c = webcrypto();
|
|
1219
|
+
const iv = c.getRandomValues(new Uint8Array(12));
|
|
1220
|
+
const data = textEncoder.encode(JSON.stringify(value));
|
|
1221
|
+
const ciphertext = await c.subtle.encrypt({ name: "AES-GCM", iv }, key, data);
|
|
1222
|
+
return { iv, ciphertext };
|
|
1223
|
+
};
|
|
1224
|
+
var decryptJSON = async (key, blob) => {
|
|
1225
|
+
const iv = new Uint8Array(blob.iv);
|
|
1226
|
+
const plain = await webcrypto().subtle.decrypt({ name: "AES-GCM", iv }, key, blob.ciphertext);
|
|
1227
|
+
return JSON.parse(textDecoder.decode(plain));
|
|
1228
|
+
};
|
|
1229
|
+
|
|
1230
|
+
// src/secrets/vault.ts
|
|
1231
|
+
var IndexedDBVault = class {
|
|
1232
|
+
dbName;
|
|
1233
|
+
constructor(opts = {}) {
|
|
1234
|
+
this.dbName = opts.dbName;
|
|
1235
|
+
}
|
|
1236
|
+
key() {
|
|
1237
|
+
return getOrCreateVaultKey(this.dbName);
|
|
1238
|
+
}
|
|
1239
|
+
/** Encrypt and store a secret string under `id`. */
|
|
1240
|
+
async setSecret(id, value) {
|
|
1241
|
+
const blob = await encryptJSON(await this.key(), value);
|
|
1242
|
+
const db = await openAgentWebDB({ dbName: this.dbName });
|
|
1243
|
+
await db.put(SECRETS_STORE, blob, id);
|
|
1244
|
+
}
|
|
1245
|
+
/** Fetch and decrypt a secret, or undefined if absent. */
|
|
1246
|
+
async getSecret(id) {
|
|
1247
|
+
const db = await openAgentWebDB({ dbName: this.dbName });
|
|
1248
|
+
const blob = await db.get(SECRETS_STORE, id);
|
|
1249
|
+
if (!blob) return void 0;
|
|
1250
|
+
return decryptJSON(await this.key(), blob);
|
|
1251
|
+
}
|
|
1252
|
+
async deleteSecret(id) {
|
|
1253
|
+
const db = await openAgentWebDB({ dbName: this.dbName });
|
|
1254
|
+
await db.delete(SECRETS_STORE, id);
|
|
1255
|
+
}
|
|
1256
|
+
async listSecretIds() {
|
|
1257
|
+
const db = await openAgentWebDB({ dbName: this.dbName });
|
|
1258
|
+
return (await db.getAllKeys(SECRETS_STORE)).map(String);
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
|
|
1262
|
+
// src/secrets/store.ts
|
|
1263
|
+
var VaultCredentialStore = class {
|
|
1264
|
+
vault;
|
|
1265
|
+
constructor(opts = {}) {
|
|
1266
|
+
this.vault = new IndexedDBVault(opts);
|
|
1267
|
+
}
|
|
1268
|
+
getApiKey(ref) {
|
|
1269
|
+
return this.vault.getSecret(ref);
|
|
1270
|
+
}
|
|
1271
|
+
setApiKey(ref, key) {
|
|
1272
|
+
return this.vault.setSecret(ref, key);
|
|
1273
|
+
}
|
|
1274
|
+
deleteApiKey(ref) {
|
|
1275
|
+
return this.vault.deleteSecret(ref);
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
var MemoryCredentialStore = class {
|
|
1279
|
+
map = /* @__PURE__ */ new Map();
|
|
1280
|
+
constructor(initial) {
|
|
1281
|
+
if (initial) for (const [k, v] of Object.entries(initial)) this.map.set(k, v);
|
|
1282
|
+
}
|
|
1283
|
+
async getApiKey(ref) {
|
|
1284
|
+
return this.map.get(ref);
|
|
1285
|
+
}
|
|
1286
|
+
async setApiKey(ref, key) {
|
|
1287
|
+
this.map.set(ref, key);
|
|
1288
|
+
}
|
|
1289
|
+
async deleteApiKey(ref) {
|
|
1290
|
+
this.map.delete(ref);
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
|
|
1294
|
+
// src/tools/define.ts
|
|
1295
|
+
var import_ai3 = require("ai");
|
|
1296
|
+
var defineTool = (config) => {
|
|
1297
|
+
const { promptHint, ...rest } = config;
|
|
1298
|
+
const t = (0, import_ai3.tool)(rest);
|
|
1299
|
+
if (promptHint) t.promptHint = promptHint;
|
|
1300
|
+
return t;
|
|
1301
|
+
};
|
|
1302
|
+
|
|
1303
|
+
// src/memory/store.ts
|
|
1304
|
+
var MemoryStore = class {
|
|
1305
|
+
data = /* @__PURE__ */ new Map();
|
|
1306
|
+
async load(sessionId) {
|
|
1307
|
+
return (this.data.get(sessionId) ?? []).slice();
|
|
1308
|
+
}
|
|
1309
|
+
async append(sessionId, message) {
|
|
1310
|
+
const arr = this.data.get(sessionId) ?? [];
|
|
1311
|
+
arr.push(message);
|
|
1312
|
+
this.data.set(sessionId, arr);
|
|
1313
|
+
}
|
|
1314
|
+
async replace(sessionId, messages) {
|
|
1315
|
+
this.data.set(sessionId, messages.slice());
|
|
1316
|
+
}
|
|
1317
|
+
async clear(sessionId) {
|
|
1318
|
+
this.data.delete(sessionId);
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
|
|
1322
|
+
// src/memory/sessions.ts
|
|
1323
|
+
var IndexedDBStore = class {
|
|
1324
|
+
dbName;
|
|
1325
|
+
constructor(opts = {}) {
|
|
1326
|
+
this.dbName = opts.dbName;
|
|
1327
|
+
}
|
|
1328
|
+
async load(sessionId) {
|
|
1329
|
+
const db = await openAgentWebDB({ dbName: this.dbName });
|
|
1330
|
+
const value = await db.get(SESSIONS_STORE, sessionId);
|
|
1331
|
+
return Array.isArray(value) ? value : [];
|
|
1332
|
+
}
|
|
1333
|
+
async replace(sessionId, messages) {
|
|
1334
|
+
const db = await openAgentWebDB({ dbName: this.dbName });
|
|
1335
|
+
await db.put(SESSIONS_STORE, messages, sessionId);
|
|
1336
|
+
}
|
|
1337
|
+
async append(sessionId, message) {
|
|
1338
|
+
const current = await this.load(sessionId);
|
|
1339
|
+
current.push(message);
|
|
1340
|
+
await this.replace(sessionId, current);
|
|
1341
|
+
}
|
|
1342
|
+
async clear(sessionId) {
|
|
1343
|
+
const db = await openAgentWebDB({ dbName: this.dbName });
|
|
1344
|
+
await db.delete(SESSIONS_STORE, sessionId);
|
|
1345
|
+
}
|
|
1346
|
+
/** List all stored session ids. */
|
|
1347
|
+
async listSessions() {
|
|
1348
|
+
const db = await openAgentWebDB({ dbName: this.dbName });
|
|
1349
|
+
return (await db.getAllKeys(SESSIONS_STORE)).map(String);
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1352
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1353
|
+
0 && (module.exports = {
|
|
1354
|
+
IndexedDBStore,
|
|
1355
|
+
IndexedDBVault,
|
|
1356
|
+
KEYS_STORE,
|
|
1357
|
+
MemoryCredentialStore,
|
|
1358
|
+
MemoryStore,
|
|
1359
|
+
PlanSchema,
|
|
1360
|
+
PlanStepSchema,
|
|
1361
|
+
ReplanSchema,
|
|
1362
|
+
SECRETS_STORE,
|
|
1363
|
+
SESSIONS_STORE,
|
|
1364
|
+
VaultCredentialStore,
|
|
1365
|
+
buildModelFromStage,
|
|
1366
|
+
compressHistory,
|
|
1367
|
+
createAgent,
|
|
1368
|
+
createPlan,
|
|
1369
|
+
createWebLLMModel,
|
|
1370
|
+
decideReplan,
|
|
1371
|
+
decryptJSON,
|
|
1372
|
+
defaultPrompts,
|
|
1373
|
+
defineTool,
|
|
1374
|
+
directBrowserOk,
|
|
1375
|
+
dispatch,
|
|
1376
|
+
encryptJSON,
|
|
1377
|
+
executeStep,
|
|
1378
|
+
generate,
|
|
1379
|
+
generateStructured,
|
|
1380
|
+
getOrCreateVaultKey,
|
|
1381
|
+
isDirectModel,
|
|
1382
|
+
isProviderSpec,
|
|
1383
|
+
isWebGPUAvailable,
|
|
1384
|
+
looksLikeJson,
|
|
1385
|
+
normalizeSteps,
|
|
1386
|
+
normalizeUsage,
|
|
1387
|
+
openAgentWebDB,
|
|
1388
|
+
parseExecutorResponse,
|
|
1389
|
+
parsePlainText,
|
|
1390
|
+
parsePlannerResponse,
|
|
1391
|
+
parseReplannerResponse,
|
|
1392
|
+
promptHintOf,
|
|
1393
|
+
renderCatalog,
|
|
1394
|
+
resolveConfig,
|
|
1395
|
+
resolveModel,
|
|
1396
|
+
resolveStage,
|
|
1397
|
+
runToolLoop,
|
|
1398
|
+
selectToolMode,
|
|
1399
|
+
shouldReplan,
|
|
1400
|
+
splitBlocker,
|
|
1401
|
+
stream,
|
|
1402
|
+
supportsNativeTools,
|
|
1403
|
+
supportsStructuredOutput,
|
|
1404
|
+
synthesizeAnswer,
|
|
1405
|
+
withSystem
|
|
1406
|
+
});
|
|
1407
|
+
//# sourceMappingURL=index.cjs.map
|