@dudko.dev/agent-web 0.0.6 → 0.0.8
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 +4 -1
- package/dist/agent-web.js +101 -86
- package/dist/agent-web.js.map +1 -1
- package/dist/index.cjs +107 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -43
- package/dist/index.d.ts +76 -43
- package/dist/index.js +106 -29
- package/dist/index.js.map +1 -1
- package/package.json +15 -15
package/dist/index.cjs
CHANGED
|
@@ -119,6 +119,7 @@ __export(src_exports, {
|
|
|
119
119
|
preloadWebLLMModel: () => preloadWebLLMModel,
|
|
120
120
|
promptHintOf: () => promptHintOf,
|
|
121
121
|
renderCatalog: () => renderCatalog,
|
|
122
|
+
replanWanted: () => replanWanted,
|
|
122
123
|
resolveConfig: () => resolveConfig,
|
|
123
124
|
resolveModel: () => resolveModel,
|
|
124
125
|
resolveStage: () => resolveStage,
|
|
@@ -153,6 +154,7 @@ var resolveConfig = (c) => ({
|
|
|
153
154
|
},
|
|
154
155
|
temperature: c.temperature,
|
|
155
156
|
replan: c.replan ?? true,
|
|
157
|
+
replanAfter: c.replanAfter ?? "failure",
|
|
156
158
|
synthesize: c.synthesize ?? true,
|
|
157
159
|
compressAfterChars: c.compressAfterChars ?? 12e3,
|
|
158
160
|
logLevel: c.logLevel ?? "warn"
|
|
@@ -162,6 +164,7 @@ var resolveConfig = (c) => ({
|
|
|
162
164
|
var import_ai = require("ai");
|
|
163
165
|
|
|
164
166
|
// src/llm/util.ts
|
|
167
|
+
var clip = (s, max) => s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
165
168
|
var promptOf = (opts) => opts.messages ? { messages: opts.messages } : { prompt: opts.prompt ?? "" };
|
|
166
169
|
var normalizeUsage = (u) => {
|
|
167
170
|
const inputTokens = u?.inputTokens ?? 0;
|
|
@@ -434,7 +437,6 @@ var stateBlock = (state) => state && state.trim() ? `
|
|
|
434
437
|
|
|
435
438
|
STATE:
|
|
436
439
|
${state}` : "";
|
|
437
|
-
var clip = (s, max) => s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
438
440
|
var historyBlock = (history) => {
|
|
439
441
|
if (!history || history.length === 0) return "";
|
|
440
442
|
const lines = history.slice(-8).map((m) => `${m.role}: ${clip(m.content, 400)}`);
|
|
@@ -467,8 +469,9 @@ Reply with a single JSON object, nothing else:
|
|
|
467
469
|
- "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.
|
|
468
470
|
- Build on the current STATE \u2014 do not repeat work that is already there.
|
|
469
471
|
- "reply" is one short human sentence (no JSON) describing what you did.
|
|
472
|
+
- After your actions run you will see their TOOL RESULTS and may continue the same step; finish with "actions": [] once it is done.
|
|
470
473
|
- If you CANNOT complete the step, set "actions": [] and put the token [BLOCKER] in "reply" with a short reason.`;
|
|
471
|
-
var REPLANNER_NATIVE = `You are the REPLANNER. After
|
|
474
|
+
var REPLANNER_NATIVE = `You are the REPLANNER. After an executed step, decide whether to keep going, revise the remaining steps, or finish.
|
|
472
475
|
"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.
|
|
473
476
|
Judge from the current STATE vs the goal. Prefer "continue".`;
|
|
474
477
|
var REPLANNER_PROMPTED = `You are the REPLANNER. After each executed step you decide whether to keep going, revise the remaining steps, or finish.
|
|
@@ -598,6 +601,15 @@ var dispatch = async (action, tools, ctx = {}) => {
|
|
|
598
601
|
// src/llm/tool-loop.ts
|
|
599
602
|
var import_ai3 = require("ai");
|
|
600
603
|
|
|
604
|
+
// src/agent/loop-types.ts
|
|
605
|
+
var BLOCKER = "[BLOCKER]";
|
|
606
|
+
var emptyUsage = () => ({ inputTokens: 0, outputTokens: 0, totalTokens: 0 });
|
|
607
|
+
var addUsage = (a, b) => ({
|
|
608
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
609
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
610
|
+
totalTokens: a.totalTokens + b.totalTokens
|
|
611
|
+
});
|
|
612
|
+
|
|
601
613
|
// src/parse.ts
|
|
602
614
|
var stripFences = (text) => (text || "").replace(/```(?:json)?/gi, "").trim();
|
|
603
615
|
var extractBalanced = (text, open) => {
|
|
@@ -796,34 +808,74 @@ var runNative = async (model, opts) => {
|
|
|
796
808
|
const [text, usage] = await Promise.all([result.text, result.usage]);
|
|
797
809
|
return { text: text.trim(), toolCalls, usage: normalizeUsage(usage) };
|
|
798
810
|
};
|
|
811
|
+
var asJson = (value) => {
|
|
812
|
+
try {
|
|
813
|
+
return JSON.stringify(value) ?? String(value);
|
|
814
|
+
} catch {
|
|
815
|
+
return String(value);
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
var toolResultsPrompt = (results) => {
|
|
819
|
+
const lines = results.map(
|
|
820
|
+
(r) => `- ${r.name} ${clip(asJson(r.input), 160)} \u2192 ${r.ok ? "ok" : "FAILED"}: ${clip(asJson(r.output), 400)}`
|
|
821
|
+
);
|
|
822
|
+
return `TOOL RESULTS:
|
|
823
|
+
${lines.join("\n")}
|
|
824
|
+
|
|
825
|
+
Continue THIS step using the results above. Reply with a single JSON object, nothing else:
|
|
826
|
+
{ "reply": string, "actions": [ { "tool": string, "args": object } ] }
|
|
827
|
+
- If the step is now complete: "actions": [] and a short outcome in "reply".
|
|
828
|
+
- Otherwise emit ONLY the remaining or corrective calls \u2014 never repeat a successful call.`;
|
|
829
|
+
};
|
|
799
830
|
var runPrompted = async (model, opts) => {
|
|
800
|
-
const
|
|
801
|
-
model,
|
|
802
|
-
system: opts.system,
|
|
803
|
-
...promptOf(opts),
|
|
804
|
-
maxOutputTokens: opts.maxOutputTokens,
|
|
805
|
-
temperature: opts.temperature,
|
|
806
|
-
abortSignal: timeoutSignal(opts.abortSignal, opts.timeoutMs)
|
|
807
|
-
});
|
|
808
|
-
const parsed = parseExecutorResponse(result.text);
|
|
809
|
-
if (parsed.reply) opts.callbacks?.onTextDelta?.(parsed.reply);
|
|
831
|
+
const maxRounds = Math.max(1, opts.maxSteps ?? 4);
|
|
810
832
|
const active = opts.activeTools;
|
|
811
833
|
const tools = active ? Object.fromEntries(Object.entries(opts.tools).filter(([name]) => active.includes(name))) : opts.tools;
|
|
834
|
+
const signal = timeoutSignal(opts.abortSignal, opts.timeoutMs);
|
|
835
|
+
const messages = opts.messages ? [...opts.messages] : [{ role: "user", content: opts.prompt ?? "" }];
|
|
812
836
|
const toolCalls = [];
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
837
|
+
const seen = /* @__PURE__ */ new Set();
|
|
838
|
+
let usage = emptyUsage();
|
|
839
|
+
let text = "";
|
|
840
|
+
for (let round = 1; round <= maxRounds; round += 1) {
|
|
841
|
+
if (signal?.aborted) break;
|
|
842
|
+
const result = await (0, import_ai3.generateText)({
|
|
843
|
+
model,
|
|
844
|
+
system: opts.system,
|
|
845
|
+
messages,
|
|
846
|
+
maxOutputTokens: opts.maxOutputTokens,
|
|
847
|
+
temperature: opts.temperature,
|
|
848
|
+
abortSignal: signal
|
|
849
|
+
});
|
|
850
|
+
usage = addUsage(usage, normalizeUsage(result.usage));
|
|
851
|
+
const parsed = parseExecutorResponse(result.text);
|
|
852
|
+
text = parsed.reply;
|
|
853
|
+
if (parsed.reply.includes(BLOCKER)) break;
|
|
854
|
+
if (parsed.actions.length === 0) break;
|
|
855
|
+
const batch = asJson(parsed.actions);
|
|
856
|
+
if (seen.has(batch)) break;
|
|
857
|
+
seen.add(batch);
|
|
858
|
+
const results = [];
|
|
859
|
+
for (const action of parsed.actions) {
|
|
860
|
+
if (signal?.aborted) break;
|
|
861
|
+
opts.callbacks?.onToolCall?.(action.tool, action.args);
|
|
862
|
+
const rec = await dispatch(action, tools, { abortSignal: signal });
|
|
863
|
+
toolCalls.push(rec);
|
|
864
|
+
results.push(rec);
|
|
865
|
+
opts.callbacks?.onToolResult?.(rec.name, rec.output, rec.ok);
|
|
866
|
+
}
|
|
867
|
+
if (round === maxRounds || signal?.aborted) break;
|
|
868
|
+
messages.push({ role: "assistant", content: result.text });
|
|
869
|
+
messages.push({ role: "user", content: toolResultsPrompt(results) });
|
|
818
870
|
}
|
|
819
|
-
|
|
871
|
+
if (text) opts.callbacks?.onTextDelta?.(text);
|
|
872
|
+
return { text, toolCalls, usage };
|
|
820
873
|
};
|
|
821
874
|
|
|
822
875
|
// src/agent/internal.ts
|
|
823
876
|
var systemFor = (ctx, base) => withSystem(base, ctx.raw.systemPrompt);
|
|
824
877
|
|
|
825
878
|
// src/agent/executor.ts
|
|
826
|
-
var BLOCKER = "[BLOCKER]";
|
|
827
879
|
var splitBlocker = (raw) => {
|
|
828
880
|
if (!raw.includes(BLOCKER)) return { summary: raw.trim(), blocked: false };
|
|
829
881
|
return { summary: raw.split(BLOCKER).join("").trim(), blocked: true };
|
|
@@ -888,15 +940,35 @@ var executeStep = async (ctx, goal, step, index, total, done) => {
|
|
|
888
940
|
usage: loop.usage
|
|
889
941
|
};
|
|
890
942
|
};
|
|
891
|
-
var shouldReplan = (result) =>
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
943
|
+
var shouldReplan = (result) => {
|
|
944
|
+
if (result.blocked) return true;
|
|
945
|
+
const { toolCalls } = result;
|
|
946
|
+
return toolCalls.some(
|
|
947
|
+
(c, i) => !c.ok && !toolCalls.slice(i + 1).some((later) => later.ok && later.name === c.name)
|
|
948
|
+
);
|
|
949
|
+
};
|
|
950
|
+
var replanWanted = async (trigger, result, opts = {}) => {
|
|
951
|
+
if (trigger === "always") return true;
|
|
952
|
+
if (typeof trigger !== "function") return shouldReplan(result);
|
|
953
|
+
const fallback = () => shouldReplan(result);
|
|
954
|
+
const decided = (async () => {
|
|
955
|
+
try {
|
|
956
|
+
return Boolean(await trigger(result));
|
|
957
|
+
} catch (err) {
|
|
958
|
+
opts.onError?.(err);
|
|
959
|
+
return fallback();
|
|
960
|
+
}
|
|
961
|
+
})();
|
|
962
|
+
const guard = timeoutSignal(opts.signal, opts.timeoutMs);
|
|
963
|
+
if (!guard) return decided;
|
|
964
|
+
return Promise.race([
|
|
965
|
+
decided,
|
|
966
|
+
new Promise((resolve) => {
|
|
967
|
+
if (guard.aborted) resolve(fallback());
|
|
968
|
+
else guard.addEventListener("abort", () => resolve(fallback()), { once: true });
|
|
969
|
+
})
|
|
970
|
+
]);
|
|
971
|
+
};
|
|
900
972
|
|
|
901
973
|
// src/agent/schemas.ts
|
|
902
974
|
var import_zod = require("zod");
|
|
@@ -1249,7 +1321,12 @@ var createAgent = async (config) => {
|
|
|
1249
1321
|
done.push(
|
|
1250
1322
|
`${step.description} \u2014 ${applied} applied${failed ? `, ${failed} failed` : ""}${stepResult.blocked ? ", blocked" : ""}`
|
|
1251
1323
|
);
|
|
1252
|
-
|
|
1324
|
+
const wantReplan = cfg.replan && iter < cfg.maxIterations && revisions < cfg.maxRevisions && !isAborted() && await replanWanted(cfg.replanAfter, stepResult, {
|
|
1325
|
+
signal: ctx.signal,
|
|
1326
|
+
timeoutMs: cfg.chatTimeoutMs,
|
|
1327
|
+
onError: (err) => log.warn("replanAfter predicate threw \u2014 using the failure rule", err)
|
|
1328
|
+
});
|
|
1329
|
+
if (wantReplan && !isAborted()) {
|
|
1253
1330
|
const decision = await decideReplan(
|
|
1254
1331
|
ctx,
|
|
1255
1332
|
text,
|
|
@@ -1578,6 +1655,7 @@ var IndexedDBStore = class {
|
|
|
1578
1655
|
preloadWebLLMModel,
|
|
1579
1656
|
promptHintOf,
|
|
1580
1657
|
renderCatalog,
|
|
1658
|
+
replanWanted,
|
|
1581
1659
|
resolveConfig,
|
|
1582
1660
|
resolveModel,
|
|
1583
1661
|
resolveStage,
|