@wenathlan/extension 1.1.56 → 1.1.57

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.
@@ -2615,6 +2615,119 @@ var sessionmemory = class {
2615
2615
  async setdryruntoggle(enabled) {
2616
2616
  return this.adapter.set("mcpdryruntoggle", enabled);
2617
2617
  }
2618
+ /** Returns every user configured provider config of the 1.1.57 llm integration; the api keys stay behind their storage id references, never inside these records. */
2619
+ async getproviders() {
2620
+ return await this.adapter.get("llmproviders") ?? [];
2621
+ }
2622
+ /** Replaces the stored provider config set after one save, test or removal. */
2623
+ async setproviders(providers) {
2624
+ return this.adapter.set("llmproviders", providers);
2625
+ }
2626
+ /** Returns the user configured local model endpoint of the browser reachable inference. */
2627
+ async getlocalmodel() {
2628
+ return this.adapter.get("llmlocalmodel");
2629
+ }
2630
+ /** Stores the local model endpoint config after one save or health check. */
2631
+ async setlocalmodel(config) {
2632
+ return this.adapter.set("llmlocalmodel", config);
2633
+ }
2634
+ /** Returns every model route entry of the routing table, newest update first. */
2635
+ async getmodelroutes() {
2636
+ return await this.adapter.get("llmmodelroutes") ?? [];
2637
+ }
2638
+ /** Replaces the stored routing table after one route edit. */
2639
+ async setmodelroutes(routes) {
2640
+ return this.adapter.set("llmmodelroutes", routes);
2641
+ }
2642
+ /** Appends one revision entry to the model route revision history so every routing change stays queryable for audit. */
2643
+ async addmodelrouterevision(route) {
2644
+ await this.adapter.set("llmmodelroutehistory", [route, ...await this.adapter.get("llmmodelroutehistory") ?? []].slice(0, 200));
2645
+ }
2646
+ /** Returns the model route revision history, newest first. */
2647
+ async getmodelroutehistory() {
2648
+ return await this.adapter.get("llmmodelroutehistory") ?? [];
2649
+ }
2650
+ /** Records one usage entry of a model call with its run and step ids; the newest call reads first and an absent retention keeps every record. */
2651
+ async addusagerecord(record2) {
2652
+ await this.adapter.set("llmusage", [record2, ...await this.adapter.get("llmusage") ?? []]);
2653
+ }
2654
+ /** Returns every stored usage record of model calls, newest first. */
2655
+ async getusagerecords() {
2656
+ return await this.adapter.get("llmusage") ?? [];
2657
+ }
2658
+ /** Returns the token and cost totals per period: the run, the step, the since floor and the until ceiling stay optional filters over the stored usage records. */
2659
+ async getusage(filter = {}) {
2660
+ const records = (await this.getusagerecords()).filter((record2) => (filter.runid === void 0 || record2.runid === filter.runid) && (filter.stepid === void 0 || record2.stepid === filter.stepid) && (filter.since === void 0 || record2.at >= filter.since) && (filter.until === void 0 || record2.at <= filter.until));
2661
+ return records.reduce((totals, record2) => ({ prompttokens: totals.prompttokens + record2.prompttokens, completiontokens: totals.completiontokens + record2.completiontokens, totaltokens: totals.totaltokens + record2.totaltokens, cost: totals.cost + record2.cost, calls: totals.calls + 1 }), { prompttokens: 0, completiontokens: 0, totaltokens: 0, cost: 0, calls: 0 });
2662
+ }
2663
+ /** Stores one model drafted plan for review and audit; newer drafts read first. */
2664
+ async addplandraft(draft) {
2665
+ await this.adapter.set("llmplandrafts", [draft, ...await this.adapter.get("llmplandrafts") ?? []]);
2666
+ }
2667
+ /** Replaces the stored draft set after one review decision. */
2668
+ async setplandrafts(drafts) {
2669
+ return this.adapter.set("llmplandrafts", drafts);
2670
+ }
2671
+ /** Returns every stored model drafted plan, newest first. */
2672
+ async getplandrafts() {
2673
+ return await this.adapter.get("llmplandrafts") ?? [];
2674
+ }
2675
+ /** Stores one replan record for the fresh review and the audit history; newer replans read first. */
2676
+ async addreplan(replan) {
2677
+ await this.adapter.set("llmreplans", [replan, ...await this.adapter.get("llmreplans") ?? []]);
2678
+ }
2679
+ /** Replaces the stored replan set after one fresh review decision. */
2680
+ async setreplans(replans) {
2681
+ return this.adapter.set("llmreplans", replans);
2682
+ }
2683
+ /** Returns every stored replan record, newest first. */
2684
+ async getreplans() {
2685
+ return await this.adapter.get("llmreplans") ?? [];
2686
+ }
2687
+ /** Stores one reflection note of an executed step under the recent note window of 100 records. */
2688
+ async addreflectnote(note) {
2689
+ await this.adapter.set("llmreflectnotes", [note, ...await this.adapter.get("llmreflectnotes") ?? []].slice(0, 100));
2690
+ }
2691
+ /** Returns the stored reflection notes, newest first. */
2692
+ async getreflectnotes() {
2693
+ return await this.adapter.get("llmreflectnotes") ?? [];
2694
+ }
2695
+ /** Replaces the stored prompt template library after one save or removal; every version with its change notes stays stored. */
2696
+ async setprompttemplates(templates) {
2697
+ return this.adapter.set("llmprompttemplates", templates);
2698
+ }
2699
+ /** Returns the stored prompt template library with every version, newest first. */
2700
+ async getprompttemplates() {
2701
+ return await this.adapter.get("llmprompttemplates") ?? [];
2702
+ }
2703
+ /** Returns the stored cost budget of the runs; the run scoped budget wins over the shared one when both exist. */
2704
+ async getcostbudget(runid) {
2705
+ const budgets = await this.adapter.get("llmcostbudgets") ?? [];
2706
+ return budgets.find((budget) => runid !== void 0 && budget.runid === runid) ?? budgets.find((budget) => budget.runid === void 0);
2707
+ }
2708
+ /** Stores one cost budget; a run scoped budget replaces the earlier budget of its run while the shared budget replaces the shared one. */
2709
+ async setcostbudget(budget) {
2710
+ const budgets = await this.adapter.get("llmcostbudgets") ?? [];
2711
+ const kept = budgets.filter((candidate) => candidate.runid !== budget.runid);
2712
+ await this.adapter.set("llmcostbudgets", [budget, ...kept]);
2713
+ }
2714
+ /** Returns the latest parsed natural language command with its intent badge payload. */
2715
+ async getcommandparse() {
2716
+ return this.adapter.get("llmcommandparse");
2717
+ }
2718
+ /** Stores the latest parsed natural language command. */
2719
+ async setcommandparse(parse) {
2720
+ return this.adapter.set("llmcommandparse", parse);
2721
+ }
2722
+ /** Returns the recent guard refusal notices of invalid or refused model output, newest first under a window of 50. */
2723
+ async getguardnotices() {
2724
+ return await this.adapter.get("llmguardnotices") ?? [];
2725
+ }
2726
+ /** Records one guard refusal notice for the panel; the verdict reason explains the parse failure and its retries. */
2727
+ async addguardnotice(output) {
2728
+ if (output.verdict === "valid") return;
2729
+ await this.adapter.set("llmguardnotices", [output, ...await this.getguardnotices()].slice(0, 50));
2730
+ }
2618
2731
  };
2619
2732
  function mediakindof(record2) {
2620
2733
  if ("pages" in record2) return "pdf";
@@ -6875,8 +6988,8 @@ function validatetimelinegrammar(step, options) {
6875
6988
  watchwindow = reviewed.window;
6876
6989
  }
6877
6990
  }
6878
- const budgetcheck = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
6879
- if (!budgetcheck.allowed) return budgetcheck;
6991
+ const budgetcheck2 = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
6992
+ if (!budgetcheck2.allowed) return budgetcheck2;
6880
6993
  if (options.level !== void 0 && !loglevels.includes(options.level)) return { allowed: false, reason: `The reviewed level floor must be one of ${loglevels.join(", ")}.` };
6881
6994
  if (options.sources !== void 0) {
6882
6995
  if (!Array.isArray(options.sources) || options.sources.length === 0 || !options.sources.every((source) => timelinesources.includes(source))) return { allowed: false, reason: `The reviewed source filters must be a non-empty list of the reviewed timeline sources: ${timelinesources.join(", ")}.` };
@@ -7452,8 +7565,8 @@ function validatecdpgrammar(step, options) {
7452
7565
  const allowlist = cdpallowlistof(options.allowlist);
7453
7566
  if (!allowlist || !allowlist.domains.every((domain) => options.domains.includes(domain))) return { allowed: false, reason: "The reviewed method allowlist must stay inside the enabled domains of the attach." };
7454
7567
  }
7455
- const budgetcheck = debugwaitbudgetallowed(typeof options.wait === "number" ? options.wait : void 0, void 0);
7456
- if (!budgetcheck.allowed) return budgetcheck;
7568
+ const budgetcheck2 = debugwaitbudgetallowed(typeof options.wait === "number" ? options.wait : void 0, void 0);
7569
+ if (!budgetcheck2.allowed) return budgetcheck2;
7457
7570
  return { allowed: true };
7458
7571
  }
7459
7572
  if (kind === "detachcdp") return { allowed: true };
@@ -7477,8 +7590,8 @@ function validatecdpgrammar(step, options) {
7477
7590
  }
7478
7591
  }
7479
7592
  if (watchwindow === void 0) return { allowed: false, reason: "The event watch needs a reviewed lifetime window before any domain event is observed." };
7480
- const budgetcheck = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
7481
- if (!budgetcheck.allowed) return budgetcheck;
7593
+ const budgetcheck2 = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
7594
+ if (!budgetcheck2.allowed) return budgetcheck2;
7482
7595
  return { allowed: true };
7483
7596
  }
7484
7597
  if (kind === "setbreakpoint") {
@@ -7515,8 +7628,8 @@ function validateprofilegrammar(step, options) {
7515
7628
  if (flowspecof(options.flow) === void 0) return { allowed: false, reason: `The flow measurement needs a reviewed flow spec with its mark prefix, step window and metric list of the reviewed metric set: navigation, paint, lcp, fid, interaction, blocking.` };
7516
7629
  const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
7517
7630
  if (typeof watch.window !== "number" || !Number.isFinite(watch.window) || watch.window < 0) return { allowed: false, reason: "The flow measurement needs a reviewed watch window of zero or more milliseconds." };
7518
- const budgetcheck = debugwaitbudgetallowed(watch.window, typeof options.wait === "number" ? options.wait : void 0);
7519
- if (!budgetcheck.allowed) return budgetcheck;
7631
+ const budgetcheck2 = debugwaitbudgetallowed(watch.window, typeof options.wait === "number" ? options.wait : void 0);
7632
+ if (!budgetcheck2.allowed) return budgetcheck2;
7520
7633
  return { allowed: true };
7521
7634
  }
7522
7635
  if (kind === "heapshot") {
@@ -7533,16 +7646,16 @@ function validateprofilegrammar(step, options) {
7533
7646
  if (kind === "profilecpu") {
7534
7647
  const profile = options.profile && typeof options.profile === "object" && !Array.isArray(options.profile) ? options.profile : void 0;
7535
7648
  if (!profile || typeof profile.duration !== "number" || !Number.isFinite(profile.duration) || profile.duration < 0) return { allowed: false, reason: "The cpu profile needs a reviewed duration of zero or more milliseconds." };
7536
- const budgetcheck = debugwaitbudgetallowed(profile.duration, typeof options.wait === "number" ? options.wait : void 0);
7537
- if (!budgetcheck.allowed) return budgetcheck;
7649
+ const budgetcheck2 = debugwaitbudgetallowed(profile.duration, typeof options.wait === "number" ? options.wait : void 0);
7650
+ if (!budgetcheck2.allowed) return budgetcheck2;
7538
7651
  return { allowed: true };
7539
7652
  }
7540
7653
  if (kind === "watchshifts") {
7541
7654
  const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
7542
7655
  if (typeof watch.window !== "number" || !Number.isFinite(watch.window) || watch.window < 0) return { allowed: false, reason: "The layout shift watch needs a reviewed observation window of zero or more milliseconds; the window stays a user choice with no code ceiling." };
7543
7656
  if (options.threshold !== void 0 && (typeof options.threshold !== "number" || !Number.isFinite(options.threshold) || options.threshold < 0)) return { allowed: false, reason: "The reviewed shift score threshold must be zero or a positive number." };
7544
- const budgetcheck = debugwaitbudgetallowed(watch.window, typeof options.wait === "number" ? options.wait : void 0);
7545
- if (!budgetcheck.allowed) return budgetcheck;
7657
+ const budgetcheck2 = debugwaitbudgetallowed(watch.window, typeof options.wait === "number" ? options.wait : void 0);
7658
+ if (!budgetcheck2.allowed) return budgetcheck2;
7546
7659
  return { allowed: true };
7547
7660
  }
7548
7661
  if (kind === "traceload") {
@@ -7550,8 +7663,8 @@ function validateprofilegrammar(step, options) {
7550
7663
  if (!trace || !Array.isArray(trace.categories) || trace.categories.length === 0 || !trace.categories.every((category) => typeof category === "string" && tracecategories.includes(category))) return { allowed: false, reason: `The trace record needs a non-empty reviewed category list of the reviewed category grammar: ${tracecategories.join(", ")}.` };
7551
7664
  if (typeof trace.window !== "number" || !Number.isFinite(trace.window) || trace.window < 0) return { allowed: false, reason: "The trace record needs a reviewed window of zero or more milliseconds and stops at the reviewed window end." };
7552
7665
  if (trace.exporttarget !== void 0 && trace.exporttarget !== "memory" && trace.exporttarget !== "download") return { allowed: false, reason: "The trace export target must be memory or download." };
7553
- const budgetcheck = debugwaitbudgetallowed(trace.window, typeof options.wait === "number" ? options.wait : void 0);
7554
- if (!budgetcheck.allowed) return budgetcheck;
7666
+ const budgetcheck2 = debugwaitbudgetallowed(trace.window, typeof options.wait === "number" ? options.wait : void 0);
7667
+ if (!budgetcheck2.allowed) return budgetcheck2;
7555
7668
  return { allowed: true };
7556
7669
  }
7557
7670
  if (kind === "annotatetrace" || kind === "replaytrace") {
@@ -8546,6 +8659,66 @@ function mockusagevalid(mock) {
8546
8659
  if (typeof mock.result.content !== "string") return { allowed: false, reason: "A tool mock needs its canned result content." };
8547
8660
  return { allowed: true };
8548
8661
  }
8662
+ function providervalid(config) {
8663
+ if (config.name.trim() === "") return { allowed: false, reason: "The provider config needs its name." };
8664
+ if (config.endpoint.trim() === "") return { allowed: false, reason: "The provider config needs the user configured endpoint url; no default endpoint ever applies." };
8665
+ let parsed;
8666
+ try {
8667
+ parsed = new URL(config.endpoint);
8668
+ } catch {
8669
+ return { allowed: false, reason: "The provider endpoint must be a well-formed url." };
8670
+ }
8671
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return { allowed: false, reason: "The provider endpoint must speak http or https." };
8672
+ if (config.models.length === 0) return { allowed: false, reason: "The provider config needs at least one user configured model name." };
8673
+ if (config.models.some((model) => model.trim() === "")) return { allowed: false, reason: "Every provider model name must stay non-empty free text." };
8674
+ if (config.style !== "chatcompletions" && config.style !== "responses" && config.style !== "messages" && config.style !== "gemini") return { allowed: false, reason: "The provider protocol shape must be one of the four wire shapes the user picks." };
8675
+ if (config.authref !== void 0 && config.authref.storageid.trim() === "") return { allowed: false, reason: "The provider auth reference needs the storage id of the stored key; the key material never enters the config." };
8676
+ return { allowed: true };
8677
+ }
8678
+ function provideregressgrade(input) {
8679
+ const valid = providervalid(input.provider);
8680
+ if (!valid.allowed) return valid;
8681
+ return { allowed: true, reason: input.local ? "The model call stays on the local machine endpoint and grades as the local data egress preference." : "The model call leaves the browser for the user configured endpoint and grades as a data egress event with its endpoint, model and token counts in the audit trail." };
8682
+ }
8683
+ function egressconsentgate(input) {
8684
+ if (input.pagecontent !== void 0 && input.pagecontent.trim() !== "" && input.granted !== true) return { allowed: false, reason: "The model call carries page content the user has not granted, so the content stays in the browser and the call refuses." };
8685
+ return { allowed: true };
8686
+ }
8687
+ function plandraftreviewgate(draft) {
8688
+ if (draft.state !== "approved") return { allowed: false, reason: "The model drafted plan stays unreviewed; the human review approves the draft before any step executes." };
8689
+ if (draft.steps.length === 0) return { allowed: false, reason: "The model drafted plan carries no step, so nothing executes." };
8690
+ return { allowed: true };
8691
+ }
8692
+ function replanreviewgate(replan) {
8693
+ if (replan.state !== "approved") return { allowed: false, reason: "The replanned tail stays unreviewed; the fresh review approves the changed steps before any of them executes." };
8694
+ if (replan.tail.some((step) => step.freshreview !== true)) return { allowed: false, reason: "Every revised step of a replan must carry the fresh review marker." };
8695
+ return { allowed: true };
8696
+ }
8697
+ function costbudgetvalid(budget) {
8698
+ if (budget.maxtokens !== void 0 && (!Number.isFinite(budget.maxtokens) || budget.maxtokens <= 0)) return { allowed: false, reason: "The token ceiling of a cost budget must stay a positive user value." };
8699
+ if (budget.maxcost !== void 0 && (!Number.isFinite(budget.maxcost) || budget.maxcost <= 0)) return { allowed: false, reason: "The cost ceiling of a cost budget must stay a positive user value." };
8700
+ if (budget.maxcost !== void 0 && (budget.currency === void 0 || budget.currency.trim() === "")) return { allowed: false, reason: "The cost ceiling of a cost budget needs its currency unit." };
8701
+ if (budget.maxtokens === void 0 && budget.maxcost === void 0) return { allowed: false, reason: "The cost budget needs at least one ceiling the user configured; an absent budget stays the documented unbounded choice." };
8702
+ return { allowed: true };
8703
+ }
8704
+ function draftriskof(step) {
8705
+ try {
8706
+ return resolvedrisk({ id: step.id, kind: step.kind, ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {}, summary: step.summary, risk: "sensitive" });
8707
+ } catch {
8708
+ return "sensitive";
8709
+ }
8710
+ }
8711
+ function planlint(draft, origin) {
8712
+ const findings = [];
8713
+ if (draft.goal.trim() === "") findings.push("The drafted plan carries no goal.");
8714
+ if (draft.steps.length === 0) findings.push("The drafted plan carries no step.");
8715
+ for (const step of draft.steps) {
8716
+ const mapped = { id: step.id, kind: step.kind, ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {}, summary: step.summary, risk: draftriskof(step) };
8717
+ const verdict = validatestep(mapped, origin);
8718
+ if (!verdict.allowed) findings.push(`The drafted step ${step.id || "without id"} of kind ${step.kind || "unknown"} violates the action grammar: ${verdict.reason ?? "the step failed its grammar check."}`);
8719
+ }
8720
+ return findings;
8721
+ }
8549
8722
 
8550
8723
  // progress.ts
8551
8724
  function emptyprogress(planid, now) {
@@ -8725,7 +8898,7 @@ function recordtoolcall(progress, planid, stepid, entry, now) {
8725
8898
  }
8726
8899
 
8727
8900
  // version.ts
8728
- var packageversion = "1.1.56";
8901
+ var packageversion = "1.1.57";
8729
8902
 
8730
8903
  // types.ts
8731
8904
  var protocolversion = packageversion;
@@ -10831,6 +11004,367 @@ function endcall(input) {
10831
11004
  return { contexts: input.contexts.map((candidate) => candidate.callid === input.callid ? context : candidate), context };
10832
11005
  }
10833
11006
 
11007
+ // llm.ts
11008
+ var defaultrefusalmarkers = ["i cannot", "i can't", "i'm unable", "refusal:", "cannot comply"];
11009
+ function buildrequest(input) {
11010
+ const headers = { "content-type": "application/json" };
11011
+ let url = input.provider.endpoint;
11012
+ const style = input.provider.style;
11013
+ if (style === "chatcompletions") {
11014
+ if (input.apikey !== void 0 && input.apikey.trim() !== "") headers.authorization = `Bearer ${input.apikey}`;
11015
+ const body2 = { model: input.model, messages: input.messages.map((message) => ({ role: message.role, content: message.content })), ...input.temperature !== void 0 ? { temperature: input.temperature } : {}, ...input.maxtokens !== void 0 ? { max_tokens: input.maxtokens } : {}, ...input.stream === true ? { stream: true } : {} };
11016
+ return { url, method: "POST", headers: { ...headers, ...input.provider.headers ?? {} }, body: JSON.stringify(body2) };
11017
+ }
11018
+ if (style === "responses") {
11019
+ if (input.apikey !== void 0 && input.apikey.trim() !== "") headers.authorization = `Bearer ${input.apikey}`;
11020
+ const system2 = input.messages.filter((message) => message.role === "system").map((message) => message.content).join("\n");
11021
+ const turns2 = input.messages.filter((message) => message.role !== "system").map((message) => ({ role: message.role === "assistant" ? "assistant" : "user", content: message.content }));
11022
+ const body2 = { model: input.model, input: turns2, ...system2.trim() !== "" ? { instructions: system2 } : {}, ...input.temperature !== void 0 ? { temperature: input.temperature } : {}, ...input.maxtokens !== void 0 ? { max_output_tokens: input.maxtokens } : {}, ...input.stream === true ? { stream: true } : {} };
11023
+ return { url, method: "POST", headers: { ...headers, ...input.provider.headers ?? {} }, body: JSON.stringify(body2) };
11024
+ }
11025
+ if (style === "messages") {
11026
+ if (input.apikey !== void 0 && input.apikey.trim() !== "") headers["x-api-key"] = input.apikey;
11027
+ const system2 = input.messages.filter((message) => message.role === "system").map((message) => message.content).join("\n");
11028
+ const turns2 = input.messages.filter((message) => message.role !== "system").map((message) => ({ role: message.role, content: message.content }));
11029
+ const body2 = { model: input.model, messages: turns2, ...system2.trim() !== "" ? { system: system2 } : {}, ...input.temperature !== void 0 ? { temperature: input.temperature } : {}, ...input.maxtokens !== void 0 ? { max_tokens: input.maxtokens } : {}, ...input.stream === true ? { stream: true } : {} };
11030
+ return { url, method: "POST", headers: { ...headers, ...input.provider.headers ?? {} }, body: JSON.stringify(body2) };
11031
+ }
11032
+ if (input.apikey !== void 0 && input.apikey.trim() !== "") url = `${url}${url.includes("?") ? "&" : "?"}key=${encodeURIComponent(input.apikey)}`;
11033
+ const system = input.messages.filter((message) => message.role === "system").map((message) => message.content).join("\n");
11034
+ const turns = input.messages.filter((message) => message.role !== "system").map((message) => ({ role: message.role === "assistant" ? "model" : "user", parts: [{ text: message.content }] }));
11035
+ const body = { contents: turns, ...system.trim() !== "" ? { systemInstruction: { parts: [{ text: system }] } } : {}, ...input.temperature !== void 0 ? { generationConfig: { temperature: input.temperature, ...input.maxtokens !== void 0 ? { maxOutputTokens: input.maxtokens } : {} } } : input.maxtokens !== void 0 ? { generationConfig: { maxOutputTokens: input.maxtokens } } : {} };
11036
+ return { url, method: "POST", headers: { ...headers, ...input.provider.headers ?? {} }, body: JSON.stringify(body) };
11037
+ }
11038
+ function numberof(value) {
11039
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
11040
+ }
11041
+ function parsecompletion(style, body) {
11042
+ let parsed;
11043
+ try {
11044
+ parsed = JSON.parse(body);
11045
+ } catch {
11046
+ return { reason: "The provider answer is not json." };
11047
+ }
11048
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { reason: "The provider answer is not a json object." };
11049
+ const record2 = parsed;
11050
+ if (style === "chatcompletions") {
11051
+ const choice = Array.isArray(record2.choices) ? record2.choices[0] : void 0;
11052
+ const message = choice !== void 0 && choice.message !== void 0 && typeof choice.message === "object" ? choice.message : void 0;
11053
+ if (message === void 0 || typeof message.content !== "string") return { reason: "The chat completions answer carries no message content." };
11054
+ const usage2 = record2.usage !== void 0 && typeof record2.usage === "object" ? record2.usage : void 0;
11055
+ const prompttokens2 = usage2 !== void 0 ? numberof(usage2.prompt_tokens) : void 0;
11056
+ const completiontokens2 = usage2 !== void 0 ? numberof(usage2.completion_tokens) : void 0;
11057
+ const totaltokens2 = usage2 !== void 0 ? numberof(usage2.total_tokens) : void 0;
11058
+ return { text: message.content, ...prompttokens2 !== void 0 || completiontokens2 !== void 0 || totaltokens2 !== void 0 ? { usage: { prompttokens: prompttokens2 ?? 0, completiontokens: completiontokens2 ?? 0, totaltokens: totaltokens2 ?? (prompttokens2 ?? 0) + (completiontokens2 ?? 0) } } : {} };
11059
+ }
11060
+ if (style === "responses") {
11061
+ const direct = typeof record2.output_text === "string" ? record2.output_text : void 0;
11062
+ let text2 = direct;
11063
+ if (text2 === void 0 && Array.isArray(record2.output)) {
11064
+ const parts2 = [];
11065
+ for (const item of record2.output) {
11066
+ if (item && typeof item === "object" && Array.isArray(item.content)) {
11067
+ for (const part of item.content) {
11068
+ if (part && typeof part === "object" && part.type === "output_text" && typeof part.text === "string") parts2.push(part.text);
11069
+ }
11070
+ }
11071
+ }
11072
+ if (parts2.length > 0) text2 = parts2.join("");
11073
+ }
11074
+ if (text2 === void 0) return { reason: "The responses answer carries no output text." };
11075
+ const usage2 = record2.usage !== void 0 && typeof record2.usage === "object" ? record2.usage : void 0;
11076
+ const prompttokens2 = usage2 !== void 0 ? numberof(usage2.input_tokens) : void 0;
11077
+ const completiontokens2 = usage2 !== void 0 ? numberof(usage2.output_tokens) : void 0;
11078
+ const totaltokens2 = usage2 !== void 0 ? numberof(usage2.total_tokens) : void 0;
11079
+ return { text: text2, ...prompttokens2 !== void 0 || completiontokens2 !== void 0 || totaltokens2 !== void 0 ? { usage: { prompttokens: prompttokens2 ?? 0, completiontokens: completiontokens2 ?? 0, totaltokens: totaltokens2 ?? (prompttokens2 ?? 0) + (completiontokens2 ?? 0) } } : {} };
11080
+ }
11081
+ if (style === "messages") {
11082
+ const parts2 = [];
11083
+ if (Array.isArray(record2.content)) {
11084
+ for (const part of record2.content) {
11085
+ if (part && typeof part === "object" && part.type === "text" && typeof part.text === "string") parts2.push(part.text);
11086
+ }
11087
+ }
11088
+ if (parts2.length === 0) return { reason: "The messages answer carries no text block." };
11089
+ const usage2 = record2.usage !== void 0 && typeof record2.usage === "object" ? record2.usage : void 0;
11090
+ const prompttokens2 = usage2 !== void 0 ? numberof(usage2.input_tokens) : void 0;
11091
+ const completiontokens2 = usage2 !== void 0 ? numberof(usage2.output_tokens) : void 0;
11092
+ return { text: parts2.join(""), ...prompttokens2 !== void 0 || completiontokens2 !== void 0 ? { usage: { prompttokens: prompttokens2 ?? 0, completiontokens: completiontokens2 ?? 0, totaltokens: (prompttokens2 ?? 0) + (completiontokens2 ?? 0) } } : {} };
11093
+ }
11094
+ const candidate = Array.isArray(record2.candidates) ? record2.candidates[0] : void 0;
11095
+ const content = candidate !== void 0 && candidate.content !== void 0 && typeof candidate.content === "object" ? candidate.content.parts : void 0;
11096
+ const parts = [];
11097
+ if (Array.isArray(content)) {
11098
+ for (const part of content) {
11099
+ if (part && typeof part === "object" && typeof part.text === "string") parts.push(part.text);
11100
+ }
11101
+ }
11102
+ if (parts.length === 0) return { reason: "The gemini answer carries no candidate text." };
11103
+ const usage = record2.usageMetadata !== void 0 && typeof record2.usageMetadata === "object" ? record2.usageMetadata : void 0;
11104
+ const prompttokens = usage !== void 0 ? numberof(usage.promptTokenCount) : void 0;
11105
+ const completiontokens = usage !== void 0 ? numberof(usage.candidatesTokenCount) : void 0;
11106
+ const totaltokens = usage !== void 0 ? numberof(usage.totalTokenCount) : void 0;
11107
+ return { text: parts.join(""), ...prompttokens !== void 0 || completiontokens !== void 0 || totaltokens !== void 0 ? { usage: { prompttokens: prompttokens ?? 0, completiontokens: completiontokens ?? 0, totaltokens: totaltokens ?? (prompttokens ?? 0) + (completiontokens ?? 0) } } : {} };
11108
+ }
11109
+ function islocalorigin(url) {
11110
+ try {
11111
+ const host = new URL(url).hostname.toLowerCase();
11112
+ return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]" || host.endsWith(".localhost");
11113
+ } catch {
11114
+ return false;
11115
+ }
11116
+ }
11117
+ async function callmodel(input) {
11118
+ if (input.provider.endpoint.trim() === "") throw new Error("The provider needs the user configured endpoint url before any call leaves.");
11119
+ if (input.provider.authref !== void 0 && (input.apikey === void 0 || input.apikey.trim() === "")) throw new Error(`The provider ${input.provider.name} references the stored key ${input.provider.authref.name} and the call needs the resolved key material.`);
11120
+ const consent = egressconsentgate({ ...input.pagecontent !== void 0 ? { pagecontent: input.pagecontent } : {}, granted: input.pagegrant === true });
11121
+ if (!consent.allowed) throw new Error(consent.reason ?? "The page content stayed ungranted and the call refused.");
11122
+ const shaped = buildrequest({ provider: input.provider, model: input.model, messages: input.messages, ...input.apikey !== void 0 ? { apikey: input.apikey } : {}, ...input.temperature !== void 0 ? { temperature: input.temperature } : {}, ...input.maxtokens !== void 0 ? { maxtokens: input.maxtokens } : {}, ...input.stream === true ? { stream: true } : {} });
11123
+ const transport = await sendfetch({ request: { url: shaped.url, method: shaped.method, headers: shaped.headers, body: shaped.body }, ...input.options !== void 0 ? { options: input.options } : {}, transport: input.transport, ...input.sleep !== void 0 ? { sleep: input.sleep } : {}, ...input.now !== void 0 ? { now: input.now } : {} });
11124
+ const parsed = parsecompletion(input.provider.style, transport.body);
11125
+ if (parsed.text === void 0) throw new Error(parsed.reason ?? "The provider answer did not parse.");
11126
+ return { text: parsed.text, ...parsed.usage !== void 0 ? { usage: parsed.usage } : {}, request: shaped };
11127
+ }
11128
+ async function calllocal(input) {
11129
+ if (input.local.endpoint.trim() === "") throw new Error("The local model needs the user configured endpoint url before any call runs.");
11130
+ if (!islocalorigin(input.local.endpoint)) throw new Error("The local model endpoint must stay a local machine address; the call never leaves the machine.");
11131
+ const provider = { id: "local", name: "The local model endpoint", endpoint: input.local.endpoint, style: input.local.style, models: [input.local.model], status: "available", createdat: 0 };
11132
+ return callmodel({ provider, model: input.local.model, messages: input.messages, ...input.apikey !== void 0 ? { apikey: input.apikey } : {}, ...input.temperature !== void 0 ? { temperature: input.temperature } : {}, ...input.maxtokens !== void 0 ? { maxtokens: input.maxtokens } : {}, ...input.options !== void 0 ? { options: input.options } : {}, transport: input.transport, ...input.sleep !== void 0 ? { sleep: input.sleep } : {}, ...input.now !== void 0 ? { now: input.now } : {} });
11133
+ }
11134
+ var commandguard = { schema: { intent: { type: "string", required: true }, entities: { type: "array", required: true }, confidence: { type: "number", required: true } }, retries: 1 };
11135
+ function classifyintent(text2) {
11136
+ const words = text2.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
11137
+ if (words.length === 0) return { intent: "ask", confidence: 0 };
11138
+ const scores = { navigate: 0, extract: 0, fill: 0, monitor: 0, automate: 0, ask: 0 };
11139
+ const keywords = [
11140
+ ["navigate", ["go", "open", "visit", "navigate", "browse", "url", "site", "page", "to"]],
11141
+ ["extract", ["extract", "scrape", "collect", "read", "gather", "copy", "table", "data", "text"]],
11142
+ ["fill", ["fill", "type", "enter", "form", "submit", "login", "sign", "checkout", "field"]],
11143
+ ["monitor", ["watch", "monitor", "observe", "track", "alert", "notify", "poll", "changes"]],
11144
+ ["automate", ["automate", "workflow", "repeat", "every", "schedule", "batch", "pipeline", "steps", "then"]],
11145
+ ["ask", ["what", "who", "when", "where", "why", "how", "explain", "summarize", "ask", "question", "tell"]]
11146
+ ];
11147
+ for (const [intent, list] of keywords) for (const word of list) if (words.includes(word)) scores[intent] += 1;
11148
+ let best = "ask";
11149
+ let bestscore = scores.ask;
11150
+ for (const [intent] of keywords) if (scores[intent] > bestscore) {
11151
+ best = intent;
11152
+ bestscore = scores[intent];
11153
+ }
11154
+ const total = Object.values(scores).reduce((sum, value) => sum + value, 0);
11155
+ const confidence = bestscore === 0 ? 0.1 : Math.min(1, Math.round((bestscore / total * 0.6 + Math.min(bestscore / 3, 1) * 0.4) * 100) / 100);
11156
+ return { intent: best, confidence };
11157
+ }
11158
+ async function parsecommand(input) {
11159
+ if (input.text.trim() === "") return { reason: "The command parse needs the natural language text." };
11160
+ const guard = input.guard ?? commandguard;
11161
+ const answer = await callmodel({ provider: input.provider, model: input.model, messages: [{ role: "system", content: "Parse the user command into json with the fields intent (one of navigate, extract, fill, monitor, automate, ask), entities (an array of { name, value } objects) and confidence (a number between 0 and 1). Answer with the json object only." }, { role: "user", content: input.text }], ...input.apikey !== void 0 ? { apikey: input.apikey } : {}, transport: input.transport, ...input.sleep !== void 0 ? { sleep: input.sleep } : {}, ...input.now !== void 0 ? { now: input.now } : {} });
11162
+ const output = guardoutput({ guard, attempts: [answer.text] });
11163
+ if (output.verdict !== "valid" || output.parsed === void 0) return { output, reason: output.reason ?? "The command answer failed its guard." };
11164
+ const parsed = output.parsed;
11165
+ if (typeof parsed.intent !== "string") return { output, reason: "The command answer carries no intent." };
11166
+ const intents = ["navigate", "extract", "fill", "monitor", "automate", "ask"];
11167
+ if (!intents.includes(parsed.intent)) return { output, reason: `The intent ${parsed.intent} is not one of the intent kinds.` };
11168
+ const entities = Array.isArray(parsed.entities) ? parsed.entities.filter((entity) => entity !== null && typeof entity === "object" && !Array.isArray(entity) && typeof entity.name === "string" && typeof entity.value === "string") : [];
11169
+ const confidence = typeof parsed.confidence === "number" && Number.isFinite(parsed.confidence) ? Math.min(1, Math.max(0, parsed.confidence)) : 0;
11170
+ return { parse: { text: input.text, intent: parsed.intent, entities, confidence, model: input.model, providerid: input.provider.id, parsedat: (input.now ?? Date.now)() }, output };
11171
+ }
11172
+ async function draftplan(input) {
11173
+ if (input.goal.trim() === "") return { reason: "The plan draft needs the goal." };
11174
+ const lessons = input.lessons ?? [];
11175
+ const answer = await callmodel({ provider: input.provider, model: input.model, messages: [{ role: "system", content: `Draft a browser agent plan as json with the fields goal (string), steps (an array of { kind, target, value, summary } objects using browser action kinds) and openquestions (an array of strings for what stays unclear).${lessons.length > 0 ? ` The running lessons of the earlier steps: ${lessons.join(" | ")}.` : ""} Answer with the json object only.` }, { role: "user", content: input.goal }], ...input.apikey !== void 0 ? { apikey: input.apikey } : {}, transport: input.transport, ...input.sleep !== void 0 ? { sleep: input.sleep } : {}, ...input.now !== void 0 ? { now: input.now } : {} });
11176
+ const guard = { schema: { goal: { type: "string", required: true }, steps: { type: "array", required: true }, openquestions: { type: "array" } }, retries: 1 };
11177
+ const output = guardoutput({ guard, attempts: [answer.text] });
11178
+ if (output.verdict !== "valid" || output.parsed === void 0) return { output, reason: output.reason ?? "The plan draft answer failed its guard." };
11179
+ const parsed = output.parsed;
11180
+ const rawsteps = Array.isArray(parsed.steps) ? parsed.steps : [];
11181
+ const steps = rawsteps.filter((step) => step !== null && typeof step === "object" && !Array.isArray(step)).map((step, index) => ({ id: `step${index + 1}`, kind: typeof step.kind === "string" ? step.kind : "", ...typeof step.target === "string" && step.target.trim() !== "" ? { target: step.target } : {}, ...typeof step.value === "string" && step.value.trim() !== "" ? { value: step.value } : {}, summary: typeof step.summary === "string" ? step.summary : "" }));
11182
+ const openquestions = Array.isArray(parsed.openquestions) ? parsed.openquestions.filter((question) => typeof question === "string") : [];
11183
+ const draft = { id: randomid(), goal: typeof parsed.goal === "string" && parsed.goal.trim() !== "" ? parsed.goal : input.goal, steps, openquestions, providerid: input.provider.id, model: input.model, state: "draft", lintfindings: [], createdat: (input.now ?? Date.now)() };
11184
+ draft.lintfindings = planlint(draft, input.origin ?? "");
11185
+ return { draft, output };
11186
+ }
11187
+ async function replannonfail(input) {
11188
+ if (input.reason.trim() === "") return { reason: "The replan needs the failure reason." };
11189
+ const completed = input.draft.steps.filter((step) => input.completedstepids.includes(step.id));
11190
+ const failed = input.draft.steps.filter((step) => input.failedstepids.includes(step.id));
11191
+ const lessons = input.lessons ?? [];
11192
+ const answer = await callmodel({ provider: input.provider, model: input.model, messages: [{ role: "system", content: `The plan ${input.draft.goal} failed at the steps ${failed.map((step) => step.summary).join("; ") || "unknown"} with the reason: ${input.reason}. The completed steps stay: ${completed.map((step) => step.summary).join("; ") || "none"}.${lessons.length > 0 ? ` The running lessons: ${lessons.join(" | ")}.` : ""} Draft the revised tail steps of the plan as json with the field steps (an array of { kind, target, value, summary } objects using browser action kinds). Answer with the json object only.` }, { role: "user", content: input.draft.goal }], ...input.apikey !== void 0 ? { apikey: input.apikey } : {}, transport: input.transport, ...input.sleep !== void 0 ? { sleep: input.sleep } : {}, ...input.now !== void 0 ? { now: input.now } : {} });
11193
+ const guard = { schema: { steps: { type: "array", required: true } }, retries: 1 };
11194
+ const output = guardoutput({ guard, attempts: [answer.text] });
11195
+ if (output.verdict !== "valid" || output.parsed === void 0) return { output, reason: output.reason ?? "The replan answer failed its guard." };
11196
+ const rawsteps = Array.isArray(output.parsed.steps) ? output.parsed.steps : [];
11197
+ const tail = rawsteps.filter((step) => step !== null && typeof step === "object" && !Array.isArray(step)).map((step, index) => ({ id: `tail${index + 1}`, kind: typeof step.kind === "string" ? step.kind : "", ...typeof step.target === "string" && step.target.trim() !== "" ? { target: step.target } : {}, ...typeof step.value === "string" && step.value.trim() !== "" ? { value: step.value } : {}, summary: typeof step.summary === "string" ? step.summary : "", freshreview: true }));
11198
+ const replan = { id: randomid(), draftid: input.draft.id, completedstepids: [...input.completedstepids], failedstepids: [...input.failedstepids], tail, reason: input.reason, providerid: input.provider.id, model: input.model, state: "pending", createdat: (input.now ?? Date.now)() };
11199
+ return { replan, output };
11200
+ }
11201
+ async function reflectstep(input) {
11202
+ if (input.outcome.trim() === "") return { reason: "The reflection needs the step outcome." };
11203
+ const lessons = input.lessons ?? [];
11204
+ const answer = await callmodel({ provider: input.provider, model: input.model, messages: [{ role: "system", content: `Reflect on the executed step ${input.stepid} of the run ${input.runid} with the outcome: ${input.outcome}.${lessons.length > 0 ? ` The running lessons of the earlier steps: ${lessons.join(" | ")}.` : ""} Answer as json with the fields outcome (string), lesson (string) and advice (string) for the next step. Answer with the json object only.` }, { role: "user", content: input.outcome }], ...input.apikey !== void 0 ? { apikey: input.apikey } : {}, transport: input.transport, ...input.sleep !== void 0 ? { sleep: input.sleep } : {}, ...input.now !== void 0 ? { now: input.now } : {} });
11205
+ const guard = { schema: { outcome: { type: "string", required: true }, lesson: { type: "string", required: true }, advice: { type: "string", required: true } }, retries: 1 };
11206
+ const output = guardoutput({ guard, attempts: [answer.text] });
11207
+ if (output.verdict !== "valid" || output.parsed === void 0) return { output, reason: output.reason ?? "The reflection answer failed its guard." };
11208
+ const parsed = output.parsed;
11209
+ if (typeof parsed.lesson !== "string" || typeof parsed.advice !== "string") return { output, reason: "The reflection answer carries no lesson or advice." };
11210
+ const note = { id: randomid(), runid: input.runid, stepid: input.stepid, outcome: typeof parsed.outcome === "string" ? parsed.outcome : input.outcome, lesson: parsed.lesson, advice: parsed.advice, providerid: input.provider.id, model: input.model, createdat: (input.now ?? Date.now)() };
11211
+ return { note, output };
11212
+ }
11213
+ function reflectionsummary(notes) {
11214
+ const latest = /* @__PURE__ */ new Map();
11215
+ for (const note of notes) latest.set(note.stepid, note);
11216
+ const lessons = [...latest.values()].sort((one, two) => one.createdat - two.createdat).map((note) => note.lesson);
11217
+ return lessons.length === 0 ? "" : lessons.join(" | ");
11218
+ }
11219
+ function stripguardrails(text2) {
11220
+ const fenced = text2.match(/```(?:[a-z]*)\s*\r?\n?([\s\S]*?)```/i);
11221
+ const candidate = fenced !== null ? fenced[1] ?? "" : text2;
11222
+ const start = candidate.indexOf("{");
11223
+ const end = candidate.lastIndexOf("}");
11224
+ if (start >= 0 && end > start) return candidate.slice(start, end + 1);
11225
+ const arraystart = candidate.indexOf("[");
11226
+ const arrayend = candidate.lastIndexOf("]");
11227
+ if (arraystart >= 0 && arrayend > arraystart) return candidate.slice(arraystart, arrayend + 1);
11228
+ return candidate.trim();
11229
+ }
11230
+ function parseoutput(input) {
11231
+ const raw = input.text;
11232
+ const stripped = stripguardrails(raw);
11233
+ const markers = input.guard.refusalmarkers ?? defaultrefusalmarkers;
11234
+ const lowered = stripped.toLowerCase();
11235
+ for (const marker of markers) if (marker.trim() !== "" && lowered.includes(marker.toLowerCase())) return { raw, verdict: "refused", reason: `The model answer carries the refusal marker ${marker}.`, attempts: 1 };
11236
+ let parsed;
11237
+ try {
11238
+ parsed = JSON.parse(stripped);
11239
+ } catch {
11240
+ return { raw, verdict: "invalid", reason: "The model answer is not json after the guardrail strip.", attempts: 1 };
11241
+ }
11242
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { raw, verdict: "invalid", reason: "The model answer is not a json object.", attempts: 1 };
11243
+ const record2 = parsed;
11244
+ for (const [name, field] of Object.entries(input.guard.schema)) {
11245
+ const value = record2[name];
11246
+ if (value === void 0 || value === null) {
11247
+ if (field.required === true) return { raw, verdict: "invalid", reason: `The required field ${name} of the expected schema is missing.`, attempts: 1 };
11248
+ continue;
11249
+ }
11250
+ const actual = Array.isArray(value) ? "array" : typeof value;
11251
+ if (actual !== field.type) return { raw, verdict: "invalid", reason: `The field ${name} carries a ${actual} value where the schema asks a ${field.type}.`, attempts: 1 };
11252
+ }
11253
+ return { raw, parsed: record2, verdict: "valid", attempts: 1 };
11254
+ }
11255
+ function guardoutput(input) {
11256
+ const limit = Math.max(1, Math.floor(input.guard.retries) + 1);
11257
+ const attempts = input.attempts.slice(0, limit);
11258
+ let last;
11259
+ for (let index = 0; index < attempts.length; index += 1) {
11260
+ const output = parseoutput({ guard: input.guard, text: attempts[index] ?? "" });
11261
+ last = { ...output, attempts: index + 1 };
11262
+ if (output.verdict === "valid") return last;
11263
+ if (output.verdict === "refused") return { ...output, attempts: index + 1 };
11264
+ }
11265
+ const exhausted = last === void 0 ? { raw: "", verdict: "invalid", reason: "The model answer never arrived.", attempts: 0 } : { ...last, verdict: "invalid", reason: `${last.reason ?? "The model answer failed its guard."} Every retry attempt failed, so the guard refuses the output and nothing executes.` };
11266
+ return exhausted;
11267
+ }
11268
+ function toolbriefof(tool) {
11269
+ return { tool: tool.name, summary: `${tool.name}: ${tool.description.split(".")[0] ?? tool.description}.`, description: tool.description, risk: tool.risk, parameters: Object.entries(tool.inputschema.properties).map(([name, property]) => ({ name, type: property.type, description: property.description, required: property.required === true })) };
11270
+ }
11271
+ function rendertoolbriefs(tools) {
11272
+ const blocks = tools.map((tool) => {
11273
+ const brief = toolbriefof(tool);
11274
+ const parameters = brief.parameters.map((parameter) => ` - name: ${parameter.name}
11275
+ type: ${parameter.type}
11276
+ required: ${parameter.required ? "true" : "false"}
11277
+ description: ${parameter.description}`).join("\n");
11278
+ return ` - tool: ${brief.tool}
11279
+ summary: ${brief.summary}
11280
+ risk: ${brief.risk}
11281
+ parameters:
11282
+ ${parameters}`;
11283
+ });
11284
+ return `tools:
11285
+ ${blocks.join("\n")}
11286
+ consent: every tool with side effects executes only the approved plan step it names; a proposal without the approved step stays refused.`;
11287
+ }
11288
+ function usagetotals(records, filter = {}) {
11289
+ const kept = records.filter((record2) => (filter.runid === void 0 || record2.runid === filter.runid) && (filter.stepid === void 0 || record2.stepid === filter.stepid) && (filter.since === void 0 || record2.at >= filter.since) && (filter.until === void 0 || record2.at <= filter.until));
11290
+ return kept.reduce((totals, record2) => ({ prompttokens: totals.prompttokens + record2.prompttokens, completiontokens: totals.completiontokens + record2.completiontokens, totaltokens: totals.totaltokens + record2.totaltokens, cost: totals.cost + record2.cost, calls: totals.calls + 1 }), { prompttokens: 0, completiontokens: 0, totaltokens: 0, cost: 0, calls: 0 });
11291
+ }
11292
+ function budgetcheck(input) {
11293
+ if (input.budget === void 0) return { allowed: true, halted: false, asksuser: false };
11294
+ if (input.budget.maxtokens !== void 0 && Number.isFinite(input.budget.maxtokens) && input.totals.totaltokens >= input.budget.maxtokens) return { allowed: false, halted: true, asksuser: true, reason: `The run reached the user configured token ceiling of ${input.budget.maxtokens} and halts until the user answers.` };
11295
+ if (input.budget.maxcost !== void 0 && Number.isFinite(input.budget.maxcost) && input.totals.cost >= input.budget.maxcost) return { allowed: false, halted: true, asksuser: true, reason: `The run reached the user configured cost ceiling of ${input.budget.maxcost} and halts until the user answers.` };
11296
+ return { allowed: true, halted: false, asksuser: false };
11297
+ }
11298
+
11299
+ // modelroute.ts
11300
+ function routevalid(route) {
11301
+ if (route.kind.trim() === "") return { allowed: false, reason: "The model route needs its task kind." };
11302
+ if (route.providerid.trim() === "") return { allowed: false, reason: "The model route needs the provider it routes to." };
11303
+ if (route.model.trim() === "") return { allowed: false, reason: "The model route needs the model name it routes to." };
11304
+ const hasfallbackprovider = route.fallbackproviderid !== void 0 && route.fallbackproviderid.trim() !== "";
11305
+ const hasfallbackmodel = route.fallbackmodel !== void 0 && route.fallbackmodel.trim() !== "";
11306
+ if (hasfallbackprovider !== hasfallbackmodel) return { allowed: false, reason: "The fallback of a model route needs its provider and its model together." };
11307
+ return { allowed: true };
11308
+ }
11309
+ function routesfor(routes, kind) {
11310
+ return routes.filter((route) => route.kind === kind).sort((one, two) => two.revision - one.revision);
11311
+ }
11312
+ function resolveroute(input) {
11313
+ const candidates = routesfor(input.routes, input.kind);
11314
+ if (candidates.length === 0) return { reason: `No model route configures the task kind ${input.kind}; the user picks the provider and model pair.` };
11315
+ for (const route of candidates) {
11316
+ if (!routevalid(route).allowed) continue;
11317
+ const provider = input.providers.find((candidate) => candidate.id === route.providerid);
11318
+ if (provider === void 0) return { reason: `The route of ${input.kind} names the missing provider ${route.providerid}.` };
11319
+ if (provider.status === "unavailable") return { reason: `The provider ${provider.name} of the route of ${input.kind} stays marked unavailable from its last failure.` };
11320
+ if (!provider.models.includes(route.model)) return { reason: `The route of ${input.kind} names the model ${route.model} outside the model list of ${provider.name}.` };
11321
+ return { route, provider, model: route.model };
11322
+ }
11323
+ return { reason: `Every route of the task kind ${input.kind} failed its validation.` };
11324
+ }
11325
+ function markprovider(input) {
11326
+ return input.providers.map((provider) => provider.id === input.providerid ? { ...provider, status: input.available ? "available" : "unavailable", lastcheckedat: input.now } : provider);
11327
+ }
11328
+ function fallbackroute(input) {
11329
+ const candidates = routesfor(input.routes, input.kind);
11330
+ const primary = candidates.find((route) => routevalid(route).allowed);
11331
+ if (primary === void 0) return { reason: `No valid route configures the task kind ${input.kind}, so no fallback applies.` };
11332
+ if (primary.fallbackproviderid === void 0 || primary.fallbackmodel === void 0) return { reason: `The route of ${input.kind} carries no user configured fallback pair.` };
11333
+ const provider = input.providers.find((candidate) => candidate.id === primary.fallbackproviderid);
11334
+ if (provider === void 0) return { reason: `The fallback names the missing provider ${primary.fallbackproviderid}.` };
11335
+ if (provider.status === "unavailable") return { reason: `The fallback provider ${provider.name} stays marked unavailable from its last failure.` };
11336
+ if (!provider.models.includes(primary.fallbackmodel)) return { reason: `The fallback names the model ${primary.fallbackmodel} outside the model list of ${provider.name}.` };
11337
+ return { route: primary, provider, model: primary.fallbackmodel };
11338
+ }
11339
+
11340
+ // promptlibrary.ts
11341
+ function templatevariables(body) {
11342
+ const names = [];
11343
+ for (const match of body.matchAll(/\{\{\s*([a-z0-9]+)\s*\}\}/g)) {
11344
+ const name = match[1] ?? "";
11345
+ if (name !== "" && !names.includes(name)) names.push(name);
11346
+ }
11347
+ return names;
11348
+ }
11349
+ function savetemplate(input) {
11350
+ const existing = input.templates.filter((template) => template.name === input.name);
11351
+ const version = existing.length === 0 ? 1 : Math.max(...existing.map((template) => template.version)) + 1;
11352
+ const record2 = { id: randomid(), name: input.name, body: input.body, variables: templatevariables(input.body), version, ...input.notes !== void 0 && input.notes.trim() !== "" ? { notes: input.notes } : {}, createdat: input.now };
11353
+ return [record2, ...input.templates];
11354
+ }
11355
+ function latesttemplate(templates, name) {
11356
+ const versions = templates.filter((template) => template.name === name);
11357
+ return versions.length === 0 ? void 0 : versions.reduce((newest, template) => template.version > newest.version ? template : newest);
11358
+ }
11359
+ function searchtemplates(templates, query) {
11360
+ const term = query.trim().toLowerCase();
11361
+ const matches = term === "" ? templates : templates.filter((template) => template.name.toLowerCase().includes(term) || template.body.toLowerCase().includes(term) || (template.notes ?? "").toLowerCase().includes(term) || template.variables.some((variable) => variable.toLowerCase().includes(term)));
11362
+ return [...matches].sort((one, two) => two.version - one.version || two.createdat - one.createdat);
11363
+ }
11364
+ function removetemplate(templates, name) {
11365
+ return templates.filter((template) => template.name !== name);
11366
+ }
11367
+
10834
11368
  // extension/pagesession.ts
10835
11369
  function capturepagestate(sections) {
10836
11370
  const wants = (section) => sections.includes(section);
@@ -15378,8 +15912,8 @@ async function executecdpstep(step, session, plan, tabid2, origin) {
15378
15912
  const input = breakpointinputof(options.breakpoint);
15379
15913
  if (!input) throw new Error("The breakpoint input is absent.");
15380
15914
  const settings = await memory.getsettings();
15381
- const budgetcheck = breakpointbudgetallowed(active.breakpoints.filter((spec) => spec.revertedat === void 0).length, breakpointceilingof(settings));
15382
- if (!budgetcheck.allowed) throw new Error(budgetcheck.reason ?? "The breakpoint ceiling refused the registration.");
15915
+ const budgetcheck2 = breakpointbudgetallowed(active.breakpoints.filter((spec) => spec.revertedat === void 0).length, breakpointceilingof(settings));
15916
+ if (!budgetcheck2.allowed) throw new Error(budgetcheck2.reason ?? "The breakpoint ceiling refused the registration.");
15383
15917
  const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The breakpoint registration returned no result." };
15384
15918
  if (!output.ok) return output;
15385
15919
  const registered = output.details?.breakpoint;
@@ -16021,8 +16555,8 @@ async function executenetcontrolstep(step, session, plan, tabid2, origin) {
16021
16555
  const limits = await memory.getratelimits(Date.now());
16022
16556
  const limit = limits.find((item) => item.origin === new URL(url).origin);
16023
16557
  const wait = ratelimitwait(limit, Date.now());
16024
- const budgetcheck = ratelimitbudgetallowed(wait, budget);
16025
- if (!budgetcheck.allowed) throw new Error(budgetcheck.reason ?? "The rate limit wait exceeds the reviewed budget.");
16558
+ const budgetcheck2 = ratelimitbudgetallowed(wait, budget);
16559
+ if (!budgetcheck2.allowed) throw new Error(budgetcheck2.reason ?? "The rate limit wait exceeds the reviewed budget.");
16026
16560
  if (wait > 0) {
16027
16561
  await audit("control", `The rate limiter waits ${wait} milliseconds until the reset window of ${new URL(url).origin} passes before the submission.`, extra);
16028
16562
  await new Promise((resolve) => setTimeout(resolve, wait));
@@ -17276,7 +17810,7 @@ async function handlerequest(message, sender) {
17276
17810
  const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
17277
17811
  const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
17278
17812
  const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
17279
- return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, trigger: triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() }), triggerretention: runsettings?.triggerretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, runhistoryretention: runsettings?.runhistoryretention, editor: editorstate({ versions: await memory.listworkflowversions(), diffs: await memory.listversiondiffs(), history: await memory.gethistory(), overrides: await memory.listsiteoverrides(), imports: (await memory.listworkflowimports()).map((entry) => ({ id: entry.id, workflowid: entry.record.id, name: entry.record.name, version: entry.record.version, steps: entry.record.steps.length, risk: entry.record.risk, importedat: entry.importedat, ...entry.filename !== void 0 ? { filename: entry.filename } : {} })), backgroundruns: await memory.getbackgroundruns(), watchdog: { ...runsettings?.watchdog !== void 0 ? { config: runsettings.watchdog } : {}, events: await memory.listwatchdogevents() } }), ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {}, mcp: await mcpstateof() };
17813
+ return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, trigger: triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() }), triggerretention: runsettings?.triggerretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, runhistoryretention: runsettings?.runhistoryretention, editor: editorstate({ versions: await memory.listworkflowversions(), diffs: await memory.listversiondiffs(), history: await memory.gethistory(), overrides: await memory.listsiteoverrides(), imports: (await memory.listworkflowimports()).map((entry) => ({ id: entry.id, workflowid: entry.record.id, name: entry.record.name, version: entry.record.version, steps: entry.record.steps.length, risk: entry.record.risk, importedat: entry.importedat, ...entry.filename !== void 0 ? { filename: entry.filename } : {} })), backgroundruns: await memory.getbackgroundruns(), watchdog: { ...runsettings?.watchdog !== void 0 ? { config: runsettings.watchdog } : {}, events: await memory.listwatchdogevents() } }), ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {}, mcp: await mcpstateof(), llm: await llmstateof() };
17280
17814
  }
17281
17815
  case "capabilities":
17282
17816
  return refreshcapabilities();
@@ -19420,6 +19954,298 @@ async function handlerequest(message, sender) {
19420
19954
  await audit("workflow", `The user removed the per site override ${removed.pattern} of the workflow ${removed.workflowid}.`, {});
19421
19955
  return { removed: removed.id };
19422
19956
  }
19957
+ case "llmstate": {
19958
+ return llmstateof();
19959
+ }
19960
+ case "llmproviders": {
19961
+ const inputprovider = message;
19962
+ const id = inputprovider.id?.trim() ?? "";
19963
+ const providers = await memory.getproviders();
19964
+ if (inputprovider.remove === true) {
19965
+ if (id === "") throw new Error("The provider removal needs the provider id.");
19966
+ await memory.setproviders(providers.filter((candidate) => candidate.id !== id));
19967
+ await audit("model", `The user removed the provider config ${id}; the stored key reference and its secret stay untouched in the credential store.`, {});
19968
+ return llmstateof();
19969
+ }
19970
+ const authrefname = inputprovider.authrefname?.trim() ?? "";
19971
+ const ref = authrefname !== "" ? (await memory.getapikeys()).find((entry) => entry.name === authrefname) : void 0;
19972
+ if (authrefname !== "" && ref === void 0) throw new Error(`No stored api key reference matches ${authrefname}; set the key from the review panel first.`);
19973
+ const style = inputprovider.style === "chatcompletions" || inputprovider.style === "responses" || inputprovider.style === "messages" || inputprovider.style === "gemini" ? inputprovider.style : void 0;
19974
+ const models = Array.isArray(inputprovider.models) ? inputprovider.models.map((model) => String(model).trim()).filter((model) => model !== "") : [];
19975
+ const storedprovider = providers.find((candidate) => candidate.id === id);
19976
+ const config = {
19977
+ id: id !== "" ? id : randomid(),
19978
+ name: inputprovider.name?.trim() ?? "",
19979
+ endpoint: inputprovider.endpoint?.trim() ?? "",
19980
+ ...style !== void 0 ? { style } : { style: "chatcompletions" },
19981
+ models,
19982
+ ...inputprovider.headers !== void 0 ? { headers: inputprovider.headers } : {},
19983
+ ...ref !== void 0 ? { authref: { name: ref.name, origins: ref.origins, header: ref.header, storageid: ref.storageid, configuredat: ref.createdat } } : {},
19984
+ ...inputprovider.costpermilliontokens !== void 0 && Number.isFinite(inputprovider.costpermilliontokens) ? { costpermilliontokens: inputprovider.costpermilliontokens } : {},
19985
+ ...inputprovider.currency !== void 0 && inputprovider.currency.trim() !== "" ? { currency: inputprovider.currency.trim() } : {},
19986
+ status: storedprovider?.status ?? "available",
19987
+ ...storedprovider?.lastcheckedat !== void 0 ? { lastcheckedat: storedprovider.lastcheckedat } : {},
19988
+ createdat: storedprovider?.createdat ?? Date.now()
19989
+ };
19990
+ const gate = providervalid(config);
19991
+ if (!gate.allowed) throw new Error(gate.reason ?? "The provider config failed its validation.");
19992
+ await memory.setproviders([config, ...providers.filter((candidate) => candidate.id !== config.id)]);
19993
+ await audit("model", `The user saved the provider config ${config.name} for the ${config.endpoint} endpoint with the ${config.style} protocol shape, ${config.models.length} model${config.models.length === 1 ? "" : "s"}${config.authref !== void 0 ? ` and the stored key reference ${config.authref.name} (the key material never enters the config)` : " and no key reference"}; every value stays the user choice.`, {});
19994
+ return llmstateof();
19995
+ }
19996
+ case "llmtestprovider": {
19997
+ const inputtest = message;
19998
+ const id = inputtest.id?.trim() ?? "";
19999
+ const provider = (await memory.getproviders()).find((candidate) => candidate.id === id);
20000
+ if (!provider) throw new Error(`No provider config matches ${id}.`);
20001
+ const now = Date.now();
20002
+ const providers = await memory.getproviders();
20003
+ try {
20004
+ const key = await providerkey(provider);
20005
+ const outcome = await callmodel({ provider, model: provider.models[0] ?? "", messages: [{ role: "user", content: "Answer with the single word ready." }], ...key !== void 0 ? { apikey: key } : {}, transport: llmtransport });
20006
+ await memory.setproviders(markprovider({ providers, providerid: provider.id, available: true, now }));
20007
+ await audit("model", `The test call of the provider ${provider.name} reached the ${provider.endpoint} endpoint with the ${provider.models[0] ?? ""} model and answered ${outcome.text.length} characters; the provider stays available.`, {});
20008
+ } catch (error) {
20009
+ await memory.setproviders(markprovider({ providers, providerid: provider.id, available: false, now }));
20010
+ await audit("model", `The test call of the provider ${provider.name} failed: ${error instanceof Error ? error.message : String(error)} The provider stays marked unavailable until its next success.`, {});
20011
+ throw error;
20012
+ }
20013
+ return llmstateof();
20014
+ }
20015
+ case "llmlocal": {
20016
+ const inputlocal = message;
20017
+ const stored = await memory.getlocalmodel();
20018
+ if (inputlocal.check === true) {
20019
+ const local = stored ?? { endpoint: "", model: "", style: "chatcompletions" };
20020
+ if (local.endpoint.trim() === "") throw new Error("The local model needs the user configured endpoint url before the health check runs.");
20021
+ const now = Date.now();
20022
+ try {
20023
+ const key = local.authref !== void 0 ? await memory.getsecret(local.authref.storageid) : void 0;
20024
+ const outcome = await calllocal({ local, messages: [{ role: "user", content: "Answer with the single word ready." }], ...key !== void 0 ? { apikey: key } : {}, transport: llmtransport });
20025
+ await memory.setlocalmodel({ ...local, health: { checkedat: now, ok: true, ...outcome.text !== "" ? { detail: `The endpoint answered ${outcome.text.length} characters.` } : {} } });
20026
+ await memory.addusagerecord({ id: randomid(), providerid: "local", endpoint: local.endpoint, model: local.model, prompttokens: outcome.usage?.prompttokens ?? 0, completiontokens: outcome.usage?.completiontokens ?? 0, totaltokens: outcome.usage?.totaltokens ?? 0, cost: 0, local: true, at: now });
20027
+ await audit("model", `The local model health check reached the ${local.endpoint} endpoint with the ${local.model} model and the endpoint stays healthy; the call never left the machine.`, {});
20028
+ } catch (error) {
20029
+ await memory.setlocalmodel({ ...local, health: { checkedat: now, ok: false, detail: error instanceof Error ? error.message : String(error) } });
20030
+ await audit("model", `The local model health check failed on the ${local.endpoint} endpoint: ${error instanceof Error ? error.message : String(error)}`, {});
20031
+ }
20032
+ return llmstateof();
20033
+ }
20034
+ const authrefname = inputlocal.authrefname?.trim() ?? "";
20035
+ const ref = authrefname !== "" ? (await memory.getapikeys()).find((entry) => entry.name === authrefname) : void 0;
20036
+ if (authrefname !== "" && ref === void 0) throw new Error(`No stored api key reference matches ${authrefname}; set the key from the review panel first.`);
20037
+ const style = inputlocal.style === "chatcompletions" || inputlocal.style === "responses" || inputlocal.style === "messages" || inputlocal.style === "gemini" ? inputlocal.style : stored?.style ?? "chatcompletions";
20038
+ const config = { endpoint: inputlocal.endpoint?.trim() ?? stored?.endpoint ?? "", model: inputlocal.model?.trim() ?? stored?.model ?? "", style, ...ref !== void 0 ? { authref: { name: ref.name, origins: ref.origins, header: ref.header, storageid: ref.storageid, configuredat: ref.createdat } } : {}, ...stored?.health !== void 0 ? { health: stored.health } : {} };
20039
+ if (config.endpoint.trim() === "") throw new Error("The local model needs the user configured endpoint url.");
20040
+ if (config.model.trim() === "") throw new Error("The local model needs the user configured model name.");
20041
+ if (!islocalorigin(config.endpoint)) throw new Error("The local model endpoint must stay a local machine address so no call leaves the machine.");
20042
+ await memory.setlocalmodel(config);
20043
+ await audit("model", `The user saved the local model endpoint ${config.endpoint} with the ${config.model} model and the ${config.style} protocol shape${config.authref !== void 0 ? ` and the stored key reference ${config.authref.name}` : " and no key reference"}.`, {});
20044
+ return llmstateof();
20045
+ }
20046
+ case "llmroutes": {
20047
+ const inputroute = message;
20048
+ const kind = inputroute.taskkind?.trim() ?? "";
20049
+ if (kind === "") throw new Error("The model route needs its task kind.");
20050
+ const routes = await memory.getmodelroutes();
20051
+ if (inputroute.remove === true) {
20052
+ await memory.setmodelroutes(routes.filter((candidate) => !(candidate.kind === kind)));
20053
+ await audit("model", `The user removed every route of the task kind ${kind}; the routing table holds no default route.`, {});
20054
+ return llmstateof();
20055
+ }
20056
+ const existing = routes.find((candidate) => candidate.kind === kind);
20057
+ const now = Date.now();
20058
+ const route = { id: existing?.id ?? randomid(), kind, providerid: inputroute.providerid?.trim() ?? "", model: inputroute.model?.trim() ?? "", ...inputroute.fallbackproviderid !== void 0 && inputroute.fallbackproviderid.trim() !== "" ? { fallbackproviderid: inputroute.fallbackproviderid.trim() } : {}, ...inputroute.fallbackmodel !== void 0 && inputroute.fallbackmodel.trim() !== "" ? { fallbackmodel: inputroute.fallbackmodel.trim() } : {}, revision: (existing?.revision ?? 0) + 1, updatedat: now };
20059
+ const gate = routevalid(route);
20060
+ if (!gate.allowed) throw new Error(gate.reason ?? "The model route failed its validation.");
20061
+ await memory.setmodelroutes([route, ...routes.filter((candidate) => candidate.kind !== kind)]);
20062
+ await memory.addmodelrouterevision(route);
20063
+ await audit("model", `The user routed the task kind ${kind} to the ${route.providerid} provider with the ${route.model} model${route.fallbackproviderid !== void 0 ? ` and the ${route.fallbackproviderid} fallback with the ${route.fallbackmodel} model` : " and no fallback"} at revision ${route.revision}.`, {});
20064
+ return llmstateof();
20065
+ }
20066
+ case "llmcommand": {
20067
+ const inputcommand = message;
20068
+ const text2 = inputcommand.text?.trim() ?? "";
20069
+ if (text2 === "") throw new Error("The command parse needs the natural language text.");
20070
+ const providers = await memory.getproviders();
20071
+ const routes = await memory.getmodelroutes();
20072
+ const resolved = resolveroute({ routes, providers, kind: "parsecommand" });
20073
+ if (resolved.provider === void 0 || resolved.model === void 0) {
20074
+ const local = await memory.getlocalmodel();
20075
+ if (local === void 0 || local.endpoint.trim() === "") {
20076
+ const fallback = classifyintent(text2);
20077
+ const parse = { text: text2, intent: fallback.intent, entities: [], confidence: fallback.confidence, parsedat: Date.now() };
20078
+ await memory.setcommandparse(parse);
20079
+ await audit("model", `The deterministic classifier mapped the command to the ${fallback.intent} intent at confidence ${fallback.confidence} because no model route and no local endpoint serve the parsecommand task kind.`, {});
20080
+ return llmstateof();
20081
+ }
20082
+ const key2 = local.authref !== void 0 ? await memory.getsecret(local.authref.storageid) : void 0;
20083
+ const now = Date.now();
20084
+ const outcome = await calllocal({ local, messages: [{ role: "system", content: "Parse the user command into json with the fields intent (one of navigate, extract, fill, monitor, automate, ask), entities (an array of { name, value } objects) and confidence (a number between 0 and 1). Answer with the json object only." }, { role: "user", content: text2 }], ...key2 !== void 0 ? { apikey: key2 } : {}, transport: llmtransport });
20085
+ const replay2 = await parsecommand({ provider: { id: "local", name: "The local model endpoint", endpoint: local.endpoint, style: local.style, models: [local.model], status: "available", createdat: now }, model: local.model, text: text2, transport: cannedtransport(outcome.text) });
20086
+ if (replay2.parse === void 0) {
20087
+ if (replay2.output !== void 0) await recordguardnotice("parsecommand", replay2.output);
20088
+ throw new Error(replay2.reason ?? "The command parse failed its guard.");
20089
+ }
20090
+ await memory.setcommandparse(replay2.parse);
20091
+ await memory.addusagerecord({ id: randomid(), providerid: "local", endpoint: local.endpoint, model: local.model, prompttokens: outcome.usage?.prompttokens ?? 0, completiontokens: outcome.usage?.completiontokens ?? 0, totaltokens: outcome.usage?.totaltokens ?? 0, cost: 0, local: true, at: now });
20092
+ await audit("model", `The local model parsed the command into the ${replay2.parse.intent} intent at confidence ${replay2.parse.confidence} with ${replay2.parse.entities.length} entit${replay2.parse.entities.length === 1 ? "y" : "ies"}; the call never left the machine.`, {});
20093
+ return llmstateof();
20094
+ }
20095
+ const raw = await callroutedmodel({ kind: "parsecommand", messages: [{ role: "system", content: "Parse the user command into json with the fields intent (one of navigate, extract, fill, monitor, automate, ask), entities (an array of { name, value } objects) and confidence (a number between 0 and 1). Answer with the json object only." }, { role: "user", content: text2 }] });
20096
+ const key = await providerkey(raw.provider);
20097
+ const replay = await parsecommand({ provider: raw.provider, model: raw.model, text: text2, ...key !== void 0 ? { apikey: key } : {}, transport: cannedtransport(raw.text) });
20098
+ if (replay.parse === void 0) {
20099
+ if (replay.output !== void 0) await recordguardnotice("parsecommand", replay.output);
20100
+ throw new Error(replay.reason ?? "The command parse failed its guard.");
20101
+ }
20102
+ await memory.setcommandparse(replay.parse);
20103
+ await audit("model", `The routed model parsed the command into the ${replay.parse.intent} intent at confidence ${replay.parse.confidence} with ${replay.parse.entities.length} entit${replay.parse.entities.length === 1 ? "y" : "ies"}.`, {});
20104
+ return llmstateof();
20105
+ }
20106
+ case "llmdraftplan": {
20107
+ const inputdraft = message;
20108
+ const goal = inputdraft.goal?.trim() ?? "";
20109
+ if (goal === "") throw new Error("The plan draft needs the goal.");
20110
+ const session = await memory.getsession();
20111
+ const origin = session?.origin ?? "";
20112
+ const raw = await callroutedmodel({ kind: "draftplan", messages: [{ role: "system", content: `Draft a browser agent plan as json with the fields goal (string), steps (an array of { kind, target, value, summary } objects using browser action kinds) and openquestions (an array of strings for what stays unclear). Answer with the json object only.` }, { role: "user", content: goal }] });
20113
+ const key = await providerkey(raw.provider);
20114
+ const replay = await draftplan({ provider: raw.provider, model: raw.model, goal, ...origin !== "" ? { origin } : {}, ...key !== void 0 ? { apikey: key } : {}, transport: cannedtransport(raw.text) });
20115
+ if (replay.draft === void 0) {
20116
+ if (replay.output !== void 0) await recordguardnotice("draftplan", replay.output);
20117
+ throw new Error(replay.reason ?? "The plan draft failed its guard.");
20118
+ }
20119
+ await memory.addplandraft(replay.draft);
20120
+ await audit("model", `The ${raw.provider.name} model drafted a ${replay.draft.steps.length} step plan for the goal ${goal}${replay.draft.lintfindings.length > 0 ? ` with ${replay.draft.lintfindings.length} grammar finding${replay.draft.lintfindings.length === 1 ? "" : "s"} the review must resolve` : " with a clean grammar check"}${replay.draft.openquestions.length > 0 ? ` and ${replay.draft.openquestions.length} open question${replay.draft.openquestions.length === 1 ? "" : "s"}` : ""}; the draft never executes until the human review approves it.`, {});
20121
+ return llmstateof();
20122
+ }
20123
+ case "llmdraftdecision": {
20124
+ const inputdecision = message;
20125
+ const drafts = await memory.getplandrafts();
20126
+ const draft = drafts.find((candidate) => candidate.id === (inputdecision.draftid ?? ""));
20127
+ if (!draft) throw new Error("No model drafted plan matches the decision.");
20128
+ if (inputdecision.approve !== true) {
20129
+ await memory.setplandrafts(drafts.map((candidate) => candidate.id === draft.id ? { ...candidate, state: "rejected" } : candidate));
20130
+ await audit("model", `The user rejected the model drafted plan ${draft.id} of the goal ${draft.goal}; the draft stays for the audit trail.`, {});
20131
+ return llmstateof();
20132
+ }
20133
+ const gate = plandraftreviewgate(draft);
20134
+ if (!gate.allowed) throw new Error(gate.reason ?? "The model drafted plan failed its review gate.");
20135
+ if (draft.lintfindings.length > 0) throw new Error(`The model drafted plan carries grammar violations the review must resolve first: ${draft.lintfindings.join(" ")}`);
20136
+ const steps = Array.isArray(inputdecision.steps) && inputdecision.steps.length > 0 ? inputdecision.steps.filter((step) => typeof step?.kind === "string" && step.kind.trim() !== "" && typeof step?.summary === "string" && step.summary.trim() !== "").map((step, index) => ({ id: typeof step.id === "string" && step.id.trim() !== "" ? step.id.trim() : `step${index + 1}`, kind: step.kind, ...typeof step.target === "string" && step.target.trim() !== "" ? { target: step.target } : {}, ...typeof step.value === "string" && step.value.trim() !== "" ? { value: step.value } : {}, summary: step.summary })) : draft.steps.map((step) => ({ id: step.id, kind: step.kind, ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {}, summary: step.summary }));
20137
+ if (steps.length === 0) throw new Error("The model drafted plan carries no step to approve.");
20138
+ for (const step of steps) {
20139
+ try {
20140
+ actionrisk(step.kind);
20141
+ } catch {
20142
+ throw new Error(`The drafted step ${step.id} carries the unsupported kind ${step.kind}.`);
20143
+ }
20144
+ }
20145
+ const session = await memory.getsession();
20146
+ const plan = draftplanof(draft, steps, session?.origin ?? "");
20147
+ await memory.setplan(plan);
20148
+ await memory.setprogress(resetforplan(await memory.getprogress(), plan, Date.now()));
20149
+ await memory.setplandrafts(drafts.map((candidate) => candidate.id === draft.id ? { ...candidate, state: "approved" } : candidate));
20150
+ await audit("model", `The user approved the model drafted plan ${draft.id} of the goal ${draft.goal}; the ${plan.steps.length} drafted steps became a pending plan that still passes the same plan review every local plan passes before anything executes.`, { planid: plan.id, ...session ? { sessionid: session.id } : {} });
20151
+ return llmstateof();
20152
+ }
20153
+ case "llmreplan": {
20154
+ const inputreplan = message;
20155
+ const drafts = await memory.getplandrafts();
20156
+ const draft = inputreplan.draftid !== void 0 && inputreplan.draftid.trim() !== "" ? drafts.find((candidate) => candidate.id === inputreplan.draftid) : drafts.find((candidate) => candidate.state === "approved");
20157
+ if (!draft) throw new Error("No approved model drafted plan exists to replan.");
20158
+ const failedstepids = Array.isArray(inputreplan.failedstepids) ? inputreplan.failedstepids.filter((id) => typeof id === "string" && id.trim() !== "") : [];
20159
+ const reason = inputreplan.reason?.trim() ?? "";
20160
+ if (reason === "") throw new Error("The replan needs the failure reason.");
20161
+ const notes = await memory.getreflectnotes();
20162
+ const raw = await callroutedmodel({ kind: "replan", messages: [{ role: "system", content: `The plan ${draft.goal} failed with the reason: ${reason}. Draft the revised tail steps of the plan as json with the field steps (an array of { kind, target, value, summary } objects using browser action kinds). Answer with the json object only.` }, { role: "user", content: draft.goal }] });
20163
+ const completedstepids = draft.steps.filter((step) => !failedstepids.includes(step.id)).map((step) => step.id);
20164
+ const key = await providerkey(raw.provider);
20165
+ const replay = await replannonfail({ provider: raw.provider, model: raw.model, draft, completedstepids, failedstepids, reason, ...reflectionsummary(notes) !== "" ? { lessons: [reflectionsummary(notes)] } : {}, ...key !== void 0 ? { apikey: key } : {}, transport: cannedtransport(raw.text) });
20166
+ if (replay.replan === void 0) {
20167
+ if (replay.output !== void 0) await recordguardnotice("replan", replay.output);
20168
+ throw new Error(replay.reason ?? "The replan failed its guard.");
20169
+ }
20170
+ await memory.addreplan(replay.replan);
20171
+ await audit("model", `The ${raw.provider.name} model replanned the failed plan ${draft.id}: ${completedstepids.length} completed step${completedstepids.length === 1 ? "" : "s"} stay, the ${failedstepids.length} failed step${failedstepids.length === 1 ? "" : "s"} fall away and the ${replay.replan.tail.length} revised step${replay.replan.tail.length === 1 ? "" : "s"} carry the fresh review marker.`, {});
20172
+ return llmstateof();
20173
+ }
20174
+ case "llmreplandecision": {
20175
+ const inputdecision = message;
20176
+ const replans = await memory.getreplans();
20177
+ const replan = replans.find((candidate) => candidate.id === (inputdecision.replanid ?? ""));
20178
+ if (!replan) throw new Error("No replan record matches the decision.");
20179
+ if (inputdecision.approve !== true) {
20180
+ await memory.setreplans(replans.map((candidate) => candidate.id === replan.id ? { ...candidate, state: "rejected" } : candidate));
20181
+ await audit("model", `The user rejected the replan ${replan.id}; the record stays for the audit trail.`, {});
20182
+ return llmstateof();
20183
+ }
20184
+ const gate = replanreviewgate(replan);
20185
+ if (!gate.allowed) throw new Error(gate.reason ?? "The replan failed its fresh review gate.");
20186
+ const draft = (await memory.getplandrafts()).find((candidate) => candidate.id === replan.draftid);
20187
+ if (!draft) throw new Error("The replan names no stored draft.");
20188
+ const completed = draft.steps.filter((step) => replan.completedstepids.includes(step.id));
20189
+ const steps = [...completed.map((step) => ({ id: step.id, kind: step.kind, ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {}, summary: step.summary })), ...replan.tail.map((step) => ({ id: step.id, kind: step.kind, ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {}, summary: step.summary }))];
20190
+ const session = await memory.getsession();
20191
+ const plan = draftplanof(draft, steps, session?.origin ?? "");
20192
+ await memory.setplan(plan);
20193
+ await memory.setprogress(resetforplan(await memory.getprogress(), plan, Date.now()));
20194
+ await memory.setreplans(replans.map((candidate) => candidate.id === replan.id ? { ...candidate, state: "approved" } : candidate));
20195
+ await audit("model", `The user approved the fresh review of the replan ${replan.id}: ${completed.length} completed step${completed.length === 1 ? "" : "s"} stay and the ${replan.tail.length} revised step${replan.tail.length === 1 ? "" : "s"} became the changed tail of a pending plan that still passes the same plan review.`, { planid: plan.id, ...session ? { sessionid: session.id } : {} });
20196
+ return llmstateof();
20197
+ }
20198
+ case "llmreflect": {
20199
+ const inputreflect = message;
20200
+ const outcomes = await memory.getoutcomes();
20201
+ const outcome = inputreflect.stepid !== void 0 && inputreflect.stepid.trim() !== "" ? outcomes.find((entry) => entry.stepid === inputreflect.stepid) : outcomes[0];
20202
+ if (!outcome) throw new Error("No executed step outcome exists to reflect on.");
20203
+ const notes = await memory.getreflectnotes();
20204
+ const summary = reflectionsummary(notes.filter((note) => note.stepid !== outcome.stepid));
20205
+ const raw = await callroutedmodel({ kind: "reflect", stepid: outcome.stepid, messages: [{ role: "system", content: `Reflect on the executed step ${outcome.stepid} with the outcome: ${outcome.summary}. Answer as json with the fields outcome (string), lesson (string) and advice (string) for the next step. Answer with the json object only.` }, { role: "user", content: outcome.summary }] });
20206
+ const key = await providerkey(raw.provider);
20207
+ const replay = await reflectstep({ provider: raw.provider, model: raw.model, runid: inputreflect.runid?.trim() ?? "adhoc", stepid: outcome.stepid, outcome: outcome.summary, ...summary !== "" ? { lessons: [summary] } : {}, ...key !== void 0 ? { apikey: key } : {}, transport: cannedtransport(raw.text) });
20208
+ if (replay.note === void 0) {
20209
+ if (replay.output !== void 0) await recordguardnotice("reflect", replay.output);
20210
+ throw new Error(replay.reason ?? "The reflection failed its guard.");
20211
+ }
20212
+ await memory.addreflectnote(replay.note);
20213
+ await audit("model", `The ${raw.provider.name} model reflected on the step ${outcome.stepid}: the lesson learned rides the next prompt together with the running lessons of the earlier steps.`, {});
20214
+ return llmstateof();
20215
+ }
20216
+ case "llmbudget": {
20217
+ const inputbudget = message;
20218
+ if (inputbudget.remove === true) {
20219
+ await memory.setcostbudget({ maxtokens: Number.MAX_SAFE_INTEGER, configuredat: Date.now() });
20220
+ await audit("model", "The user removed the cost budget; the runs stay unbounded because no ceiling applies.", {});
20221
+ return llmstateof();
20222
+ }
20223
+ const budget = { ...inputbudget.runid !== void 0 && inputbudget.runid.trim() !== "" ? { runid: inputbudget.runid.trim() } : {}, ...inputbudget.maxtokens !== void 0 && Number.isFinite(inputbudget.maxtokens) ? { maxtokens: inputbudget.maxtokens } : {}, ...inputbudget.maxcost !== void 0 && Number.isFinite(inputbudget.maxcost) ? { maxcost: inputbudget.maxcost } : {}, ...inputbudget.currency !== void 0 && inputbudget.currency.trim() !== "" ? { currency: inputbudget.currency.trim() } : {}, configuredat: Date.now() };
20224
+ const gate = costbudgetvalid(budget);
20225
+ if (!gate.allowed) throw new Error(gate.reason ?? "The cost budget failed its validation.");
20226
+ await memory.setcostbudget(budget);
20227
+ await audit("model", `The user configured the cost budget${budget.runid !== void 0 ? ` of the run ${budget.runid}` : ""}${budget.maxtokens !== void 0 ? ` with a ${budget.maxtokens} token ceiling` : ""}${budget.maxcost !== void 0 ? `${budget.maxtokens !== void 0 ? " and" : " with"} a ${budget.maxcost} ${budget.currency ?? ""} cost ceiling` : ""}; a reached ceiling halts the run and asks the user.`, {});
20228
+ return llmstateof();
20229
+ }
20230
+ case "llmtemplate": {
20231
+ const inputtemplate = message;
20232
+ if (inputtemplate.remove === true) {
20233
+ const name2 = inputtemplate.name?.trim() ?? "";
20234
+ if (name2 === "") throw new Error("The template removal needs the template name.");
20235
+ await memory.setprompttemplates(removetemplate(await memory.getprompttemplates(), name2));
20236
+ await audit("model", `The user removed every version of the prompt template ${name2}; the change history leaves with the name.`, {});
20237
+ return llmstateof();
20238
+ }
20239
+ const name = inputtemplate.name?.trim() ?? "";
20240
+ const body = inputtemplate.body ?? "";
20241
+ if (name === "") throw new Error("The prompt template needs its name.");
20242
+ if (body.trim() === "") throw new Error("The prompt template needs its body.");
20243
+ const stored = savetemplate({ templates: await memory.getprompttemplates(), name, body, ...inputtemplate.notes !== void 0 && inputtemplate.notes.trim() !== "" ? { notes: inputtemplate.notes } : {}, now: Date.now() });
20244
+ await memory.setprompttemplates(stored);
20245
+ const latest = latesttemplate(stored, name);
20246
+ await audit("model", `The user saved the prompt template ${name} at version ${latest?.version ?? 1} with ${latest?.variables.length ?? 0} variable${(latest?.variables.length ?? 0) === 1 ? "" : "s"}${inputtemplate.notes !== void 0 ? ` and the change notes` : ""}; every earlier version stays stored.`, {});
20247
+ return llmstateof();
20248
+ }
19423
20249
  default:
19424
20250
  throw new Error("Unknown Devthink request.");
19425
20251
  }
@@ -19526,6 +20352,82 @@ async function restorebackgroundruns() {
19526
20352
  async function mcpconfigof() {
19527
20353
  return await memory.getmcpconfig() ?? defaultmcpconfig();
19528
20354
  }
20355
+ var llmtaskkinds = ["parsecommand", "classifyintent", "draftplan", "replan", "reflect", "summarize"];
20356
+ var llmtransport = async (url, init) => {
20357
+ const response = await fetch(url, { method: init.method, headers: init.headers, ...init.body !== void 0 ? { body: init.body } : {}, ...init.mode !== void 0 ? { mode: init.mode } : {}, redirect: init.redirect });
20358
+ return { status: response.status, headers: Object.fromEntries(response.headers.entries()), body: await response.text(), ...response.redirected ? { redirected: true } : {} };
20359
+ };
20360
+ function cannedtransport(text2) {
20361
+ return async () => ({ status: 200, headers: { "content-type": "application/json" }, body: JSON.stringify({ choices: [{ message: { role: "assistant", content: text2 } }] }) });
20362
+ }
20363
+ async function providerkey(provider) {
20364
+ if (provider.authref === void 0) return void 0;
20365
+ const secret = await memory.getsecret(provider.authref.storageid);
20366
+ if (secret === void 0) throw new Error(`The provider ${provider.name} references the stored key ${provider.authref.name} and its secret is missing; set it from the review panel first.`);
20367
+ return secret;
20368
+ }
20369
+ async function callroutedmodel(input) {
20370
+ const providers = await memory.getproviders();
20371
+ const routes = await memory.getmodelroutes();
20372
+ const primary = resolveroute({ routes, providers, kind: input.kind });
20373
+ const pair = primary.provider !== void 0 && primary.model !== void 0 ? primary : fallbackroute({ routes, providers, kind: input.kind });
20374
+ if (pair.provider === void 0 || pair.model === void 0) throw new Error(pair.reason ?? `No model route configures the task kind ${input.kind}; the user picks the provider and model pair.`);
20375
+ const budget = await memory.getcostbudget();
20376
+ if (budget !== void 0) {
20377
+ const verdict = budgetcheck({ budget, totals: usagetotals(await memory.getusagerecords()) });
20378
+ if (!verdict.allowed) {
20379
+ await audit("model", `The cost budget halted the ${input.kind} model call before it left: ${verdict.reason ?? "the ceiling is reached"} The user answers before anything else runs.`, {});
20380
+ throw new Error(verdict.reason ?? "The cost budget halted the run.");
20381
+ }
20382
+ }
20383
+ const attempt = async (provider, model) => {
20384
+ const egress = provideregressgrade({ provider, local: islocalorigin(provider.endpoint) });
20385
+ if (!egress.allowed) throw new Error(egress.reason ?? "The provider config failed its gate.");
20386
+ const key = await providerkey(provider);
20387
+ const at = Date.now();
20388
+ const outcome = await callmodel({ provider, model, messages: input.messages, ...key !== void 0 ? { apikey: key } : {}, transport: llmtransport });
20389
+ const usage = outcome.usage ?? { prompttokens: 0, completiontokens: 0, totaltokens: 0 };
20390
+ const cost = provider.costpermilliontokens !== void 0 && Number.isFinite(provider.costpermilliontokens) ? (usage.prompttokens + usage.completiontokens) / 1e6 * provider.costpermilliontokens : 0;
20391
+ await memory.addusagerecord({ id: randomid(), ...input.stepid !== void 0 ? { stepid: input.stepid } : {}, providerid: provider.id, endpoint: provider.endpoint, model, prompttokens: usage.prompttokens, completiontokens: usage.completiontokens, totaltokens: usage.totaltokens, cost, ...islocalorigin(provider.endpoint) ? { local: true } : {}, at });
20392
+ await audit("model", `The ${input.kind} model call reached the ${provider.endpoint} endpoint with the ${model} model and ${usage.totaltokens} total token${usage.totaltokens === 1 ? "" : "s"} (${usage.prompttokens} prompt and ${usage.completiontokens} completion)${cost > 0 ? ` at the recorded cost of ${cost.toFixed(4)}` : ""}; ${egress.reason ?? ""}`, {});
20393
+ return { text: outcome.text, provider, model, at };
20394
+ };
20395
+ try {
20396
+ return await attempt(pair.provider, pair.model);
20397
+ } catch (error) {
20398
+ const now = Date.now();
20399
+ const marked = markprovider({ providers, providerid: pair.provider.id, available: false, now });
20400
+ await memory.setproviders(marked);
20401
+ await audit("model", `The provider ${pair.provider.name} failed the ${input.kind} call and stays marked unavailable until its next success: ${error instanceof Error ? error.message : String(error)}`, {});
20402
+ const fallback = fallbackroute({ routes, providers: marked, kind: input.kind });
20403
+ if (fallback.provider === void 0 || fallback.model === void 0) throw error;
20404
+ const outcome = await attempt(fallback.provider, fallback.model);
20405
+ await memory.setproviders(markprovider({ providers: await memory.getproviders(), providerid: fallback.provider.id, available: true, now: Date.now() }));
20406
+ return outcome;
20407
+ }
20408
+ }
20409
+ async function recordguardnotice(kind, output) {
20410
+ if (output.verdict === "valid") return;
20411
+ await memory.addguardnotice(output);
20412
+ await audit("model", `The guardrails refused the ${kind} model output after ${output.attempts} attempt${output.attempts === 1 ? "" : "s"}: ${output.reason ?? "the output failed its guard"}`, {});
20413
+ }
20414
+ function draftplanof(draft, steps, origin) {
20415
+ const now = Date.now();
20416
+ const mapped = steps.map((step) => ({ id: step.id, kind: step.kind, ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {}, summary: step.summary, risk: actionrisk(step.kind) }));
20417
+ return { id: randomid(), objective: draft.goal, origin, steps: mapped, createdat: now, expiresat: now + sessionduration, state: "pending" };
20418
+ }
20419
+ async function llmstateof() {
20420
+ const providers = await memory.getproviders();
20421
+ const routes = await memory.getmodelroutes();
20422
+ const budget = await memory.getcostbudget();
20423
+ const local = await memory.getlocalmodel();
20424
+ const parse = await memory.getcommandparse();
20425
+ const tasks = llmtaskkinds.map((kind) => {
20426
+ const resolved = resolveroute({ routes, providers, kind });
20427
+ return { kind, ...resolved.provider !== void 0 ? { provider: resolved.provider.name, model: resolved.model } : {} };
20428
+ });
20429
+ return { providers, ...local !== void 0 ? { local } : {}, routes, routehistory: (await memory.getmodelroutehistory()).slice(0, 10), drafts: (await memory.getplandrafts()).slice(0, 10), replans: (await memory.getreplans()).slice(0, 10), notes: (await memory.getreflectnotes()).slice(0, 10), ...budget !== void 0 ? { budget } : {}, usage: await memory.getusage(), templates: searchtemplates(await memory.getprompttemplates(), "").slice(0, 20), ...parse !== void 0 ? { parse } : {}, guardnotices: (await memory.getguardnotices()).slice(0, 10), tasks, toolbriefs: rendertoolbriefs((buildtoolcatalog().domains[0]?.tools ?? []).slice(0, 4)) };
20430
+ }
19529
20431
  async function mcpstateof() {
19530
20432
  const config = await mcpconfigof();
19531
20433
  const state = await memory.getmcpstate();