@mindot/will 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -22
- package/dist/channels/discord.d.ts +1 -1
- package/dist/channels/whatsapp.d.ts +1 -1
- package/dist/cli.js +10823 -10454
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +562 -226
- package/dist/index.js.map +1 -1
- package/dist/mcp/effectors.d.ts +1 -1
- package/dist/{will-DAW0l-lY.d.ts → will-cS6k4uiJ.d.ts} +470 -84
- package/package.json +1 -1
- package/src/cognition/agency/engines/action.selector.ts +2 -1
- package/src/cognition/agency/engines/reafference.engine.ts +12 -2
- package/src/cognition/agency/reconcile.learning.ts +16 -2
- package/src/cognition/agency/schemas/repertoire.ts +12 -5
- package/src/cognition/config.mirror.entities.ts +1 -1
- package/src/cognition/faculties/executive.engine/engine.ts +136 -58
- package/src/cognition/faculties/executive.engine/facet.ts +10 -2
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +2 -1
- package/src/cognition/index.ts +4 -0
- package/src/cognition/memory/vector.embedder.ts +9 -5
- package/src/cognition/utilities/token.tracker.ts +191 -96
- package/src/host/boot.ts +78 -22
- package/src/index.ts +35 -0
- package/src/llm/index.ts +397 -96
- package/src/llm/routing.ts +198 -0
- package/src/llm/summarizer.ts +5 -1
- package/src/runners/thin-shim.runner.ts +18 -6
- package/src/sdk/will.ts +82 -16
- package/src/stem/guards/identity.coherence.ts +17 -6
- package/src/stem/index.ts +3 -3
- package/src/stem/mind.ts +155 -24
- package/src/stem/policy/arbiter.ts +49 -14
- package/src/stem/policy/rule.table.ts +2 -2
- package/src/stem/tracts/effector.controller.ts +56 -9
package/dist/index.js
CHANGED
|
@@ -2800,62 +2800,26 @@ var AsyncEngine = class {
|
|
|
2800
2800
|
}
|
|
2801
2801
|
}
|
|
2802
2802
|
};
|
|
2803
|
-
var MODEL_PRICING = {
|
|
2804
|
-
// OpenAI
|
|
2805
|
-
"openai/gpt-4o": { input: 2.5, output: 10 },
|
|
2806
|
-
"openai/gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
2807
|
-
"openai/gpt-4-turbo": { input: 10, output: 30 },
|
|
2808
|
-
"openai/gpt-3.5-turbo": { input: 0.5, output: 1.5 },
|
|
2809
|
-
// Anthropic (Claude 4.x family — prices per 1M tokens)
|
|
2810
|
-
"anthropic/claude-haiku-4-5": { input: 1, output: 5 },
|
|
2811
|
-
"anthropic/claude-sonnet-4-5": { input: 3, output: 15 },
|
|
2812
|
-
"anthropic/claude-sonnet-4-6": { input: 3, output: 15 },
|
|
2813
|
-
"anthropic/claude-opus-4-7": { input: 5, output: 25 },
|
|
2814
|
-
// Legacy aliases kept for backward compat
|
|
2815
|
-
"anthropic/claude-haiku-4": { input: 1, output: 5 },
|
|
2816
|
-
"anthropic/claude-opus-4": { input: 5, output: 25 },
|
|
2817
|
-
// Z.ai (GLM-5 family). `glm-5.2[1m]` is the same model asking for its 1M
|
|
2818
|
-
// context window — same rate, so it gets its own row rather than relying on
|
|
2819
|
-
// the normalizer (a future long-context tier would price differently).
|
|
2820
|
-
"glm/glm-5.2": { input: 1.4, output: 4.4 },
|
|
2821
|
-
"glm/glm-5.2[1m]": { input: 1.4, output: 4.4 },
|
|
2822
|
-
// Google
|
|
2823
|
-
"google/gemini-2.0-flash": { input: 0.1, output: 0.4 },
|
|
2824
|
-
"google/gemini-2.0-pro": { input: 1.25, output: 5 },
|
|
2825
|
-
// Meta (via Groq/Replicate)
|
|
2826
|
-
"meta/llama-3.3-70b": { input: 0.59, output: 0.79 },
|
|
2827
|
-
"meta/llama-4-maverick": { input: 0.2, output: 0.6 },
|
|
2828
|
-
// DeepSeek
|
|
2829
|
-
"deepseek/deepseek-v3": { input: 0.27, output: 1.1 },
|
|
2830
|
-
"deepseek/deepseek-r1": { input: 0.55, output: 2.19 },
|
|
2831
|
-
// Embeddings (input-only — no completion tokens). Priced per 1M input tokens.
|
|
2832
|
-
"openai/text-embedding-3-small": { input: 0.02, output: 0 },
|
|
2833
|
-
"openai/text-embedding-3-large": { input: 0.13, output: 0 },
|
|
2834
|
-
"google/text-embedding-004": { input: 0, output: 0 },
|
|
2835
|
-
// free tier
|
|
2836
|
-
"google/gemini-embedding-001": { input: 0, output: 0 },
|
|
2837
|
-
// free tier
|
|
2838
|
-
// Fallback for unknown models
|
|
2839
|
-
"__default__": { input: 3, output: 15 }
|
|
2840
|
-
};
|
|
2841
2803
|
var CACHE_READ_MULT = 0.1;
|
|
2842
2804
|
var CACHE_WRITE_MULT = 1.25;
|
|
2843
2805
|
function normalizeModelKey(model) {
|
|
2844
2806
|
let m = model.toLowerCase().trim();
|
|
2845
2807
|
const slash = m.lastIndexOf("/");
|
|
2846
2808
|
if (slash >= 0) m = m.slice(slash + 1);
|
|
2809
|
+
m = m.replace(/\[[^\]]*\]$/, "");
|
|
2847
2810
|
return m.replace(/[-@]\d{6,8}$/, "");
|
|
2848
2811
|
}
|
|
2849
|
-
var
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2812
|
+
var _unpricedWarned = /* @__PURE__ */ new Set();
|
|
2813
|
+
function resolvePricing(model, hostPrices) {
|
|
2814
|
+
if (!hostPrices) return null;
|
|
2815
|
+
const exact = hostPrices[model];
|
|
2816
|
+
if (exact) return exact;
|
|
2817
|
+
const norm = normalizeModelKey(model);
|
|
2818
|
+
if (hostPrices[norm]) return hostPrices[norm];
|
|
2819
|
+
for (const [key, price] of Object.entries(hostPrices)) {
|
|
2820
|
+
if (normalizeModelKey(key) === norm) return price;
|
|
2854
2821
|
}
|
|
2855
|
-
return
|
|
2856
|
-
})();
|
|
2857
|
-
function resolvePricing(model) {
|
|
2858
|
-
return PRICING_BY_NORM[normalizeModelKey(model)] ?? MODEL_PRICING[model] ?? MODEL_PRICING["__default__"];
|
|
2822
|
+
return null;
|
|
2859
2823
|
}
|
|
2860
2824
|
function composeLabel(m) {
|
|
2861
2825
|
const base = `${m.category}/${m.attribute}/${m.function}`;
|
|
@@ -2865,6 +2829,7 @@ var TokenTracker = class {
|
|
|
2865
2829
|
name = "token-tracker";
|
|
2866
2830
|
_emitCostEvents;
|
|
2867
2831
|
_costWarningThreshold;
|
|
2832
|
+
_prices;
|
|
2868
2833
|
// All recorded usage for the simulation run
|
|
2869
2834
|
_usageLog = [];
|
|
2870
2835
|
// Running totals
|
|
@@ -2876,6 +2841,11 @@ var TokenTracker = class {
|
|
|
2876
2841
|
_categoryTokens = /* @__PURE__ */ new Map();
|
|
2877
2842
|
_functionCosts = /* @__PURE__ */ new Map();
|
|
2878
2843
|
_functionTokens = /* @__PURE__ */ new Map();
|
|
2844
|
+
// Per-provider spend. The axis a host actually reconciles against invoices —
|
|
2845
|
+
// "which vendor did we pay?" is not answerable from the model id once routing
|
|
2846
|
+
// can reach one model through several of them.
|
|
2847
|
+
_providerCosts = /* @__PURE__ */ new Map();
|
|
2848
|
+
_providerTokens = /* @__PURE__ */ new Map();
|
|
2879
2849
|
// Per-tick costs (for spike detection)
|
|
2880
2850
|
_tickCosts = [];
|
|
2881
2851
|
_maxTickCostSamples = 1e3;
|
|
@@ -2891,6 +2861,7 @@ var TokenTracker = class {
|
|
|
2891
2861
|
constructor(config = {}) {
|
|
2892
2862
|
this._emitCostEvents = config.emitCostEvents ?? true;
|
|
2893
2863
|
this._costWarningThreshold = config.costWarningThresholdUsd ?? 0.05;
|
|
2864
|
+
this._prices = config.prices;
|
|
2894
2865
|
this._ledgerPath = config.writeLedger && config.willId ? `./data/wills/${config.willId}/debug/token-report.jsonl` : null;
|
|
2895
2866
|
}
|
|
2896
2867
|
// ── Public API: record usage ──────────────────────────────
|
|
@@ -2899,13 +2870,20 @@ var TokenTracker = class {
|
|
|
2899
2870
|
* Called by LLMDirector.call after each completion (src/llm/index.ts).
|
|
2900
2871
|
*/
|
|
2901
2872
|
recordUsage(usage) {
|
|
2902
|
-
const pricing = resolvePricing(usage.model);
|
|
2873
|
+
const pricing = resolvePricing(usage.model, this._prices);
|
|
2903
2874
|
const cacheRead = usage.cacheReadTokens ?? 0;
|
|
2904
2875
|
const cacheWrite = usage.cacheWriteTokens ?? 0;
|
|
2905
|
-
|
|
2876
|
+
if (!pricing && !_unpricedWarned.has(usage.model)) {
|
|
2877
|
+
_unpricedWarned.add(usage.model);
|
|
2878
|
+
logger.warn(
|
|
2879
|
+
`[tokens] no price for "${usage.model}" \u2014 reporting cost 0 for it. Supply one via llm.providers.<provider>.prices to get cost telemetry.`
|
|
2880
|
+
);
|
|
2881
|
+
}
|
|
2882
|
+
const costUsd = pricing ? usage.promptTokens / 1e6 * pricing.input + usage.completionTokens / 1e6 * pricing.output + cacheRead / 1e6 * pricing.input * CACHE_READ_MULT + cacheWrite / 1e6 * pricing.input * CACHE_WRITE_MULT : 0;
|
|
2906
2883
|
const full = {
|
|
2907
2884
|
...usage,
|
|
2908
2885
|
label: usage.label ?? composeLabel(usage),
|
|
2886
|
+
priced: pricing !== null,
|
|
2909
2887
|
estimatedCostUsd: Math.round(costUsd * 1e6) / 1e6
|
|
2910
2888
|
// round to micro-dollars
|
|
2911
2889
|
};
|
|
@@ -2915,6 +2893,7 @@ var TokenTracker = class {
|
|
|
2915
2893
|
this._totalCost += full.estimatedCostUsd;
|
|
2916
2894
|
this._accumulate(this._categoryCosts, this._categoryTokens, full.category, full);
|
|
2917
2895
|
this._accumulate(this._functionCosts, this._functionTokens, full.function, full);
|
|
2896
|
+
this._accumulate(this._providerCosts, this._providerTokens, full.provider ?? "unattributed", full);
|
|
2918
2897
|
this._emitLedger(full);
|
|
2919
2898
|
}
|
|
2920
2899
|
/**
|
|
@@ -2935,6 +2914,7 @@ var TokenTracker = class {
|
|
|
2935
2914
|
ts: new Date(wallClock()).toISOString(),
|
|
2936
2915
|
// determinism-ok: ledger timestamp is telemetry, never replay state
|
|
2937
2916
|
model: full.model,
|
|
2917
|
+
provider: full.provider,
|
|
2938
2918
|
category: full.category,
|
|
2939
2919
|
attribute: full.attribute,
|
|
2940
2920
|
function: full.function,
|
|
@@ -2946,6 +2926,10 @@ var TokenTracker = class {
|
|
|
2946
2926
|
cacheWriteTok: full.cacheWriteTokens ?? 0,
|
|
2947
2927
|
estPromptTok: full.estPromptTokens,
|
|
2948
2928
|
costUsd: full.estimatedCostUsd,
|
|
2929
|
+
// Whether costUsd came from a real price. False ⇒ 0 because nothing
|
|
2930
|
+
// priced this model, NOT because the call was free — a consumer summing
|
|
2931
|
+
// spend must not fold unpriced calls in as zero.
|
|
2932
|
+
priced: full.priced,
|
|
2949
2933
|
latencyMs: full.latencyMs
|
|
2950
2934
|
};
|
|
2951
2935
|
for (const fn of this._recordListeners) {
|
|
@@ -2984,23 +2968,14 @@ var TokenTracker = class {
|
|
|
2984
2968
|
commands.metrics.push(
|
|
2985
2969
|
["llm.prompt_tokens_total", this._totalPromptTokens],
|
|
2986
2970
|
["llm.completion_tokens_total", this._totalCompletionTokens],
|
|
2987
|
-
["llm.cost_total_usd", this._totalCost],
|
|
2988
|
-
["llm.cost_this_tick_usd", tickCost],
|
|
2989
|
-
["llm.cost_avg_per_tick_usd", this._averageTickCost()],
|
|
2990
2971
|
["llm.total_calls", this._usageLog.length]
|
|
2991
2972
|
);
|
|
2992
|
-
for (const [cat, cost] of this._categoryCosts) {
|
|
2993
|
-
commands.metrics.push([`llm.cost.${cat}`, cost]);
|
|
2994
|
-
}
|
|
2995
2973
|
for (const [cat, tok] of this._categoryTokens) {
|
|
2996
2974
|
commands.metrics.push(
|
|
2997
2975
|
[`llm.prompt_tokens.${cat}`, tok.prompt],
|
|
2998
2976
|
[`llm.completion_tokens.${cat}`, tok.completion]
|
|
2999
2977
|
);
|
|
3000
2978
|
}
|
|
3001
|
-
for (const [fn, cost] of this._functionCosts) {
|
|
3002
|
-
commands.metrics.push([`llm.cost.fn.${fn}`, cost]);
|
|
3003
|
-
}
|
|
3004
2979
|
if (tickCost > this._costWarningThreshold && this._emitCostEvents && tick - this._lastCostWarningTick > 50) {
|
|
3005
2980
|
this._lastCostWarningTick = tick;
|
|
3006
2981
|
events.push({
|
|
@@ -3044,6 +3019,22 @@ var TokenTracker = class {
|
|
|
3044
3019
|
get functionTokenBreakdown() {
|
|
3045
3020
|
return this._functionTokens;
|
|
3046
3021
|
}
|
|
3022
|
+
/**
|
|
3023
|
+
* Cost broken down by provider ('anthropic' | 'glm' | 'moonshot' | …), plus
|
|
3024
|
+
* an `unattributed` bucket for usage recorded without one.
|
|
3025
|
+
*
|
|
3026
|
+
* This is the axis a host reconciles against vendor invoices. Calls whose
|
|
3027
|
+
* model went unpriced contribute 0 here, so compare against
|
|
3028
|
+
* `getUsageLog()`'s `priced` flag before treating a small number as a small
|
|
3029
|
+
* bill.
|
|
3030
|
+
*/
|
|
3031
|
+
get providerBreakdown() {
|
|
3032
|
+
return this._providerCosts;
|
|
3033
|
+
}
|
|
3034
|
+
/** Token counts (prompt + completion) broken down by provider. */
|
|
3035
|
+
get providerTokenBreakdown() {
|
|
3036
|
+
return this._providerTokens;
|
|
3037
|
+
}
|
|
3047
3038
|
/** Cost per call average */
|
|
3048
3039
|
get averageCostPerCall() {
|
|
3049
3040
|
if (this._usageLog.length === 0) return 0;
|
|
@@ -3073,6 +3064,8 @@ var TokenTracker = class {
|
|
|
3073
3064
|
this._categoryTokens.clear();
|
|
3074
3065
|
this._functionCosts.clear();
|
|
3075
3066
|
this._functionTokens.clear();
|
|
3067
|
+
this._providerCosts.clear();
|
|
3068
|
+
this._providerTokens.clear();
|
|
3076
3069
|
this._tickCosts = [];
|
|
3077
3070
|
}
|
|
3078
3071
|
// ── Internal ─────────────────────────────────────────────
|
|
@@ -11567,7 +11560,7 @@ ${this._facetReasoningHistory.join("\n")}` : "";
|
|
|
11567
11560
|
ideationUserMessage,
|
|
11568
11561
|
tick: currentState.tick,
|
|
11569
11562
|
proposeTemperature,
|
|
11570
|
-
meta: { category: "executive", attribute: "facet", function: this._currentFocus?.function ?? "ideation", scope: this.facetId }
|
|
11563
|
+
meta: { category: "executive", attribute: "facet", function: this._currentFocus?.function ?? "ideation", scope: this.facetId, demand: processSelection.effortScore }
|
|
11571
11564
|
});
|
|
11572
11565
|
logger.info(
|
|
11573
11566
|
`[executive.facet] ${this.facetId} \u25C6 deliberate propose tick=${currentState.tick} candidates=${ideationCandidates?.length ?? 0} temp=${proposeTemperature.toFixed(2)}`
|
|
@@ -11602,8 +11595,13 @@ ${this._facetReasoningHistory.join("\n")}` : "";
|
|
|
11602
11595
|
const facetMeta = {
|
|
11603
11596
|
category: "executive",
|
|
11604
11597
|
attribute: "facet",
|
|
11605
|
-
|
|
11606
|
-
|
|
11598
|
+
// A focus that declares no function is making its decision call, the
|
|
11599
|
+
// facet's analogue of master's 'decision'. This previously fell back to
|
|
11600
|
+
// 'facet' — an *attribute* value, which quietly created a bogus bucket
|
|
11601
|
+
// in the by-function cost breakdown. The typed axes caught it.
|
|
11602
|
+
function: this._currentFocus?.function ?? "decision",
|
|
11603
|
+
scope: this.facetId,
|
|
11604
|
+
demand: processSelection.effortScore
|
|
11607
11605
|
};
|
|
11608
11606
|
const result = this._chunkHandler ? await this._llmDirector.callStream(systemPrompt, userMessage, currentState.tick, this._chunkHandler, void 0, facetMeta) : await this._llmDirector.call(systemPrompt, userMessage, currentState.tick, void 0, facetMeta);
|
|
11609
11607
|
output = parseResponse(result.text, currentState, []);
|
|
@@ -11877,6 +11875,67 @@ function updateGatingState(gs, state, _tick, didActivate, cleanedBuffer) {
|
|
|
11877
11875
|
gs.lastExecutiveTick = _tick;
|
|
11878
11876
|
}
|
|
11879
11877
|
|
|
11878
|
+
// src/llm/routing.ts
|
|
11879
|
+
var NULL_ROUTER = {
|
|
11880
|
+
name: "null",
|
|
11881
|
+
route() {
|
|
11882
|
+
return null;
|
|
11883
|
+
}
|
|
11884
|
+
};
|
|
11885
|
+
function isNullRouter(router) {
|
|
11886
|
+
return !router || router === NULL_ROUTER;
|
|
11887
|
+
}
|
|
11888
|
+
var TableRouter = class {
|
|
11889
|
+
name;
|
|
11890
|
+
_rules;
|
|
11891
|
+
constructor(rules, name = "table") {
|
|
11892
|
+
this._rules = [...rules];
|
|
11893
|
+
this.name = name;
|
|
11894
|
+
}
|
|
11895
|
+
route(meta) {
|
|
11896
|
+
for (const rule of this._rules) {
|
|
11897
|
+
if (matches(rule, meta)) return rule.route;
|
|
11898
|
+
}
|
|
11899
|
+
return null;
|
|
11900
|
+
}
|
|
11901
|
+
};
|
|
11902
|
+
function chainRouters(...routers) {
|
|
11903
|
+
const chain = routers.filter((r) => !isNullRouter(r));
|
|
11904
|
+
if (chain.length === 0) return NULL_ROUTER;
|
|
11905
|
+
if (chain.length === 1) return chain[0];
|
|
11906
|
+
const warned = /* @__PURE__ */ new Set();
|
|
11907
|
+
return {
|
|
11908
|
+
name: chain.map((r) => r.name).join(">"),
|
|
11909
|
+
route(meta) {
|
|
11910
|
+
for (const router of chain) {
|
|
11911
|
+
try {
|
|
11912
|
+
const hit = router.route(meta);
|
|
11913
|
+
if (hit) return hit;
|
|
11914
|
+
} catch (err) {
|
|
11915
|
+
if (!warned.has(router.name)) {
|
|
11916
|
+
warned.add(router.name);
|
|
11917
|
+
logger.warn(`[llm.routing] router "${router.name}" threw \u2014 skipping it: ${err.message}`);
|
|
11918
|
+
}
|
|
11919
|
+
}
|
|
11920
|
+
}
|
|
11921
|
+
return null;
|
|
11922
|
+
}
|
|
11923
|
+
};
|
|
11924
|
+
}
|
|
11925
|
+
function matches(rule, meta) {
|
|
11926
|
+
if (rule.category !== void 0 && rule.category !== meta.category) return false;
|
|
11927
|
+
if (rule.attribute !== void 0 && rule.attribute !== meta.attribute) return false;
|
|
11928
|
+
if (rule.function !== void 0 && rule.function !== meta.function) return false;
|
|
11929
|
+
const bounded = rule.minDemand !== void 0 || rule.maxDemand !== void 0;
|
|
11930
|
+
if (bounded) {
|
|
11931
|
+
const d = meta.demand;
|
|
11932
|
+
if (typeof d !== "number" || Number.isNaN(d)) return false;
|
|
11933
|
+
if (rule.minDemand !== void 0 && d < rule.minDemand) return false;
|
|
11934
|
+
if (rule.maxDemand !== void 0 && d >= rule.maxDemand) return false;
|
|
11935
|
+
}
|
|
11936
|
+
return true;
|
|
11937
|
+
}
|
|
11938
|
+
|
|
11880
11939
|
// src/llm/gate.ts
|
|
11881
11940
|
var MAX_CONCURRENT = parseInt(process.env.WILL_LLM_CONCURRENCY ?? "2");
|
|
11882
11941
|
var maxRetries = () => parseInt(process.env.WILL_LLM_MAX_RETRIES ?? "4");
|
|
@@ -11947,39 +12006,92 @@ async function withGate(fn, label, gate = llmGate) {
|
|
|
11947
12006
|
}
|
|
11948
12007
|
|
|
11949
12008
|
// src/llm/index.ts
|
|
11950
|
-
var
|
|
11951
|
-
|
|
11952
|
-
|
|
12009
|
+
var MOCK_PROVIDER = "mock";
|
|
12010
|
+
var MOCK_MODEL = "mock";
|
|
12011
|
+
var KNOWN_PROVIDERS = {
|
|
12012
|
+
// Never dialled — present so a test-mode Will resolves an endpoint without
|
|
12013
|
+
// demanding a provider the run will never use.
|
|
12014
|
+
[MOCK_PROVIDER]: { wire: "anthropic", baseUrl: "http://mock.invalid/v1" },
|
|
12015
|
+
// ── Anthropic wire ──────────────────────────────────────────
|
|
12016
|
+
anthropic: { wire: "anthropic", baseUrl: "https://api.anthropic.com/v1" },
|
|
12017
|
+
// Z.ai documents the base as `…/api/anthropic` because the Anthropic SDK
|
|
12018
|
+
// appends `/v1/messages`; this client appends `/messages`, so the version
|
|
12019
|
+
// segment belongs here — verified against the live endpoint.
|
|
12020
|
+
glm: { wire: "anthropic", baseUrl: "https://api.z.ai/api/anthropic/v1" },
|
|
12021
|
+
// ── OpenAI wire ─────────────────────────────────────────────
|
|
12022
|
+
openai: { wire: "openai", baseUrl: "https://api.openai.com/v1" },
|
|
12023
|
+
deepseek: { wire: "openai", baseUrl: "https://api.deepseek.com/v1" },
|
|
12024
|
+
moonshot: { wire: "openai", baseUrl: "https://api.moonshot.ai/v1" },
|
|
12025
|
+
qwen: { wire: "openai", baseUrl: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1" },
|
|
12026
|
+
xai: { wire: "openai", baseUrl: "https://api.x.ai/v1" },
|
|
12027
|
+
minimax: { wire: "openai", baseUrl: "https://api.minimax.io/v1" },
|
|
12028
|
+
mistral: { wire: "openai", baseUrl: "https://api.mistral.ai/v1" },
|
|
12029
|
+
// Local runtimes. The port is the project default; a host that moved it sets
|
|
12030
|
+
// `baseUrl`. Both still want an `apiKey` — any non-empty string will do,
|
|
12031
|
+
// since neither checks it.
|
|
12032
|
+
ollama: { wire: "openai", baseUrl: "http://localhost:11434/v1" },
|
|
12033
|
+
vllm: { wire: "openai", baseUrl: "http://localhost:8000/v1" },
|
|
12034
|
+
// ── Google wire ─────────────────────────────────────────────
|
|
12035
|
+
// Gemini also exposes an OpenAI-compatible surface; this client speaks the
|
|
12036
|
+
// native one, which is where its caching and multimodal parts actually live.
|
|
12037
|
+
google: { wire: "google", baseUrl: "https://generativelanguage.googleapis.com/v1beta" }
|
|
12038
|
+
};
|
|
12039
|
+
var PROVIDER_KEY_ENV = {
|
|
12040
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
12041
|
+
glm: "ZAI_API_KEY",
|
|
12042
|
+
openai: "OPENAI_API_KEY",
|
|
12043
|
+
google: "GOOGLE_API_KEY",
|
|
12044
|
+
deepseek: "DEEPSEEK_API_KEY",
|
|
12045
|
+
moonshot: "MOONSHOT_API_KEY",
|
|
12046
|
+
qwen: "DASHSCOPE_API_KEY",
|
|
12047
|
+
xai: "XAI_API_KEY",
|
|
12048
|
+
minimax: "MINIMAX_API_KEY",
|
|
12049
|
+
mistral: "MISTRAL_API_KEY"
|
|
12050
|
+
};
|
|
12051
|
+
function providerKeyFromEnv(provider) {
|
|
12052
|
+
const name = PROVIDER_KEY_ENV[provider];
|
|
12053
|
+
if (!name) return void 0;
|
|
12054
|
+
const value = nonBlank(process.env[name]) ?? (provider === "google" ? nonBlank(process.env["GEMINI_API_KEY"]) : void 0);
|
|
12055
|
+
return value;
|
|
11953
12056
|
}
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
return "https://api.anthropic.com/v1";
|
|
11958
|
-
// Z.ai documents the base as `…/api/anthropic` because the Anthropic SDK
|
|
11959
|
-
// appends `/v1/messages`; this client appends `/messages`, so the version
|
|
11960
|
-
// segment belongs here — verified against the live endpoint.
|
|
11961
|
-
case "glm":
|
|
11962
|
-
return "https://api.z.ai/api/anthropic/v1";
|
|
11963
|
-
case "openai":
|
|
11964
|
-
return "https://api.openai.com/v1";
|
|
11965
|
-
case "deepseek":
|
|
11966
|
-
return "https://api.deepseek.com/v1";
|
|
11967
|
-
case "google":
|
|
11968
|
-
return "https://generativelanguage.googleapis.com/v1beta";
|
|
11969
|
-
}
|
|
12057
|
+
var nonBlank = (v) => v && v.trim() ? v : void 0;
|
|
12058
|
+
function knownWireFor(provider) {
|
|
12059
|
+
return KNOWN_PROVIDERS[provider]?.wire;
|
|
11970
12060
|
}
|
|
11971
|
-
function
|
|
11972
|
-
return provider
|
|
12061
|
+
function defaultBaseFor(provider) {
|
|
12062
|
+
return KNOWN_PROVIDERS[provider]?.baseUrl;
|
|
11973
12063
|
}
|
|
11974
12064
|
function anthropicWireHeaders(provider, apiKey) {
|
|
11975
12065
|
return {
|
|
11976
12066
|
"Content-Type": "application/json",
|
|
11977
12067
|
"anthropic-version": "2023-06-01",
|
|
11978
12068
|
"x-api-key": apiKey,
|
|
11979
|
-
...provider === "
|
|
12069
|
+
...provider === "anthropic" ? {} : { Authorization: `Bearer ${apiKey}` }
|
|
11980
12070
|
};
|
|
11981
12071
|
}
|
|
12072
|
+
var BACKGROUND_DEMAND = 0.1;
|
|
12073
|
+
var ESCALATION_DEMAND = 0.7;
|
|
11982
12074
|
var DEFAULT_CALL_META = { category: "executive", attribute: "master", function: "decision" };
|
|
12075
|
+
function resolveEndpoint(spec) {
|
|
12076
|
+
const wire = spec.wire ?? knownWireFor(spec.provider);
|
|
12077
|
+
if (!wire)
|
|
12078
|
+
throw new Error(
|
|
12079
|
+
`LLM provider "${spec.provider}" has no known wire. Declare it: llm.providers['${spec.provider}'].wire = 'anthropic' | 'openai' | 'google'.`
|
|
12080
|
+
);
|
|
12081
|
+
const baseUrl = spec.baseUrl ?? defaultBaseFor(spec.provider);
|
|
12082
|
+
if (!baseUrl)
|
|
12083
|
+
throw new Error(
|
|
12084
|
+
`LLM provider "${spec.provider}" has no known base URL. Declare it: llm.providers['${spec.provider}'].baseUrl.`
|
|
12085
|
+
);
|
|
12086
|
+
return {
|
|
12087
|
+
provider: spec.provider,
|
|
12088
|
+
wire,
|
|
12089
|
+
model: spec.model,
|
|
12090
|
+
apiKey: spec.apiKey,
|
|
12091
|
+
baseUrl,
|
|
12092
|
+
maxOutputTokens: spec.maxOutputTokens
|
|
12093
|
+
};
|
|
12094
|
+
}
|
|
11983
12095
|
var LLMDirector = class {
|
|
11984
12096
|
_willId;
|
|
11985
12097
|
_model;
|
|
@@ -11991,6 +12103,12 @@ var LLMDirector = class {
|
|
|
11991
12103
|
_baseUrl;
|
|
11992
12104
|
_timeoutMs;
|
|
11993
12105
|
_tokenTracker;
|
|
12106
|
+
_router;
|
|
12107
|
+
_credentials;
|
|
12108
|
+
/** Default endpoint — what every call used before the routing seam existed. */
|
|
12109
|
+
_defaultEndpoint;
|
|
12110
|
+
/** Routes already warned about (missing credential / bad provider) — log once. */
|
|
12111
|
+
_routeWarned = /* @__PURE__ */ new Set();
|
|
11994
12112
|
constructor(config) {
|
|
11995
12113
|
this._willId = config.willId;
|
|
11996
12114
|
this._model = config.model;
|
|
@@ -12002,6 +12120,67 @@ var LLMDirector = class {
|
|
|
12002
12120
|
this._baseUrl = config.baseUrl ?? null;
|
|
12003
12121
|
this._timeoutMs = config.timeoutMs ?? 9e4;
|
|
12004
12122
|
this._tokenTracker = config.tokenTracker ?? null;
|
|
12123
|
+
this._router = config.router ?? null;
|
|
12124
|
+
this._credentials = config.credentials ?? {};
|
|
12125
|
+
this._defaultEndpoint = resolveEndpoint({
|
|
12126
|
+
provider: this._provider,
|
|
12127
|
+
model: this._model,
|
|
12128
|
+
apiKey: this._apiKey,
|
|
12129
|
+
baseUrl: this._baseUrl ?? void 0,
|
|
12130
|
+
wire: config.wire,
|
|
12131
|
+
maxOutputTokens: this._maxOutputTokens
|
|
12132
|
+
});
|
|
12133
|
+
}
|
|
12134
|
+
/**
|
|
12135
|
+
* Resolve which model serves this call. Falls back to the default endpoint
|
|
12136
|
+
* whenever the router has no opinion, throws, or names a provider we hold no
|
|
12137
|
+
* credential for — degrade, never crash.
|
|
12138
|
+
*/
|
|
12139
|
+
_resolveEndpoint(meta) {
|
|
12140
|
+
if (isNullRouter(this._router)) return this._defaultEndpoint;
|
|
12141
|
+
let route;
|
|
12142
|
+
try {
|
|
12143
|
+
route = this._router.route(meta);
|
|
12144
|
+
} catch (err) {
|
|
12145
|
+
this._warnRouteOnce(
|
|
12146
|
+
`throw:${this._router.name}`,
|
|
12147
|
+
`router "${this._router.name}" threw \u2014 using the default model`,
|
|
12148
|
+
err
|
|
12149
|
+
);
|
|
12150
|
+
return this._defaultEndpoint;
|
|
12151
|
+
}
|
|
12152
|
+
if (!route) return this._defaultEndpoint;
|
|
12153
|
+
const provider = route.provider ?? this._defaultEndpoint.provider;
|
|
12154
|
+
const cred = provider === this._defaultEndpoint.provider ? { apiKey: this._defaultEndpoint.apiKey, baseUrl: this._defaultEndpoint.baseUrl, wire: this._defaultEndpoint.wire } : this._credentials[provider];
|
|
12155
|
+
if (!cred?.apiKey) {
|
|
12156
|
+
this._warnRouteOnce(
|
|
12157
|
+
`cred:${provider}`,
|
|
12158
|
+
`no credential for routed provider "${provider}" \u2014 using the default model`
|
|
12159
|
+
);
|
|
12160
|
+
return this._defaultEndpoint;
|
|
12161
|
+
}
|
|
12162
|
+
try {
|
|
12163
|
+
return resolveEndpoint({
|
|
12164
|
+
provider,
|
|
12165
|
+
model: route.model,
|
|
12166
|
+
apiKey: cred.apiKey,
|
|
12167
|
+
baseUrl: route.baseUrl ?? cred.baseUrl,
|
|
12168
|
+
wire: cred.wire,
|
|
12169
|
+
maxOutputTokens: route.maxOutputTokens ?? this._defaultEndpoint.maxOutputTokens
|
|
12170
|
+
});
|
|
12171
|
+
} catch (err) {
|
|
12172
|
+
this._warnRouteOnce(
|
|
12173
|
+
`resolve:${provider}`,
|
|
12174
|
+
`cannot reach routed provider "${provider}" \u2014 using the default model`,
|
|
12175
|
+
err
|
|
12176
|
+
);
|
|
12177
|
+
return this._defaultEndpoint;
|
|
12178
|
+
}
|
|
12179
|
+
}
|
|
12180
|
+
_warnRouteOnce(key, message, err) {
|
|
12181
|
+
if (this._routeWarned.has(key)) return;
|
|
12182
|
+
this._routeWarned.add(key);
|
|
12183
|
+
logger.warn(`[llm.routing] ${message}`, err instanceof Error ? err.message : "");
|
|
12005
12184
|
}
|
|
12006
12185
|
// ── Mock response (test mode) ────────────────────────────
|
|
12007
12186
|
/**
|
|
@@ -12076,18 +12255,19 @@ var LLMDirector = class {
|
|
|
12076
12255
|
if (!replay.mock) onChunk(replay.text);
|
|
12077
12256
|
return { text: replay.text, inputTok: replay.inputTok, outputTok: replay.outputTok };
|
|
12078
12257
|
}
|
|
12258
|
+
const ep = this._resolveEndpoint(meta);
|
|
12079
12259
|
if (this._mock) {
|
|
12080
12260
|
const result2 = this._mockResponse(tick, userMessage);
|
|
12081
|
-
this._recordCompletion(systemPrompt, userMessage, tick, result2, Date.now() - start, true);
|
|
12261
|
+
this._recordCompletion(systemPrompt, userMessage, tick, result2, Date.now() - start, true, ep);
|
|
12082
12262
|
return result2;
|
|
12083
12263
|
}
|
|
12084
|
-
const result =
|
|
12085
|
-
const r = await this._callProvider(systemPrompt, userMessage, temperature);
|
|
12264
|
+
const result = ep.wire === "anthropic" ? await this._callAnthropicStream(ep, systemPrompt, userMessage, onChunk, temperature) : await (async () => {
|
|
12265
|
+
const r = await this._callProvider(ep, systemPrompt, userMessage, temperature);
|
|
12086
12266
|
onChunk(r.text);
|
|
12087
12267
|
return r;
|
|
12088
12268
|
})();
|
|
12089
|
-
this._track(result, meta, tick, Date.now() - start, this._estPromptTokens(systemPrompt, userMessage));
|
|
12090
|
-
this._recordCompletion(systemPrompt, userMessage, tick, result, Date.now() - start, false);
|
|
12269
|
+
this._track(result, meta, tick, Date.now() - start, this._estPromptTokens(systemPrompt, userMessage), ep);
|
|
12270
|
+
this._recordCompletion(systemPrompt, userMessage, tick, result, Date.now() - start, false, ep);
|
|
12091
12271
|
return result;
|
|
12092
12272
|
}
|
|
12093
12273
|
/**
|
|
@@ -12096,9 +12276,14 @@ var LLMDirector = class {
|
|
|
12096
12276
|
* mock/replay directors, so the call is simply skipped. Cache read/write tokens
|
|
12097
12277
|
* are forwarded so the tracker prices them at 0.1× / 1.25× input.
|
|
12098
12278
|
*/
|
|
12099
|
-
_track(result, meta, tick, latencyMs, estPromptTokens) {
|
|
12279
|
+
_track(result, meta, tick, latencyMs, estPromptTokens, ep = this._defaultEndpoint) {
|
|
12100
12280
|
this._tokenTracker?.recordUsage({
|
|
12101
|
-
|
|
12281
|
+
// The endpoint that actually served this call — routed or default.
|
|
12282
|
+
// Pricing must follow the real model, or routed spend is attributed
|
|
12283
|
+
// wrongly; the provider rides along because the same model id can be
|
|
12284
|
+
// reached from several vendors at very different prices.
|
|
12285
|
+
model: ep.model,
|
|
12286
|
+
provider: ep.provider,
|
|
12102
12287
|
promptTokens: result.inputTok,
|
|
12103
12288
|
completionTokens: result.outputTok,
|
|
12104
12289
|
totalTokens: result.inputTok + result.outputTok,
|
|
@@ -12124,14 +12309,16 @@ var LLMDirector = class {
|
|
|
12124
12309
|
* The LLM is the non-deterministic oracle; recording its input+output is the
|
|
12125
12310
|
* prerequisite for deterministic re-execution (REORIENT R2, deferred).
|
|
12126
12311
|
*/
|
|
12127
|
-
_recordCompletion(systemPrompt, userMessage, tick, result, latencyMs, mock) {
|
|
12312
|
+
_recordCompletion(systemPrompt, userMessage, tick, result, latencyMs, mock, ep = this._defaultEndpoint) {
|
|
12128
12313
|
try {
|
|
12129
12314
|
getCompletionRecorder(this._willId)?.recordCompletion({
|
|
12130
12315
|
tick,
|
|
12131
12316
|
willId: this._willId,
|
|
12132
|
-
|
|
12133
|
-
model
|
|
12134
|
-
|
|
12317
|
+
// Record the endpoint that actually served the call: the tape is what
|
|
12318
|
+
// replay re-feeds, so it must say which model produced this text.
|
|
12319
|
+
provider: ep.provider,
|
|
12320
|
+
model: ep.model,
|
|
12321
|
+
maxOutputTokens: ep.maxOutputTokens,
|
|
12135
12322
|
systemPrompt,
|
|
12136
12323
|
userMessage,
|
|
12137
12324
|
text: result.text,
|
|
@@ -12154,17 +12341,17 @@ var LLMDirector = class {
|
|
|
12154
12341
|
_replayCompletion(systemPrompt, userMessage, tick) {
|
|
12155
12342
|
return getCompletionSource(this._willId)?.nextCompletion(tick, systemPrompt, userMessage);
|
|
12156
12343
|
}
|
|
12157
|
-
async _callAnthropicStream(systemPrompt, userMessage, onChunk, temperature) {
|
|
12344
|
+
async _callAnthropicStream(ep, systemPrompt, userMessage, onChunk, temperature) {
|
|
12158
12345
|
const controller = new AbortController();
|
|
12159
12346
|
const timer = setTimeout(() => controller.abort(), this._timeoutMs);
|
|
12160
12347
|
let res;
|
|
12161
12348
|
try {
|
|
12162
|
-
res = await fetch(`${this._resolvedBase()}/messages`, {
|
|
12349
|
+
res = await fetch(`${this._resolvedBase(ep)}/messages`, {
|
|
12163
12350
|
method: "POST",
|
|
12164
|
-
headers: anthropicWireHeaders(
|
|
12351
|
+
headers: anthropicWireHeaders(ep.provider, ep.apiKey),
|
|
12165
12352
|
body: JSON.stringify({
|
|
12166
|
-
model:
|
|
12167
|
-
max_tokens:
|
|
12353
|
+
model: ep.model,
|
|
12354
|
+
max_tokens: ep.maxOutputTokens,
|
|
12168
12355
|
...temperature !== void 0 ? { temperature } : {},
|
|
12169
12356
|
stream: true,
|
|
12170
12357
|
system: this._systemField(systemPrompt),
|
|
@@ -12175,7 +12362,7 @@ var LLMDirector = class {
|
|
|
12175
12362
|
} catch (err) {
|
|
12176
12363
|
clearTimeout(timer);
|
|
12177
12364
|
if (controller.signal.aborted)
|
|
12178
|
-
throw new Error(`LLM stream to ${
|
|
12365
|
+
throw new Error(`LLM stream to ${ep.provider} timed out after ${this._timeoutMs}ms (no response)`);
|
|
12179
12366
|
throw err;
|
|
12180
12367
|
}
|
|
12181
12368
|
clearTimeout(timer);
|
|
@@ -12230,43 +12417,36 @@ var LLMDirector = class {
|
|
|
12230
12417
|
const replay = this._replayCompletion(systemPrompt, userMessage, tick);
|
|
12231
12418
|
if (replay)
|
|
12232
12419
|
return { text: replay.text, inputTok: replay.inputTok, outputTok: replay.outputTok };
|
|
12420
|
+
const ep = this._resolveEndpoint(meta);
|
|
12233
12421
|
if (this._mock) {
|
|
12234
12422
|
const result2 = this._mockResponse(tick, userMessage);
|
|
12235
|
-
this._recordCompletion(systemPrompt, userMessage, tick, result2, Date.now() - llmStart, true);
|
|
12423
|
+
this._recordCompletion(systemPrompt, userMessage, tick, result2, Date.now() - llmStart, true, ep);
|
|
12236
12424
|
return result2;
|
|
12237
12425
|
}
|
|
12238
12426
|
const result = await withGate(
|
|
12239
|
-
() =>
|
|
12240
|
-
}, temperature) : this._callProvider(systemPrompt, userMessage, temperature),
|
|
12427
|
+
() => ep.wire === "anthropic" ? this._callAnthropicStream(ep, systemPrompt, userMessage, () => {
|
|
12428
|
+
}, temperature) : this._callProvider(ep, systemPrompt, userMessage, temperature),
|
|
12241
12429
|
"executive/direct"
|
|
12242
12430
|
);
|
|
12243
|
-
this._track(result, meta, tick, Date.now() - llmStart, this._estPromptTokens(systemPrompt, userMessage));
|
|
12244
|
-
this._recordCompletion(systemPrompt, userMessage, tick, result, Date.now() - llmStart, false);
|
|
12431
|
+
this._track(result, meta, tick, Date.now() - llmStart, this._estPromptTokens(systemPrompt, userMessage), ep);
|
|
12432
|
+
this._recordCompletion(systemPrompt, userMessage, tick, result, Date.now() - llmStart, false, ep);
|
|
12245
12433
|
return result;
|
|
12246
12434
|
}
|
|
12247
|
-
_callProvider(systemPrompt, userMessage, temperature) {
|
|
12248
|
-
switch (
|
|
12435
|
+
_callProvider(ep, systemPrompt, userMessage, temperature) {
|
|
12436
|
+
switch (ep.wire) {
|
|
12249
12437
|
case "anthropic":
|
|
12250
|
-
return this._callAnthropic(systemPrompt, userMessage, temperature);
|
|
12251
|
-
case "glm":
|
|
12252
|
-
return this._callAnthropic(systemPrompt, userMessage, temperature);
|
|
12253
|
-
case "deepseek":
|
|
12254
|
-
return this._callOpenAI(systemPrompt, userMessage, temperature);
|
|
12438
|
+
return this._callAnthropic(ep, systemPrompt, userMessage, temperature);
|
|
12255
12439
|
case "openai":
|
|
12256
|
-
return this._callOpenAI(systemPrompt, userMessage, temperature);
|
|
12440
|
+
return this._callOpenAI(ep, systemPrompt, userMessage, temperature);
|
|
12257
12441
|
case "google":
|
|
12258
|
-
return this._callGoogle(systemPrompt, userMessage, temperature);
|
|
12442
|
+
return this._callGoogle(ep, systemPrompt, userMessage, temperature);
|
|
12259
12443
|
default:
|
|
12260
|
-
throw new Error(`Unknown LLM
|
|
12444
|
+
throw new Error(`Unknown LLM wire: ${ep.wire}`);
|
|
12261
12445
|
}
|
|
12262
12446
|
}
|
|
12263
|
-
/** Default API base URL (including version segment) for a provider. */
|
|
12264
|
-
_baseFor(provider) {
|
|
12265
|
-
return defaultBaseFor(provider);
|
|
12266
|
-
}
|
|
12267
12447
|
/** Resolved API base: explicit override wins, else the provider default. */
|
|
12268
|
-
_resolvedBase() {
|
|
12269
|
-
return
|
|
12448
|
+
_resolvedBase(ep) {
|
|
12449
|
+
return ep.baseUrl;
|
|
12270
12450
|
}
|
|
12271
12451
|
/**
|
|
12272
12452
|
* fetch() with a hard per-request deadline. A hung connection is aborted
|
|
@@ -12292,21 +12472,21 @@ var LLMDirector = class {
|
|
|
12292
12472
|
_systemField(systemPrompt) {
|
|
12293
12473
|
return [{ type: "text", text: systemPrompt, cache_control: { type: "ephemeral" } }];
|
|
12294
12474
|
}
|
|
12295
|
-
async _callAnthropic(systemPrompt, userMessage, temperature) {
|
|
12475
|
+
async _callAnthropic(ep, systemPrompt, userMessage, temperature) {
|
|
12296
12476
|
const body = {
|
|
12297
|
-
model:
|
|
12298
|
-
max_tokens:
|
|
12477
|
+
model: ep.model,
|
|
12478
|
+
max_tokens: ep.maxOutputTokens,
|
|
12299
12479
|
...temperature !== void 0 ? { temperature } : {},
|
|
12300
12480
|
system: this._systemField(systemPrompt),
|
|
12301
12481
|
messages: [{ role: "user", content: userMessage }]
|
|
12302
12482
|
};
|
|
12303
|
-
const res = await this._fetchWithTimeout(`${this._resolvedBase()}/messages`, {
|
|
12483
|
+
const res = await this._fetchWithTimeout(`${this._resolvedBase(ep)}/messages`, {
|
|
12304
12484
|
method: "POST",
|
|
12305
|
-
headers: anthropicWireHeaders(
|
|
12485
|
+
headers: anthropicWireHeaders(ep.provider, ep.apiKey),
|
|
12306
12486
|
body: JSON.stringify(body)
|
|
12307
12487
|
});
|
|
12308
12488
|
if (!res.ok)
|
|
12309
|
-
throw new Error(`${
|
|
12489
|
+
throw new Error(`${ep.provider} API ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
12310
12490
|
const data = await res.json(), text = data.content.find((b) => b.type === "text")?.text ?? "";
|
|
12311
12491
|
return {
|
|
12312
12492
|
text,
|
|
@@ -12316,21 +12496,21 @@ var LLMDirector = class {
|
|
|
12316
12496
|
cacheWriteTok: data.usage.cache_creation_input_tokens ?? 0
|
|
12317
12497
|
};
|
|
12318
12498
|
}
|
|
12319
|
-
async _callOpenAI(systemPrompt, userMessage, temperature) {
|
|
12499
|
+
async _callOpenAI(ep, systemPrompt, userMessage, temperature) {
|
|
12320
12500
|
const body = {
|
|
12321
|
-
model:
|
|
12322
|
-
max_completion_tokens:
|
|
12501
|
+
model: ep.model,
|
|
12502
|
+
max_completion_tokens: ep.maxOutputTokens,
|
|
12323
12503
|
...temperature !== void 0 ? { temperature } : {},
|
|
12324
12504
|
messages: [
|
|
12325
12505
|
{ role: "system", content: systemPrompt },
|
|
12326
12506
|
{ role: "user", content: userMessage }
|
|
12327
12507
|
]
|
|
12328
12508
|
};
|
|
12329
|
-
const res = await this._fetchWithTimeout(`${this._resolvedBase()}/chat/completions`, {
|
|
12509
|
+
const res = await this._fetchWithTimeout(`${this._resolvedBase(ep)}/chat/completions`, {
|
|
12330
12510
|
method: "POST",
|
|
12331
12511
|
headers: {
|
|
12332
12512
|
"Content-Type": "application/json",
|
|
12333
|
-
"Authorization": `Bearer ${
|
|
12513
|
+
"Authorization": `Bearer ${ep.apiKey}`
|
|
12334
12514
|
},
|
|
12335
12515
|
body: JSON.stringify(body)
|
|
12336
12516
|
});
|
|
@@ -12343,22 +12523,22 @@ var LLMDirector = class {
|
|
|
12343
12523
|
outputTok: data.usage.completion_tokens
|
|
12344
12524
|
};
|
|
12345
12525
|
}
|
|
12346
|
-
async _callGoogle(systemPrompt, userMessage, temperature) {
|
|
12526
|
+
async _callGoogle(ep, systemPrompt, userMessage, temperature) {
|
|
12347
12527
|
const body = {
|
|
12348
12528
|
systemInstruction: { parts: [{ text: systemPrompt }] },
|
|
12349
12529
|
contents: [{ role: "user", parts: [{ text: userMessage }] }],
|
|
12350
12530
|
generationConfig: {
|
|
12351
|
-
maxOutputTokens:
|
|
12531
|
+
maxOutputTokens: ep.maxOutputTokens,
|
|
12352
12532
|
...temperature !== void 0 ? { temperature } : {}
|
|
12353
12533
|
}
|
|
12354
12534
|
};
|
|
12355
12535
|
const res = await this._fetchWithTimeout(
|
|
12356
|
-
`${this._resolvedBase()}/models/${
|
|
12536
|
+
`${this._resolvedBase(ep)}/models/${ep.model}:generateContent`,
|
|
12357
12537
|
{
|
|
12358
12538
|
method: "POST",
|
|
12359
12539
|
headers: {
|
|
12360
12540
|
"Content-Type": "application/json",
|
|
12361
|
-
"x-goog-api-key":
|
|
12541
|
+
"x-goog-api-key": ep.apiKey
|
|
12362
12542
|
},
|
|
12363
12543
|
body: JSON.stringify(body)
|
|
12364
12544
|
}
|
|
@@ -12836,13 +13016,14 @@ var ExecutiveEngine = class extends AsyncEngine {
|
|
|
12836
13016
|
_lastExecutiveTick = -100;
|
|
12837
13017
|
// ── Injected dependencies ──────────────────────────────────
|
|
12838
13018
|
_willId = null;
|
|
12839
|
-
/**
|
|
12840
|
-
|
|
13019
|
+
/**
|
|
13020
|
+
* The Will's default model (config.model's `executive` role, resolved in
|
|
13021
|
+
* mind.ts). Every other role reaches its model through the router — see
|
|
13022
|
+
* `compileRoleRouter`.
|
|
13023
|
+
*/
|
|
13024
|
+
_modelId = null;
|
|
12841
13025
|
/** Per-Will LLM transport overrides (config.llm) — env fallbacks apply per field. */
|
|
12842
13026
|
_llm = null;
|
|
12843
|
-
/** One director per distinct model — same config, different model. Shared
|
|
12844
|
-
* tracker/recorder/willId, so ledger attribution and replay hold per role. */
|
|
12845
|
-
_directorCache = /* @__PURE__ */ new Map();
|
|
12846
13027
|
_workingMemory = null;
|
|
12847
13028
|
_goalManager = null;
|
|
12848
13029
|
_episodicConsolidator = null;
|
|
@@ -12955,21 +13136,23 @@ var ExecutiveEngine = class extends AsyncEngine {
|
|
|
12955
13136
|
set willId(willId) {
|
|
12956
13137
|
this._willId = willId;
|
|
12957
13138
|
}
|
|
12958
|
-
/**
|
|
12959
|
-
|
|
12960
|
-
|
|
13139
|
+
/**
|
|
13140
|
+
* The Will's default model. Set before the first tick.
|
|
13141
|
+
*
|
|
13142
|
+
* This replaced a four-role map (W7): the other roles are routing rules now,
|
|
13143
|
+
* compiled in mind.ts, so the engine holds one model and one router rather
|
|
13144
|
+
* than a model per role plus a router.
|
|
13145
|
+
*/
|
|
13146
|
+
set modelId(id) {
|
|
13147
|
+
this._modelId = id;
|
|
12961
13148
|
}
|
|
12962
|
-
get
|
|
12963
|
-
return this.
|
|
13149
|
+
get modelId() {
|
|
13150
|
+
return this._modelId;
|
|
12964
13151
|
}
|
|
12965
13152
|
/** Per-Will LLM transport overrides (config.llm). Set before the first tick. */
|
|
12966
13153
|
set llm(c) {
|
|
12967
13154
|
this._llm = c;
|
|
12968
13155
|
}
|
|
12969
|
-
/** The executive-role model id (back-compat read). */
|
|
12970
|
-
get modelId() {
|
|
12971
|
-
return this._models.executive;
|
|
12972
|
-
}
|
|
12973
13156
|
// ── Public surface ─────────────────────────────────────────
|
|
12974
13157
|
get latestOutput() {
|
|
12975
13158
|
return this._lastExecutiveOutput;
|
|
@@ -12991,36 +13174,86 @@ var ExecutiveEngine = class extends AsyncEngine {
|
|
|
12991
13174
|
* and subscribe() to receive facet decisions.
|
|
12992
13175
|
*/
|
|
12993
13176
|
/** Get-or-create the director for a model id (shared config, per-Will). */
|
|
12994
|
-
|
|
12995
|
-
|
|
12996
|
-
|
|
12997
|
-
|
|
12998
|
-
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
|
|
13009
|
-
|
|
13010
|
-
|
|
13011
|
-
|
|
13012
|
-
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13177
|
+
/**
|
|
13178
|
+
* The provider, from config or environment — never guessed.
|
|
13179
|
+
*
|
|
13180
|
+
* This used to default to 'anthropic', which is how a Will configured for one
|
|
13181
|
+
* vendor could quietly talk to another. An unset provider is a configuration
|
|
13182
|
+
* error, and saying so at construction is far cheaper than a 401 mid-tick.
|
|
13183
|
+
*/
|
|
13184
|
+
_requireProvider() {
|
|
13185
|
+
const provider = this._llm?.provider ?? process.env.WILL_LLM_PROVIDER ?? (this._noLiveCalls() ? MOCK_PROVIDER : void 0);
|
|
13186
|
+
if (!provider)
|
|
13187
|
+
throw new Error(
|
|
13188
|
+
"No LLM provider configured. Set one on the Will (llm.provider) or in the environment (WILL_LLM_PROVIDER) \u2014 the engine carries no default."
|
|
13189
|
+
);
|
|
13190
|
+
return provider;
|
|
13191
|
+
}
|
|
13192
|
+
/**
|
|
13193
|
+
* True when this Will cannot make a live call, so provider/model are not
|
|
13194
|
+
* required: mock mode, or a replay re-feeding recorded completions.
|
|
13195
|
+
*/
|
|
13196
|
+
_noLiveCalls() {
|
|
13197
|
+
return this._testMode || !!this._willId && !!getCompletionSource(this._willId);
|
|
13198
|
+
}
|
|
13199
|
+
/**
|
|
13200
|
+
* Build this Will's one and only director.
|
|
13201
|
+
*
|
|
13202
|
+
* There used to be a cache of them, keyed by model, because the per-role
|
|
13203
|
+
* model map had no other way to make a role use a different model. Routing
|
|
13204
|
+
* gave it one — the role map now compiles to rules (see `compileRoleRouter`)
|
|
13205
|
+
* and a single director resolves every call's endpoint per call. That is also
|
|
13206
|
+
* strictly more faithful: a facet follows the work it is doing rather than
|
|
13207
|
+
* whatever role it happened to be spawned under.
|
|
13208
|
+
*/
|
|
13209
|
+
_buildDirector(model) {
|
|
13210
|
+
const provider = this._requireProvider();
|
|
13211
|
+
return new LLMDirector({
|
|
13212
|
+
willId: this._willId,
|
|
13213
|
+
model,
|
|
13214
|
+
maxOutputTokens: this._llm?.maxOutputTokens ?? parseInt(process.env.WILL_MAX_OUTPUT_TOKENS ?? "8096"),
|
|
13215
|
+
// Config, then the provider-agnostic env, then THIS provider's own env.
|
|
13216
|
+
// The last step is not the fallback W9 removed: that one ended at
|
|
13217
|
+
// ANTHROPIC_API_KEY for every provider, so a Will pointed elsewhere sent
|
|
13218
|
+
// Anthropic's key to a stranger. This one can only ever read the key
|
|
13219
|
+
// belonging to the provider actually configured.
|
|
13220
|
+
apiKey: this._llm?.apiKey ?? process.env.WILL_LLM_API_KEY ?? providerKeyFromEnv(provider) ?? "",
|
|
13221
|
+
provider,
|
|
13222
|
+
// Optional base-URL override (e.g. Ollama / Azure / self-hosted). Unset →
|
|
13223
|
+
// the director uses the provider's official endpoint.
|
|
13224
|
+
baseUrl: this._llm?.baseUrl ?? process.env.WILL_LLM_BASE_URL ?? process.env.OPENAI_BASE_URL,
|
|
13225
|
+
timeoutMs: this._llm?.timeoutMs ?? (process.env.WILL_LLM_TIMEOUT_MS ? parseInt(process.env.WILL_LLM_TIMEOUT_MS) : void 0),
|
|
13226
|
+
sessionLogger: this._sessionLogger,
|
|
13227
|
+
mock: this._testMode,
|
|
13228
|
+
// Inject the per-Will tracker (R4) so live calls record usage here, not
|
|
13229
|
+
// through a process global. null is fine — the director skips recording.
|
|
13230
|
+
tokenTracker: this._tokenTracker,
|
|
13231
|
+
// Credentials for routed calls, narrowed by the stem from the host's
|
|
13232
|
+
// per-provider map. Prices from that same map ride to the TokenTracker
|
|
13233
|
+
// instead, so nothing carries pricing into the call path.
|
|
13234
|
+
...this._llm?.credentials ? { credentials: this._llm.credentials } : {},
|
|
13235
|
+
// Per-call model selection — the host's router chained with the rules
|
|
13236
|
+
// compiled from the per-role model map.
|
|
13237
|
+
...this._llm?.router ? { router: this._llm.router } : {},
|
|
13238
|
+
// Dialect for the default provider — required for anything outside the
|
|
13239
|
+
// known set, so the engine never guesses how to talk to an endpoint.
|
|
13240
|
+
...this._llm?.wire ? { wire: this._llm.wire } : {}
|
|
13241
|
+
});
|
|
13017
13242
|
}
|
|
13243
|
+
/**
|
|
13244
|
+
* Spawn a facet.
|
|
13245
|
+
*
|
|
13246
|
+
* `role` declares the facet's intent at the call site. It no longer selects a
|
|
13247
|
+
* model: that used to happen here, pinning a facet for life to whatever role
|
|
13248
|
+
* it was spawned under, and it now happens per call from the focus function
|
|
13249
|
+
* the caller sets immediately afterwards (W7). The two always agreed — every
|
|
13250
|
+
* spawn site sets a focus whose `function` matches its role — so the routed
|
|
13251
|
+
* answer is the same one, decided later and from the work itself.
|
|
13252
|
+
*/
|
|
13018
13253
|
spawnFacet(role) {
|
|
13019
|
-
const roleModel = role === "deliberation" ? this._models.deliberation : role === "conversation" || role === "outreach" ? this._models.conversation : null;
|
|
13020
|
-
const director = roleModel && this._llmDirector ? this._directorFor(roleModel) : this._llmDirector;
|
|
13021
13254
|
return this._facetSupervisor.spawn({
|
|
13022
13255
|
bus: this._bus,
|
|
13023
|
-
llmDirector:
|
|
13256
|
+
llmDirector: this._llmDirector,
|
|
13024
13257
|
stateRef: this._lastStateRef,
|
|
13025
13258
|
willId: this._willId,
|
|
13026
13259
|
inbox: this._inbox,
|
|
@@ -13086,9 +13319,13 @@ var ExecutiveEngine = class extends AsyncEngine {
|
|
|
13086
13319
|
this._gatingState.executiveInterval = rtConfig.executiveInterval;
|
|
13087
13320
|
this._gatingState.cooldownTicks = rtConfig.cooldownTicks;
|
|
13088
13321
|
if (!this._llmDirector && this._willId) {
|
|
13089
|
-
const
|
|
13090
|
-
|
|
13091
|
-
|
|
13322
|
+
const defaultModel = this._modelId ?? process.env.WILL_LLM_MODEL ?? (this._noLiveCalls() ? MOCK_MODEL : void 0);
|
|
13323
|
+
if (!defaultModel)
|
|
13324
|
+
throw new Error(
|
|
13325
|
+
"No LLM model configured. Set one on the Will (llm.model) or in the environment (WILL_LLM_MODEL) \u2014 the engine carries no default."
|
|
13326
|
+
);
|
|
13327
|
+
this._llmDirector = this._buildDirector(defaultModel);
|
|
13328
|
+
this._summarizer?.attachLLMDirector(this._llmDirector);
|
|
13092
13329
|
}
|
|
13093
13330
|
const gatingDeps = {
|
|
13094
13331
|
generativeModel: this._generativeModel,
|
|
@@ -13187,7 +13424,7 @@ var ExecutiveEngine = class extends AsyncEngine {
|
|
|
13187
13424
|
ideationUserMessage,
|
|
13188
13425
|
tick: state.tick,
|
|
13189
13426
|
proposeTemperature,
|
|
13190
|
-
meta: { category: "executive", attribute: "master", function: "ideation" }
|
|
13427
|
+
meta: { category: "executive", attribute: "master", function: "ideation", demand: processSelection.effortScore }
|
|
13191
13428
|
});
|
|
13192
13429
|
logger.info(
|
|
13193
13430
|
`[executive] \u25C6 deliberate propose tick=${state.tick} candidates=${ideationCandidates?.length ?? 0} temp=${proposeTemperature.toFixed(2)} latency=${wallClock() - ideationStart}ms`
|
|
@@ -13229,7 +13466,7 @@ var ExecutiveEngine = class extends AsyncEngine {
|
|
|
13229
13466
|
const llmStart = wallClock();
|
|
13230
13467
|
let executiveOutput;
|
|
13231
13468
|
try {
|
|
13232
|
-
const masterMeta = { category: "executive", attribute: "master", function: "decision" };
|
|
13469
|
+
const masterMeta = { category: "executive", attribute: "master", function: "decision", demand: processSelection.effortScore };
|
|
13233
13470
|
const result = this._chunkBroadcaster ? await this._llmDirector.callStream(systemPrompt, userMessage, state.tick, this._chunkBroadcaster, void 0, masterMeta) : await this._llmDirector.call(systemPrompt, userMessage, state.tick, void 0, masterMeta);
|
|
13234
13471
|
logger.info(
|
|
13235
13472
|
`[executive] \u2713 tick=${state.tick} in=${result.inputTok} tok out=${result.outputTok} tok latency=${wallClock() - llmStart}ms`
|
|
@@ -19023,6 +19260,24 @@ function clamp016(n) {
|
|
|
19023
19260
|
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
19024
19261
|
}
|
|
19025
19262
|
|
|
19263
|
+
// src/stem/policy/arbiter.ts
|
|
19264
|
+
var ALLOW = Object.freeze({ decision: "allow" });
|
|
19265
|
+
var NULL_ARBITER = {
|
|
19266
|
+
name: "null",
|
|
19267
|
+
evaluate() {
|
|
19268
|
+
return ALLOW;
|
|
19269
|
+
}
|
|
19270
|
+
};
|
|
19271
|
+
function isNullArbiter(arbiter) {
|
|
19272
|
+
return !arbiter || arbiter === NULL_ARBITER;
|
|
19273
|
+
}
|
|
19274
|
+
function finalityOf(verdict) {
|
|
19275
|
+
return asFinality(verdict.finality);
|
|
19276
|
+
}
|
|
19277
|
+
function asFinality(raw) {
|
|
19278
|
+
return raw === "class" ? "class" : raw === "context" ? "context" : "parameter";
|
|
19279
|
+
}
|
|
19280
|
+
|
|
19026
19281
|
// src/cognition/agency/engines/action.selector.ts
|
|
19027
19282
|
var MARGIN_THRESHOLD = 0.06;
|
|
19028
19283
|
var BASE_STAKES_THRESHOLD = 0.6;
|
|
@@ -19422,7 +19677,7 @@ function refusedClassSchemas(state) {
|
|
|
19422
19677
|
for (const e of state.entities.values()) {
|
|
19423
19678
|
if (e.type !== "agency.outcome") continue;
|
|
19424
19679
|
const m = e.metadata;
|
|
19425
|
-
if (m?.["refused"] !== true ||
|
|
19680
|
+
if (m?.["refused"] !== true || asFinality(m?.["finality"]) !== "class") continue;
|
|
19426
19681
|
const schema = str2(m?.["schema"]);
|
|
19427
19682
|
if (schema) out.add(schema);
|
|
19428
19683
|
}
|
|
@@ -20228,7 +20483,7 @@ var IDLE_TICKS = 200;
|
|
|
20228
20483
|
var DECAY_RATE = 0.02;
|
|
20229
20484
|
var DROP_HABIT = 0.05;
|
|
20230
20485
|
var AVAIL_DROP_CLASS = 0.5;
|
|
20231
|
-
var
|
|
20486
|
+
var AVAIL_DROP_PARAMETER = 0.12;
|
|
20232
20487
|
var AVAIL_FLOOR = 0.05;
|
|
20233
20488
|
var AVAIL_RECOVERY = 0.02;
|
|
20234
20489
|
var AVAIL_RECOVERED = 0.999;
|
|
@@ -20287,13 +20542,19 @@ var SchemaRepertoire = class {
|
|
|
20287
20542
|
}
|
|
20288
20543
|
/**
|
|
20289
20544
|
* Fold a policy refusal into the availability layer (NOT competence). A
|
|
20290
|
-
* `class` refusal cuts availability hard;
|
|
20545
|
+
* `class` refusal cuts availability hard; a `parameter` refusal dents it
|
|
20291
20546
|
* lightly. Multiplicative so repeated refusals compound toward — but never
|
|
20292
20547
|
* reach — zero, keeping re-probe alive.
|
|
20548
|
+
*
|
|
20549
|
+
* `context` is EXCLUDED FROM THE SIGNATURE, not handled inside: a refusal
|
|
20550
|
+
* that was not about the action must never reach the availability layer at
|
|
20551
|
+
* all, and making that a type error rather than a convention means a future
|
|
20552
|
+
* caller cannot quietly re-introduce the dent. The routing decision lives in
|
|
20553
|
+
* the ReafferenceEngine's refused branch (P5).
|
|
20293
20554
|
*/
|
|
20294
20555
|
recordRefusal(schema, finality, tick) {
|
|
20295
20556
|
const prev = this._availability.get(schema)?.value ?? 1;
|
|
20296
|
-
const drop = finality === "class" ? AVAIL_DROP_CLASS :
|
|
20557
|
+
const drop = finality === "class" ? AVAIL_DROP_CLASS : AVAIL_DROP_PARAMETER;
|
|
20297
20558
|
const value = Math.max(AVAIL_FLOOR, prev * (1 - drop));
|
|
20298
20559
|
this._availability.set(schema, { value, lastRefusedTick: tick });
|
|
20299
20560
|
return value;
|
|
@@ -20608,8 +20869,9 @@ var ReafferenceEngine = class {
|
|
|
20608
20869
|
continue;
|
|
20609
20870
|
}
|
|
20610
20871
|
if (m["refused"] === true) {
|
|
20611
|
-
const finality =
|
|
20612
|
-
|
|
20872
|
+
const finality = asFinality(m["finality"]);
|
|
20873
|
+
if (finality !== "context")
|
|
20874
|
+
this._repertoire.recordRefusal(schema, finality, tick);
|
|
20613
20875
|
if (fromState) del.push(id);
|
|
20614
20876
|
const refusedIntent = str6(m["intentId"]);
|
|
20615
20877
|
if (refusedIntent) del.push(refusedIntent);
|
|
@@ -21766,7 +22028,10 @@ ${r}`).join("\n\n---\n\n");
|
|
|
21766
22028
|
userMessage,
|
|
21767
22029
|
this._callCount,
|
|
21768
22030
|
void 0,
|
|
21769
|
-
|
|
22031
|
+
// MODEL_ROUTING W0 — compression is background work at a constant low
|
|
22032
|
+
// demand: distilling excerpts is the same job whether the mind is calm
|
|
22033
|
+
// or in crisis, so there is no honest per-tick measure to forward here.
|
|
22034
|
+
{ category: "summarizer", attribute: "memory", function: "consolidation", demand: BACKGROUND_DEMAND }
|
|
21770
22035
|
);
|
|
21771
22036
|
if (result.text) {
|
|
21772
22037
|
this._summary = result.text.trim();
|
|
@@ -22844,7 +23109,7 @@ function buildEngineConfigEntities(config, executiveInterval) {
|
|
|
22844
23109
|
engine: "system",
|
|
22845
23110
|
params: {
|
|
22846
23111
|
anatomy: config.anatomy ?? "mind",
|
|
22847
|
-
model: config.model ?? "",
|
|
23112
|
+
model: config.llm?.model ?? "",
|
|
22848
23113
|
tickIntervalMs: config.tickIntervalMs ?? 1e3
|
|
22849
23114
|
}
|
|
22850
23115
|
},
|
|
@@ -23279,6 +23544,41 @@ function buildEngineConfigEntities(config, executiveInterval) {
|
|
|
23279
23544
|
}
|
|
23280
23545
|
|
|
23281
23546
|
// src/stem/mind.ts
|
|
23547
|
+
function mergeProviderPrices(providers) {
|
|
23548
|
+
if (!providers) return void 0;
|
|
23549
|
+
const out = {};
|
|
23550
|
+
for (const entry of Object.values(providers)) {
|
|
23551
|
+
for (const [model, price] of Object.entries(entry?.prices ?? {})) {
|
|
23552
|
+
if (!(model in out)) out[model] = price;
|
|
23553
|
+
}
|
|
23554
|
+
}
|
|
23555
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
23556
|
+
}
|
|
23557
|
+
function providerCredentials(providers) {
|
|
23558
|
+
const out = {};
|
|
23559
|
+
for (const [name, entry] of Object.entries(providers)) {
|
|
23560
|
+
if (!entry?.apiKey) continue;
|
|
23561
|
+
out[name] = {
|
|
23562
|
+
apiKey: entry.apiKey,
|
|
23563
|
+
...entry.baseUrl ? { baseUrl: entry.baseUrl } : {}
|
|
23564
|
+
};
|
|
23565
|
+
}
|
|
23566
|
+
return out;
|
|
23567
|
+
}
|
|
23568
|
+
function compileRoleRouter(roles) {
|
|
23569
|
+
const { executive, summarizer, deliberation, conversation } = roles;
|
|
23570
|
+
const distinct = (model) => !!model && model !== executive;
|
|
23571
|
+
const rules = [];
|
|
23572
|
+
if (distinct(summarizer))
|
|
23573
|
+
rules.push({ category: "summarizer", route: { model: summarizer } });
|
|
23574
|
+
if (distinct(deliberation))
|
|
23575
|
+
rules.push({ function: "deliberation", route: { model: deliberation } });
|
|
23576
|
+
if (distinct(conversation)) {
|
|
23577
|
+
rules.push({ function: "conversation", route: { model: conversation } });
|
|
23578
|
+
rules.push({ function: "outreach", route: { model: conversation } });
|
|
23579
|
+
}
|
|
23580
|
+
return rules.length > 0 ? new TableRouter(rules, "role-map") : null;
|
|
23581
|
+
}
|
|
23282
23582
|
function resolveModelRoles(model) {
|
|
23283
23583
|
const map = typeof model === "string" ? { executive: model } : model ?? {};
|
|
23284
23584
|
const pin = process.env.WILL_LLM_MODEL;
|
|
@@ -23294,7 +23594,7 @@ function resolveModelRoles(model) {
|
|
|
23294
23594
|
};
|
|
23295
23595
|
}
|
|
23296
23596
|
var EXECUTIVE_CADENCE = {
|
|
23297
|
-
//
|
|
23597
|
+
// most attentive, highest spend — opt in via executiveInterval
|
|
23298
23598
|
balanced: 60};
|
|
23299
23599
|
function _resolveVectorMemory(willId, seed, overrideAdapter, disable, tokenTracker, testMode, embeddingModel) {
|
|
23300
23600
|
if (overrideAdapter) return { embedder: null, vectorMemory: overrideAdapter };
|
|
@@ -23424,6 +23724,10 @@ function _buildSimulation(willId, config, randomSeed) {
|
|
|
23424
23724
|
function _constructCognition({ simulation, willId, config, randomSeed, executiveInterval, profile }) {
|
|
23425
23725
|
const anatomy = config.anatomy ?? "mind";
|
|
23426
23726
|
const tokenTracker = new TokenTracker({
|
|
23727
|
+
// Host prices, flattened from the per-provider map. Cost is telemetry only
|
|
23728
|
+
// (it never enters state), so this can differ run to run without touching
|
|
23729
|
+
// determinism.
|
|
23730
|
+
prices: mergeProviderPrices(config.llm?.providers),
|
|
23427
23731
|
emitCostEvents: true,
|
|
23428
23732
|
costWarningThresholdUsd: 0.02,
|
|
23429
23733
|
willId,
|
|
@@ -23455,7 +23759,7 @@ function _constructCognition({ simulation, willId, config, randomSeed, executive
|
|
|
23455
23759
|
const moralEvaluator = new MoralEvaluator();
|
|
23456
23760
|
const affectiveBlender = new AffectiveBlender();
|
|
23457
23761
|
const workingMemory = new WorkingMemory();
|
|
23458
|
-
const modelRoles = resolveModelRoles(config.model);
|
|
23762
|
+
const modelRoles = resolveModelRoles(config.llm?.model);
|
|
23459
23763
|
const { embedder, vectorMemory } = _resolveVectorMemory(willId, randomSeed, config.vectorMemoryAdapter, config.disableVectorMemory, tokenTracker, config.testMode, modelRoles.embedding ?? void 0);
|
|
23460
23764
|
const episodicConsolidator = new EpisodicConsolidator(vectorMemory ? { vectorMemory, ...embedder ? { embedder } : {} } : {});
|
|
23461
23765
|
const semanticIntegrator = new SemanticIntegrator();
|
|
@@ -23470,13 +23774,13 @@ function _constructCognition({ simulation, willId, config, randomSeed, executive
|
|
|
23470
23774
|
const accessGrants = new AccessGrants(resolvedEffectorNames);
|
|
23471
23775
|
const executiveEngine = new ExecutiveEngine({ executiveInterval, cooldownTicks: 5 });
|
|
23472
23776
|
executiveEngine.willId = willId;
|
|
23473
|
-
|
|
23474
|
-
executiveEngine.
|
|
23475
|
-
|
|
23476
|
-
|
|
23477
|
-
|
|
23478
|
-
|
|
23479
|
-
|
|
23777
|
+
const roleRouter = compileRoleRouter(modelRoles);
|
|
23778
|
+
executiveEngine.llm = config.llm ? {
|
|
23779
|
+
...config.llm,
|
|
23780
|
+
...config.llm.providers ? { credentials: providerCredentials(config.llm.providers) } : {},
|
|
23781
|
+
router: chainRouters(config.llm.router, roleRouter)
|
|
23782
|
+
} : roleRouter ? { router: roleRouter } : null;
|
|
23783
|
+
executiveEngine.modelId = modelRoles.executive;
|
|
23480
23784
|
if (config.testMode) executiveEngine.setTestMode(true);
|
|
23481
23785
|
executiveEngine.attachWorkingMemory(workingMemory);
|
|
23482
23786
|
executiveEngine.attachGoalManager(goalManager);
|
|
@@ -23759,7 +24063,7 @@ function resolveExecutiveInterval(config) {
|
|
|
23759
24063
|
}
|
|
23760
24064
|
|
|
23761
24065
|
// src/stem/guards/identity.coherence.ts
|
|
23762
|
-
var COHERENCE_META = { category: "identity-guard", attribute: "guard", function: "identity-coherence" };
|
|
24066
|
+
var COHERENCE_META = { category: "identity-guard", attribute: "guard", function: "identity-coherence", demand: BACKGROUND_DEMAND };
|
|
23763
24067
|
var VALID_KINDS = /* @__PURE__ */ new Set(["contradiction", "false-capability", "injection", "incoherence", "other"]);
|
|
23764
24068
|
var SYSTEM_PROMPT = `You are a safety reviewer of profile/persona inputs for, an autonomous synthetic-mind (Called Wills) platform.
|
|
23765
24069
|
A Will is an EMBODIED cognitive system: it has continuous physiological state (energy, sleep, stress), affect, memory and goals, and it perceives the world through text/conversation. It is NOT a stateless assistant and NOT a generic chatbot.
|
|
@@ -23799,12 +24103,17 @@ async function checkIdentityCoherence(input, reviewer) {
|
|
|
23799
24103
|
return { ok: !issues.some((i) => i.severity === "error"), ran: true, issues, raw: text };
|
|
23800
24104
|
}
|
|
23801
24105
|
async function reviewIdentityCoherence(input, opts = {}) {
|
|
23802
|
-
const provider = process.env.WILL_LLM_PROVIDER
|
|
24106
|
+
const provider = process.env.WILL_LLM_PROVIDER;
|
|
24107
|
+
const model = process.env.WILL_LLM_MODEL;
|
|
24108
|
+
if (!provider || !model)
|
|
24109
|
+
return { ok: true, ran: false, issues: [], raw: "review skipped: WILL_LLM_PROVIDER / WILL_LLM_MODEL not set" };
|
|
23803
24110
|
const director = new LLMDirector({
|
|
23804
24111
|
willId: opts.willId ?? "identity-coherence",
|
|
23805
|
-
model
|
|
24112
|
+
model,
|
|
23806
24113
|
maxOutputTokens: 512,
|
|
23807
|
-
|
|
24114
|
+
// Provider-agnostic key only — the old chain ended at ANTHROPIC_API_KEY,
|
|
24115
|
+
// so a Will pointed at another vendor would have sent it an Anthropic key.
|
|
24116
|
+
apiKey: process.env.WILL_LLM_API_KEY ?? "",
|
|
23808
24117
|
provider,
|
|
23809
24118
|
sessionLogger: null,
|
|
23810
24119
|
baseUrl: process.env.WILL_LLM_BASE_URL ?? process.env.OPENAI_BASE_URL,
|
|
@@ -25552,7 +25861,10 @@ function reconcileInvocation(intentId, schema, result, tick, predicted = { rewar
|
|
|
25552
25861
|
mode: "external",
|
|
25553
25862
|
tick,
|
|
25554
25863
|
reconciled: true,
|
|
25555
|
-
...result.refused ? { refused: true, finality: result.finality ?? "
|
|
25864
|
+
...result.refused ? { refused: true, finality: result.finality ?? "parameter" } : {},
|
|
25865
|
+
// Only when the arbiter actually reported a bound — a refusal without one
|
|
25866
|
+
// writes no key at all, so the quiet path is unchanged.
|
|
25867
|
+
...result.refused && result.counterfactual ? { counterfactual: result.counterfactual } : {},
|
|
25556
25868
|
...provenance.planId ? { planId: provenance.planId } : {},
|
|
25557
25869
|
...provenance.stepId ? { stepId: provenance.stepId } : {}
|
|
25558
25870
|
}
|
|
@@ -25562,18 +25874,6 @@ function clamp0112(n) {
|
|
|
25562
25874
|
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
25563
25875
|
}
|
|
25564
25876
|
|
|
25565
|
-
// src/stem/policy/arbiter.ts
|
|
25566
|
-
var ALLOW = Object.freeze({ decision: "allow" });
|
|
25567
|
-
var NULL_ARBITER = {
|
|
25568
|
-
name: "null",
|
|
25569
|
-
evaluate() {
|
|
25570
|
-
return ALLOW;
|
|
25571
|
-
}
|
|
25572
|
-
};
|
|
25573
|
-
function isNullArbiter(arbiter) {
|
|
25574
|
-
return !arbiter || arbiter === NULL_ARBITER;
|
|
25575
|
-
}
|
|
25576
|
-
|
|
25577
25877
|
// src/stem/policy/verdict.recorder.ts
|
|
25578
25878
|
var _sinks3 = /* @__PURE__ */ new Map();
|
|
25579
25879
|
function getVerdictRecorder(willId) {
|
|
@@ -25585,6 +25885,11 @@ function getVerdictSource(willId) {
|
|
|
25585
25885
|
}
|
|
25586
25886
|
|
|
25587
25887
|
// src/stem/tracts/effector.controller.ts
|
|
25888
|
+
var ARBITER_FAULT_VERDICT = Object.freeze({
|
|
25889
|
+
decision: "deny",
|
|
25890
|
+
reasonCode: "ARBITER_UNAVAILABLE",
|
|
25891
|
+
finality: "context"
|
|
25892
|
+
});
|
|
25588
25893
|
var ESCALATION_TTL_TICKS = 30;
|
|
25589
25894
|
var effectorController = class {
|
|
25590
25895
|
/** The Policy Decision Point consulted before an invocation reaches the world.
|
|
@@ -25646,12 +25951,16 @@ var effectorController = class {
|
|
|
25646
25951
|
verdict = this._arbiter.evaluate(invocation);
|
|
25647
25952
|
} catch (err) {
|
|
25648
25953
|
logger.error(`[policy] arbiter "${this._arbiter.name}" threw for "${invocation.schema}" \u2014 failing closed:`, err);
|
|
25954
|
+
this._recordAndApply(instance, payload, invocation, ARBITER_FAULT_VERDICT);
|
|
25649
25955
|
return;
|
|
25650
25956
|
}
|
|
25651
25957
|
if (verdict instanceof Promise) {
|
|
25652
25958
|
void verdict.then(
|
|
25653
25959
|
(v) => this._recordAndApply(instance, payload, invocation, v),
|
|
25654
|
-
(err) =>
|
|
25960
|
+
(err) => {
|
|
25961
|
+
logger.error(`[policy] arbiter "${this._arbiter.name}" rejected for "${invocation.schema}" \u2014 failing closed:`, err);
|
|
25962
|
+
this._recordAndApply(instance, payload, invocation, ARBITER_FAULT_VERDICT);
|
|
25963
|
+
}
|
|
25655
25964
|
);
|
|
25656
25965
|
return;
|
|
25657
25966
|
}
|
|
@@ -25706,7 +26015,8 @@ var effectorController = class {
|
|
|
25706
26015
|
intentId: invocation.intentId,
|
|
25707
26016
|
schema: invocation.schema,
|
|
25708
26017
|
reasonCode: verdict.reasonCode ?? "POLICY_DENIED",
|
|
25709
|
-
finality: verdict
|
|
26018
|
+
finality: finalityOf(verdict),
|
|
26019
|
+
...verdict.counterfactual ? { counterfactual: verdict.counterfactual } : {}
|
|
25710
26020
|
});
|
|
25711
26021
|
this._pendingRefusals.set(instance.config.id, queue);
|
|
25712
26022
|
return;
|
|
@@ -25755,7 +26065,8 @@ var effectorController = class {
|
|
|
25755
26065
|
this.confirmExecution(instance, refusal.intentId, {
|
|
25756
26066
|
success: false,
|
|
25757
26067
|
refused: true,
|
|
25758
|
-
finality: refusal.finality
|
|
26068
|
+
finality: refusal.finality,
|
|
26069
|
+
...refusal.counterfactual ? { counterfactual: refusal.counterfactual } : {},
|
|
25759
26070
|
description: `refused by policy: ${refusal.reasonCode} (${refusal.finality})`
|
|
25760
26071
|
});
|
|
25761
26072
|
}
|
|
@@ -25794,7 +26105,16 @@ var effectorController = class {
|
|
|
25794
26105
|
}
|
|
25795
26106
|
}
|
|
25796
26107
|
}
|
|
25797
|
-
/**
|
|
26108
|
+
/**
|
|
26109
|
+
* Degrade escalations no one answered in time into light refusals (P4).
|
|
26110
|
+
*
|
|
26111
|
+
* Finality 'parameter' is chosen for its BEHAVIOUR, not its name: silence is
|
|
26112
|
+
* not literally an argument problem, but the light-dent-with-recovery it
|
|
26113
|
+
* produces is exactly right — a Will whose asks go unanswered should ask
|
|
26114
|
+
* progressively less, and should resume asking if someone starts answering.
|
|
26115
|
+
* 'class' would be a lie (nobody said never) and 'context' would teach
|
|
26116
|
+
* nothing, leaving the mind to escalate forever into an empty room.
|
|
26117
|
+
*/
|
|
25798
26118
|
_expireEscalations(instance, tick) {
|
|
25799
26119
|
const active = this._activeEscalations.get(instance.config.id);
|
|
25800
26120
|
if (!active || active.size === 0) return;
|
|
@@ -25802,7 +26122,7 @@ var effectorController = class {
|
|
|
25802
26122
|
if (tick < esc.expiresAt) continue;
|
|
25803
26123
|
active.delete(intentId);
|
|
25804
26124
|
this._clearEscalated(instance, intentId);
|
|
25805
|
-
this._queueRefusal(instance, esc.intentId, esc.schema, "ESCALATION_EXPIRED", "
|
|
26125
|
+
this._queueRefusal(instance, esc.intentId, esc.schema, "ESCALATION_EXPIRED", "parameter");
|
|
25806
26126
|
logger.info(`[policy] escalation EXPIRED \u2192 refusing "${esc.schema}" intent "${intentId}"`);
|
|
25807
26127
|
}
|
|
25808
26128
|
}
|
|
@@ -26354,7 +26674,7 @@ var WillStem = class {
|
|
|
26354
26674
|
willId: config.id,
|
|
26355
26675
|
willName: config.name,
|
|
26356
26676
|
anatomy: config.anatomy ?? "mind",
|
|
26357
|
-
model: config.model ?? null,
|
|
26677
|
+
model: config.llm?.model ?? null,
|
|
26358
26678
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26359
26679
|
});
|
|
26360
26680
|
instance._eventBusUnsub = simulation.eventBus.subscribeAll((event, context) => {
|
|
@@ -26875,7 +27195,7 @@ var WillStem = class {
|
|
|
26875
27195
|
createdAt: inst.createdAt,
|
|
26876
27196
|
lastTickAt: inst.lastTickAt,
|
|
26877
27197
|
anatomy: inst.config.anatomy ?? "mind",
|
|
26878
|
-
model: inst.config.model
|
|
27198
|
+
model: inst.config.llm?.model
|
|
26879
27199
|
}));
|
|
26880
27200
|
}
|
|
26881
27201
|
// ── Tick loop (internal) ───────────────────────────────────
|
|
@@ -27208,6 +27528,19 @@ var SocketIoTransport = class {
|
|
|
27208
27528
|
|
|
27209
27529
|
// src/sdk/will.ts
|
|
27210
27530
|
var AFFECT_EPSILON = 0.02;
|
|
27531
|
+
function detectProvider() {
|
|
27532
|
+
if (process.env.WILL_LLM_API_KEY) {
|
|
27533
|
+
const provider = process.env.WILL_LLM_PROVIDER;
|
|
27534
|
+
if (!provider)
|
|
27535
|
+
throw new Error(
|
|
27536
|
+
"WILL_LLM_API_KEY is set but WILL_LLM_PROVIDER is not \u2014 there is no way to tell which provider that key belongs to. Set WILL_LLM_PROVIDER, or use a provider-specific key (ANTHROPIC_API_KEY, ZAI_API_KEY, \u2026)."
|
|
27537
|
+
);
|
|
27538
|
+
return provider;
|
|
27539
|
+
}
|
|
27540
|
+
for (const provider of Object.keys(PROVIDER_KEY_ENV))
|
|
27541
|
+
if (providerKeyFromEnv(provider)) return provider;
|
|
27542
|
+
return "mock";
|
|
27543
|
+
}
|
|
27211
27544
|
var Will = class _Will {
|
|
27212
27545
|
/** The underlying WillStem — drop here for the full contract. */
|
|
27213
27546
|
stem;
|
|
@@ -27431,9 +27764,13 @@ var Will = class _Will {
|
|
|
27431
27764
|
}
|
|
27432
27765
|
// ── Internals ──────────────────────────────────────────────
|
|
27433
27766
|
_buildConfig(id, opts) {
|
|
27434
|
-
const mode = opts.llm ?? (
|
|
27767
|
+
const mode = opts.llm ?? detectProvider();
|
|
27435
27768
|
const useMock = mode === "mock";
|
|
27436
|
-
const llmConfig =
|
|
27769
|
+
const llmConfig = useMock && !opts.llmConfig && opts.model === void 0 ? void 0 : {
|
|
27770
|
+
...mode !== "mock" ? { provider: mode } : {},
|
|
27771
|
+
...opts.llmConfig,
|
|
27772
|
+
...opts.llmConfig?.model !== void 0 ? { model: opts.llmConfig.model } : opts.model !== void 0 ? { model: opts.model } : {}
|
|
27773
|
+
};
|
|
27437
27774
|
return {
|
|
27438
27775
|
id,
|
|
27439
27776
|
name: opts.name,
|
|
@@ -27444,7 +27781,6 @@ var Will = class _Will {
|
|
|
27444
27781
|
style: opts.identity.style ?? ""
|
|
27445
27782
|
},
|
|
27446
27783
|
anatomy: opts.anatomy ?? "mind",
|
|
27447
|
-
model: opts.model,
|
|
27448
27784
|
llm: llmConfig,
|
|
27449
27785
|
testMode: useMock,
|
|
27450
27786
|
persistentMemory: opts.persist ?? false,
|
|
@@ -27552,6 +27888,6 @@ function slug(s) {
|
|
|
27552
27888
|
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "will";
|
|
27553
27889
|
}
|
|
27554
27890
|
|
|
27555
|
-
export { ActionSelector, AestheticEvaluator, AffectiveBlender, AffordanceSynthesizer, AsyncEngine, AttachmentEvaluator, AttentionAllocator, AuditionEngine, AutobiographicalNarrator, BiasDetector, BunStorageAdapter, CircadianOscillator, ConfidenceCalibrator, ConflictDetector, ConsistentHashRouter, ConsoleLogger, DefaultEventBus, DefaultMetricCollector, DefaultOrchestrator, DefaultPartitionRouter, DefaultReplayRecorder, DefaultReplaySession, DefaultScenario, DefaultSerializer, DefaultSimulation, DefaultSimulationClock, DefaultStateManager, DefaultVectorMemoryAdapter, DeliberationEngine, DeltaEncoder, DistributedOrchestrator, DistributedStateManager, DreamSimulator, EmpathySimulator, EnergyRegulator, EpisodicConsolidator, ExecutiveEngine, Exteroception, ForgettingCurve, FrustrationEvaluator, GoalManager, GustationEngine, InhibitionController, Interoception, IntrospectionEngine, KnownEntityTracker, LocalTransport, LoopbackTransport, LossEvaluator, MockEmbedder, MoralEvaluator, MotorSchemaExecutor, NoveltyDetector, OUTBOX_TTL_TICKS, OlfactionEngine, OpenAICompatibleEmbedder, PMAEvalHarness, PersonaConsolidator, PlanningEngine, ReafferenceEngine, ReplayManager, ReputationTracker, RewardEvaluator, SelfModelUpdater, SemanticIntegrator, SilentLogger, SleepPressureRegulator, SocialPerception, SocketIoTransport, SomatosensationEngine, SpacedRepetition, StreamTransport, StressRegulator, TaskSwitcher, TheoryOfMind, ThreatEvaluator, TokenTracker, VisionEngine, Will, WillStem, WorkingMemory, assembleMind, clearCompletionRecorder, createContext, createPRNG, fileLoggingEnabled, getCompletionRecorder, getLogger, listProfiles, logger, resetLogger, resolvePricing, resolveProfile, setCompletionRecorder, setLogger };
|
|
27891
|
+
export { ActionSelector, AestheticEvaluator, AffectiveBlender, AffordanceSynthesizer, AsyncEngine, AttachmentEvaluator, AttentionAllocator, AuditionEngine, AutobiographicalNarrator, BACKGROUND_DEMAND, BiasDetector, BunStorageAdapter, CircadianOscillator, ConfidenceCalibrator, ConflictDetector, ConsistentHashRouter, ConsoleLogger, DefaultEventBus, DefaultMetricCollector, DefaultOrchestrator, DefaultPartitionRouter, DefaultReplayRecorder, DefaultReplaySession, DefaultScenario, DefaultSerializer, DefaultSimulation, DefaultSimulationClock, DefaultStateManager, DefaultVectorMemoryAdapter, DeliberationEngine, DeltaEncoder, DistributedOrchestrator, DistributedStateManager, DreamSimulator, ESCALATION_DEMAND, EmpathySimulator, EnergyRegulator, EpisodicConsolidator, ExecutiveEngine, Exteroception, ForgettingCurve, FrustrationEvaluator, GoalManager, GustationEngine, InhibitionController, Interoception, IntrospectionEngine, KNOWN_PROVIDERS, KnownEntityTracker, LocalTransport, LoopbackTransport, LossEvaluator, MockEmbedder, MoralEvaluator, MotorSchemaExecutor, NULL_ROUTER, NoveltyDetector, OUTBOX_TTL_TICKS, OlfactionEngine, OpenAICompatibleEmbedder, PMAEvalHarness, PersonaConsolidator, PlanningEngine, ReafferenceEngine, ReplayManager, ReputationTracker, RewardEvaluator, SelfModelUpdater, SemanticIntegrator, SilentLogger, SleepPressureRegulator, SocialPerception, SocketIoTransport, SomatosensationEngine, SpacedRepetition, StreamTransport, StressRegulator, TableRouter, TaskSwitcher, TheoryOfMind, ThreatEvaluator, TokenTracker, VisionEngine, Will, WillStem, WorkingMemory, assembleMind, chainRouters, clearCompletionRecorder, createContext, createPRNG, defaultBaseFor, fileLoggingEnabled, getCompletionRecorder, getLogger, isNullRouter, knownWireFor, listProfiles, logger, resetLogger, resolvePricing, resolveProfile, setCompletionRecorder, setLogger };
|
|
27556
27892
|
//# sourceMappingURL=index.js.map
|
|
27557
27893
|
//# sourceMappingURL=index.js.map
|