@mindot/will 0.6.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 +11104 -10312
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +972 -213
- package/dist/index.js.map +1 -1
- package/dist/mcp/effectors.d.ts +1 -1
- package/dist/{will-Bikuk4s2.d.ts → will-cS6k4uiJ.d.ts} +510 -86
- package/package.json +1 -1
- package/src/cognition/agency/engines/action.selector.ts +42 -9
- package/src/cognition/agency/engines/affordance.synthesizer.ts +9 -0
- package/src/cognition/agency/engines/motor.schema.executor.ts +4 -0
- package/src/cognition/agency/engines/reafference.engine.ts +40 -5
- package/src/cognition/agency/reconcile.learning.ts +23 -0
- package/src/cognition/agency/schemas/repertoire.ts +114 -7
- package/src/cognition/agency/selection.scoring.ts +7 -1
- package/src/cognition/agency/types.ts +9 -0
- 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 +18 -3
- package/src/stem/mind.ts +155 -24
- package/src/stem/policy/arbiter.ts +171 -0
- package/src/stem/policy/rule.table.ts +172 -0
- package/src/stem/policy/verdict.recorder.ts +0 -0
- package/src/stem/tracts/effector.controller.ts +426 -0
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
|
-
sessionLogger: this._sessionLogger,
|
|
13009
|
-
mock: this._testMode,
|
|
13010
|
-
// Inject the per-Will tracker (R4) so live calls record usage here, not
|
|
13011
|
-
// through a process global. null is fine — the director skips recording.
|
|
13012
|
-
tokenTracker: this._tokenTracker
|
|
13013
|
-
});
|
|
13014
|
-
this._directorCache.set(model, d);
|
|
13015
|
-
}
|
|
13016
|
-
return d;
|
|
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;
|
|
13017
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
|
+
});
|
|
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`
|
|
@@ -18724,7 +18961,9 @@ function risk(a, bias) {
|
|
|
18724
18961
|
return clamp015(Math.max(0, -a.expectedValence) * 0.5 + bias.threat * 0.5);
|
|
18725
18962
|
}
|
|
18726
18963
|
function scoreAffordance(a, bias, w = DEFAULT_WEIGHTS) {
|
|
18727
|
-
|
|
18964
|
+
const raw = w.goal * goalRelevance(a, bias) + w.reward * a.expectedReward + w.novelty * novelty(a) + w.drive * driveUrgency(a, bias) + w.habit * a.habitStrength + w.plan * (a.planBias ?? 0) - w.cost * a.cost - w.inhib * bias.inhibition - w.risk * risk(a, bias);
|
|
18965
|
+
const availability = a.availability ?? 1;
|
|
18966
|
+
return raw > 0 ? raw * availability : raw;
|
|
18728
18967
|
}
|
|
18729
18968
|
function stakes(winner, bias) {
|
|
18730
18969
|
return clamp015(Math.max(
|
|
@@ -18803,6 +19042,7 @@ var AffordanceSynthesizer = class {
|
|
|
18803
19042
|
// ── react ─────────────────────────────────────────────────────
|
|
18804
19043
|
async react(_delta, tick, state, _context) {
|
|
18805
19044
|
this._repertoire?.restoreComposites(state.entities);
|
|
19045
|
+
this._repertoire?.restoreAvailability(state.entities);
|
|
18806
19046
|
const schemas = this._repertoire?.schemas() ?? this._schemas;
|
|
18807
19047
|
const skills = this._skills?.() ?? this._repertoire?.skills() ?? null;
|
|
18808
19048
|
const valence = metric(state, "affect.valence", 0);
|
|
@@ -18928,6 +19168,7 @@ var AffordanceSynthesizer = class {
|
|
|
18928
19168
|
/** Compose an Affordance from a schema + the evoking context, folding in learned priors. */
|
|
18929
19169
|
_build(schema, tick, state, valence, energyLow, skills, ctx) {
|
|
18930
19170
|
const skill = skills?.get(schema.id);
|
|
19171
|
+
const availability = this._repertoire?.availabilityOf(schema.id) ?? 1;
|
|
18931
19172
|
const expectedReward = skill?.valueEstimate ?? clamp016(((schema.baseValence ?? 0) + 1) / 2);
|
|
18932
19173
|
const expectedValence = schema.baseValence ?? valence;
|
|
18933
19174
|
const habitStrength = skill?.habitStrength ?? 0;
|
|
@@ -18949,6 +19190,7 @@ var AffordanceSynthesizer = class {
|
|
|
18949
19190
|
available: this._available(schema.preconditions, (k) => metric(state, k, 0)),
|
|
18950
19191
|
tags: schema.tags ?? [],
|
|
18951
19192
|
...schema.description ? { description: schema.description } : {},
|
|
19193
|
+
...availability < 1 ? { availability } : {},
|
|
18952
19194
|
planBias: ctx.planBias,
|
|
18953
19195
|
planId: ctx.planId,
|
|
18954
19196
|
stepId: ctx.stepId,
|
|
@@ -18972,6 +19214,7 @@ var AffordanceSynthesizer = class {
|
|
|
18972
19214
|
available: a.available,
|
|
18973
19215
|
tags: a.tags,
|
|
18974
19216
|
description: a.description,
|
|
19217
|
+
...a.availability !== void 0 ? { availability: a.availability } : {},
|
|
18975
19218
|
planBias: a.planBias,
|
|
18976
19219
|
planId: a.planId,
|
|
18977
19220
|
stepId: a.stepId,
|
|
@@ -19017,6 +19260,24 @@ function clamp016(n) {
|
|
|
19017
19260
|
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
19018
19261
|
}
|
|
19019
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
|
+
|
|
19020
19281
|
// src/cognition/agency/engines/action.selector.ts
|
|
19021
19282
|
var MARGIN_THRESHOLD = 0.06;
|
|
19022
19283
|
var BASE_STAKES_THRESHOLD = 0.6;
|
|
@@ -19175,7 +19436,10 @@ var ActionSelector = class {
|
|
|
19175
19436
|
]
|
|
19176
19437
|
}
|
|
19177
19438
|
});
|
|
19178
|
-
|
|
19439
|
+
const policyRevoke = !!deliberating && refusedClassSchemas(state).has(deliberating.schema);
|
|
19440
|
+
if (deliberating && (rupture >= RUPTURE_REVOKE_GATE || policyRevoke)) {
|
|
19441
|
+
const reason = policyRevoke ? "policy-refusal" : "exafferent-rupture";
|
|
19442
|
+
const revRupture = policyRevoke ? Math.max(rupture, RUPTURE_REVOKE_GATE) : rupture;
|
|
19179
19443
|
if (this._bus) {
|
|
19180
19444
|
try {
|
|
19181
19445
|
this._bus.publish({
|
|
@@ -19183,7 +19447,7 @@ var ActionSelector = class {
|
|
|
19183
19447
|
version: 1,
|
|
19184
19448
|
sourceEngine: this.name,
|
|
19185
19449
|
salience: 0.85,
|
|
19186
|
-
payload: { from: deliberating.schema, reason
|
|
19450
|
+
payload: { from: deliberating.schema, reason, rupture: revRupture, tick }
|
|
19187
19451
|
});
|
|
19188
19452
|
} catch (err) {
|
|
19189
19453
|
logger.warn(`[selector] revoked publish failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -19192,11 +19456,12 @@ var ActionSelector = class {
|
|
|
19192
19456
|
this._lastRevoked = { schema: deliberating.schema, tick };
|
|
19193
19457
|
return {
|
|
19194
19458
|
commands: {
|
|
19195
|
-
set: [revocationEntity(deliberating.id, deliberating.schema,
|
|
19459
|
+
set: [revocationEntity(deliberating.id, deliberating.schema, revRupture, tick)],
|
|
19196
19460
|
metrics: [
|
|
19197
19461
|
["agency.field.eligible", eligible.length],
|
|
19198
19462
|
["agency.selection.busy", 1],
|
|
19199
19463
|
["agency.commitment.revoked", 1],
|
|
19464
|
+
...policyRevoke ? [["agency.policy.revoked", 1]] : [],
|
|
19200
19465
|
...stabMetrics
|
|
19201
19466
|
]
|
|
19202
19467
|
}
|
|
@@ -19407,6 +19672,17 @@ function computeRupture(state, tick, senseEvents = []) {
|
|
|
19407
19672
|
if (maxSalience <= RUPTURE_SALIENCE_GATE) return 0;
|
|
19408
19673
|
return clamp017((maxSalience - RUPTURE_SALIENCE_GATE) / (1 - RUPTURE_SALIENCE_GATE));
|
|
19409
19674
|
}
|
|
19675
|
+
function refusedClassSchemas(state) {
|
|
19676
|
+
const out = /* @__PURE__ */ new Set();
|
|
19677
|
+
for (const e of state.entities.values()) {
|
|
19678
|
+
if (e.type !== "agency.outcome") continue;
|
|
19679
|
+
const m = e.metadata;
|
|
19680
|
+
if (m?.["refused"] !== true || asFinality(m?.["finality"]) !== "class") continue;
|
|
19681
|
+
const schema = str2(m?.["schema"]);
|
|
19682
|
+
if (schema) out.add(schema);
|
|
19683
|
+
}
|
|
19684
|
+
return out;
|
|
19685
|
+
}
|
|
19410
19686
|
function effectiveWeights(state) {
|
|
19411
19687
|
const p = readEffectiveParams(state, "engine-config-action-selector");
|
|
19412
19688
|
return {
|
|
@@ -19794,6 +20070,7 @@ var MotorSchemaExecutor = class {
|
|
|
19794
20070
|
}
|
|
19795
20071
|
for (const [id, e] of state.entities) {
|
|
19796
20072
|
if (e.type !== "agency.intent" || str5(e.metadata?.["status"]) !== "awaiting") continue;
|
|
20073
|
+
if (e.metadata?.["escalated"] === true) continue;
|
|
19797
20074
|
const dispatchedAt = num3(e.metadata?.["dispatchedAt"], tick);
|
|
19798
20075
|
if (tick - dispatchedAt < AWAIT_TIMEOUT) continue;
|
|
19799
20076
|
const intent = readIntent(id, e.metadata);
|
|
@@ -20205,11 +20482,19 @@ var PROC_THRESHOLD = 0.6;
|
|
|
20205
20482
|
var IDLE_TICKS = 200;
|
|
20206
20483
|
var DECAY_RATE = 0.02;
|
|
20207
20484
|
var DROP_HABIT = 0.05;
|
|
20485
|
+
var AVAIL_DROP_CLASS = 0.5;
|
|
20486
|
+
var AVAIL_DROP_PARAMETER = 0.12;
|
|
20487
|
+
var AVAIL_FLOOR = 0.05;
|
|
20488
|
+
var AVAIL_RECOVERY = 0.02;
|
|
20489
|
+
var AVAIL_RECOVERED = 0.999;
|
|
20208
20490
|
var SchemaRepertoire = class {
|
|
20209
20491
|
_templates = /* @__PURE__ */ new Map();
|
|
20210
20492
|
_skills = /* @__PURE__ */ new Map();
|
|
20211
20493
|
/** Tracks which templates were learned at runtime (vs innate) so decay can forget them. */
|
|
20212
20494
|
_learned = /* @__PURE__ */ new Set();
|
|
20495
|
+
/** Availability layer (P2): schema → { value 0..1, lastRefusedTick }. Empty until
|
|
20496
|
+
* a refusal lands — a never-refused Will writes nothing here (byte-identical). */
|
|
20497
|
+
_availability = /* @__PURE__ */ new Map();
|
|
20213
20498
|
constructor(seed = INNATE_SCHEMAS) {
|
|
20214
20499
|
for (const s of seed) this._templates.set(s.id, s);
|
|
20215
20500
|
}
|
|
@@ -20243,6 +20528,37 @@ var SchemaRepertoire = class {
|
|
|
20243
20528
|
getSkill(id) {
|
|
20244
20529
|
return this._skills.get(id);
|
|
20245
20530
|
}
|
|
20531
|
+
// ── availability (P2) ─────────────────────────────────────────
|
|
20532
|
+
availability() {
|
|
20533
|
+
return this._availability;
|
|
20534
|
+
}
|
|
20535
|
+
/**
|
|
20536
|
+
* How available a schema is right now, 0..1. Absent from the ledger ⇒ 1
|
|
20537
|
+
* (fully available — the common case). This is the ONLY value the
|
|
20538
|
+
* AffordanceSynthesizer reads; it never touches competence.
|
|
20539
|
+
*/
|
|
20540
|
+
availabilityOf(schema) {
|
|
20541
|
+
return this._availability.get(schema)?.value ?? 1;
|
|
20542
|
+
}
|
|
20543
|
+
/**
|
|
20544
|
+
* Fold a policy refusal into the availability layer (NOT competence). A
|
|
20545
|
+
* `class` refusal cuts availability hard; a `parameter` refusal dents it
|
|
20546
|
+
* lightly. Multiplicative so repeated refusals compound toward — but never
|
|
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).
|
|
20554
|
+
*/
|
|
20555
|
+
recordRefusal(schema, finality, tick) {
|
|
20556
|
+
const prev = this._availability.get(schema)?.value ?? 1;
|
|
20557
|
+
const drop = finality === "class" ? AVAIL_DROP_CLASS : AVAIL_DROP_PARAMETER;
|
|
20558
|
+
const value = Math.max(AVAIL_FLOOR, prev * (1 - drop));
|
|
20559
|
+
this._availability.set(schema, { value, lastRefusedTick: tick });
|
|
20560
|
+
return value;
|
|
20561
|
+
}
|
|
20246
20562
|
/**
|
|
20247
20563
|
* Fold one outcome into the schema's learned skill. Returns the updated skill
|
|
20248
20564
|
* and whether it just crossed the proceduralization threshold this update.
|
|
@@ -20269,12 +20585,14 @@ var SchemaRepertoire = class {
|
|
|
20269
20585
|
return { skill, proceduralized: !wasProceduralized && habitStrength >= PROC_THRESHOLD };
|
|
20270
20586
|
}
|
|
20271
20587
|
/**
|
|
20272
|
-
* Forgetting curve over the competence layer
|
|
20273
|
-
* lose habit; learned composites
|
|
20274
|
-
* entirely (template + skill).
|
|
20588
|
+
* Forgetting curve over the competence layer, plus availability recovery.
|
|
20589
|
+
* Skills unused for IDLE_TICKS lose habit; learned composites below DROP_HABIT
|
|
20590
|
+
* are dropped entirely (template + skill). Availability entries climb back
|
|
20591
|
+
* toward 1 and are dropped once fully recovered. Returns the ids that were
|
|
20592
|
+
* removed from each layer so their mirrored state entities can be deleted.
|
|
20275
20593
|
*/
|
|
20276
20594
|
decay(tick) {
|
|
20277
|
-
const
|
|
20595
|
+
const skills = [];
|
|
20278
20596
|
for (const [id, skill] of this._skills) {
|
|
20279
20597
|
if (tick - skill.lastEnactedTick <= IDLE_TICKS) continue;
|
|
20280
20598
|
const habitStrength = clamp0110(skill.habitStrength - DECAY_RATE);
|
|
@@ -20282,12 +20600,20 @@ var SchemaRepertoire = class {
|
|
|
20282
20600
|
this._skills.delete(id);
|
|
20283
20601
|
this._templates.delete(id);
|
|
20284
20602
|
this._learned.delete(id);
|
|
20285
|
-
|
|
20603
|
+
skills.push(id);
|
|
20286
20604
|
continue;
|
|
20287
20605
|
}
|
|
20288
20606
|
this._skills.set(id, { ...skill, habitStrength });
|
|
20289
20607
|
}
|
|
20290
|
-
|
|
20608
|
+
const availability = [];
|
|
20609
|
+
for (const [id, avail] of this._availability) {
|
|
20610
|
+
const value = avail.value + AVAIL_RECOVERY * (1 - avail.value);
|
|
20611
|
+
if (value >= AVAIL_RECOVERED) {
|
|
20612
|
+
this._availability.delete(id);
|
|
20613
|
+
availability.push(id);
|
|
20614
|
+
} else this._availability.set(id, { ...avail, value });
|
|
20615
|
+
}
|
|
20616
|
+
return { skills, availability };
|
|
20291
20617
|
}
|
|
20292
20618
|
// ── PMA portability (Phase 6 reads these) ─────────────────────
|
|
20293
20619
|
/** Learned composite templates + all skills above a confidence floor. */
|
|
@@ -20336,6 +20662,28 @@ var SchemaRepertoire = class {
|
|
|
20336
20662
|
this._learned.add(s.id);
|
|
20337
20663
|
}
|
|
20338
20664
|
}
|
|
20665
|
+
/** Availability ledger encoded as `agency.availability` state entities (P2).
|
|
20666
|
+
* Empty until a refusal lands, so the quiet path writes nothing. */
|
|
20667
|
+
availabilityEntities() {
|
|
20668
|
+
const out = [];
|
|
20669
|
+
for (const [schema, a] of this._availability)
|
|
20670
|
+
out.push(availabilityEntity(schema, a.value, a.lastRefusedTick));
|
|
20671
|
+
return out;
|
|
20672
|
+
}
|
|
20673
|
+
/** Rehydrate the availability ledger from state after a restore. Idempotent;
|
|
20674
|
+
* keeps whichever value is more restrictive so a concurrent refusal isn't lost. */
|
|
20675
|
+
restoreAvailability(entities) {
|
|
20676
|
+
for (const e of entities.values()) {
|
|
20677
|
+
if (e.type !== AVAILABILITY_ENTITY_TYPE) continue;
|
|
20678
|
+
const m = e.metadata ?? {};
|
|
20679
|
+
const schema = typeof m["schema"] === "string" ? m["schema"] : "";
|
|
20680
|
+
if (!schema) continue;
|
|
20681
|
+
const value = typeof m["value"] === "number" ? m["value"] : 1;
|
|
20682
|
+
const tick = typeof m["lastRefusedTick"] === "number" ? m["lastRefusedTick"] : 0;
|
|
20683
|
+
const prev = this._availability.get(schema);
|
|
20684
|
+
if (!prev || value < prev.value) this._availability.set(schema, { value, lastRefusedTick: tick });
|
|
20685
|
+
}
|
|
20686
|
+
}
|
|
20339
20687
|
};
|
|
20340
20688
|
function freshSkill(schema, value, tick) {
|
|
20341
20689
|
return {
|
|
@@ -20352,6 +20700,17 @@ function freshSkill(schema, value, tick) {
|
|
|
20352
20700
|
function clamp0110(n) {
|
|
20353
20701
|
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
20354
20702
|
}
|
|
20703
|
+
var AVAILABILITY_ENTITY_TYPE = "agency.availability";
|
|
20704
|
+
function availabilityEntityId(schema) {
|
|
20705
|
+
return `agency-availability-${schema}`;
|
|
20706
|
+
}
|
|
20707
|
+
function availabilityEntity(schema, value, lastRefusedTick) {
|
|
20708
|
+
return {
|
|
20709
|
+
id: availabilityEntityId(schema),
|
|
20710
|
+
type: AVAILABILITY_ENTITY_TYPE,
|
|
20711
|
+
metadata: { schema, value, lastRefusedTick }
|
|
20712
|
+
};
|
|
20713
|
+
}
|
|
20355
20714
|
var SCHEMA_ENTITY_TYPE = "agency.schema";
|
|
20356
20715
|
function schemaEntityId(schemaId) {
|
|
20357
20716
|
return `agency-schema-${schemaId}`;
|
|
@@ -20502,12 +20861,25 @@ var ReafferenceEngine = class {
|
|
|
20502
20861
|
}
|
|
20503
20862
|
let updates = 0;
|
|
20504
20863
|
let discovered = 0;
|
|
20864
|
+
let refused = 0;
|
|
20505
20865
|
for (const { id, meta: m, fromState } of outcomes) {
|
|
20506
20866
|
const schema = str6(m["schema"]);
|
|
20507
20867
|
if (!schema) {
|
|
20508
20868
|
if (fromState) del.push(id);
|
|
20509
20869
|
continue;
|
|
20510
20870
|
}
|
|
20871
|
+
if (m["refused"] === true) {
|
|
20872
|
+
const finality = asFinality(m["finality"]);
|
|
20873
|
+
if (finality !== "context")
|
|
20874
|
+
this._repertoire.recordRefusal(schema, finality, tick);
|
|
20875
|
+
if (fromState) del.push(id);
|
|
20876
|
+
const refusedIntent = str6(m["intentId"]);
|
|
20877
|
+
if (refusedIntent) del.push(refusedIntent);
|
|
20878
|
+
const refusedPlan = str6(m["planId"]);
|
|
20879
|
+
if (refusedPlan) this._emitPlanOutcome(refusedPlan, str6(m["stepId"]), schema, false, 0, 0, tick);
|
|
20880
|
+
refused++;
|
|
20881
|
+
continue;
|
|
20882
|
+
}
|
|
20511
20883
|
const { skill, proceduralized } = this._repertoire.recordOutcome({
|
|
20512
20884
|
schema,
|
|
20513
20885
|
success: m["success"] === true,
|
|
@@ -20534,11 +20906,14 @@ var ReafferenceEngine = class {
|
|
|
20534
20906
|
}
|
|
20535
20907
|
}
|
|
20536
20908
|
const dropped = this._repertoire.decay(tick);
|
|
20537
|
-
for (const id of dropped) {
|
|
20909
|
+
for (const id of dropped.skills) {
|
|
20538
20910
|
del.push(`agency-skill-${id}`);
|
|
20539
20911
|
del.push(schemaEntityId(id));
|
|
20540
20912
|
}
|
|
20913
|
+
for (const id of dropped.availability)
|
|
20914
|
+
del.push(availabilityEntityId(id));
|
|
20541
20915
|
for (const e of this._repertoire.compositeEntities()) set.push(e);
|
|
20916
|
+
for (const e of this._repertoire.availabilityEntities()) set.push(e);
|
|
20542
20917
|
const skills = this._repertoire.skills();
|
|
20543
20918
|
const habitual = [...skills.values()].filter((s) => s.habitStrength >= PROC_THRESHOLD2).length;
|
|
20544
20919
|
metrics.push(
|
|
@@ -20548,6 +20923,7 @@ var ReafferenceEngine = class {
|
|
|
20548
20923
|
["agency.habitual.count", habitual],
|
|
20549
20924
|
["agency.sensory.confirmed", sensory]
|
|
20550
20925
|
);
|
|
20926
|
+
if (refused > 0) metrics.push(["agency.refused.count", refused]);
|
|
20551
20927
|
return { commands: { set, delete: del, metrics } };
|
|
20552
20928
|
}
|
|
20553
20929
|
_emitProceduralized(skill, tick) {
|
|
@@ -21652,7 +22028,10 @@ ${r}`).join("\n\n---\n\n");
|
|
|
21652
22028
|
userMessage,
|
|
21653
22029
|
this._callCount,
|
|
21654
22030
|
void 0,
|
|
21655
|
-
|
|
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 }
|
|
21656
22035
|
);
|
|
21657
22036
|
if (result.text) {
|
|
21658
22037
|
this._summary = result.text.trim();
|
|
@@ -22730,7 +23109,7 @@ function buildEngineConfigEntities(config, executiveInterval) {
|
|
|
22730
23109
|
engine: "system",
|
|
22731
23110
|
params: {
|
|
22732
23111
|
anatomy: config.anatomy ?? "mind",
|
|
22733
|
-
model: config.model ?? "",
|
|
23112
|
+
model: config.llm?.model ?? "",
|
|
22734
23113
|
tickIntervalMs: config.tickIntervalMs ?? 1e3
|
|
22735
23114
|
}
|
|
22736
23115
|
},
|
|
@@ -23165,6 +23544,41 @@ function buildEngineConfigEntities(config, executiveInterval) {
|
|
|
23165
23544
|
}
|
|
23166
23545
|
|
|
23167
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
|
+
}
|
|
23168
23582
|
function resolveModelRoles(model) {
|
|
23169
23583
|
const map = typeof model === "string" ? { executive: model } : model ?? {};
|
|
23170
23584
|
const pin = process.env.WILL_LLM_MODEL;
|
|
@@ -23180,7 +23594,7 @@ function resolveModelRoles(model) {
|
|
|
23180
23594
|
};
|
|
23181
23595
|
}
|
|
23182
23596
|
var EXECUTIVE_CADENCE = {
|
|
23183
|
-
//
|
|
23597
|
+
// most attentive, highest spend — opt in via executiveInterval
|
|
23184
23598
|
balanced: 60};
|
|
23185
23599
|
function _resolveVectorMemory(willId, seed, overrideAdapter, disable, tokenTracker, testMode, embeddingModel) {
|
|
23186
23600
|
if (overrideAdapter) return { embedder: null, vectorMemory: overrideAdapter };
|
|
@@ -23310,6 +23724,10 @@ function _buildSimulation(willId, config, randomSeed) {
|
|
|
23310
23724
|
function _constructCognition({ simulation, willId, config, randomSeed, executiveInterval, profile }) {
|
|
23311
23725
|
const anatomy = config.anatomy ?? "mind";
|
|
23312
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),
|
|
23313
23731
|
emitCostEvents: true,
|
|
23314
23732
|
costWarningThresholdUsd: 0.02,
|
|
23315
23733
|
willId,
|
|
@@ -23341,7 +23759,7 @@ function _constructCognition({ simulation, willId, config, randomSeed, executive
|
|
|
23341
23759
|
const moralEvaluator = new MoralEvaluator();
|
|
23342
23760
|
const affectiveBlender = new AffectiveBlender();
|
|
23343
23761
|
const workingMemory = new WorkingMemory();
|
|
23344
|
-
const modelRoles = resolveModelRoles(config.model);
|
|
23762
|
+
const modelRoles = resolveModelRoles(config.llm?.model);
|
|
23345
23763
|
const { embedder, vectorMemory } = _resolveVectorMemory(willId, randomSeed, config.vectorMemoryAdapter, config.disableVectorMemory, tokenTracker, config.testMode, modelRoles.embedding ?? void 0);
|
|
23346
23764
|
const episodicConsolidator = new EpisodicConsolidator(vectorMemory ? { vectorMemory, ...embedder ? { embedder } : {} } : {});
|
|
23347
23765
|
const semanticIntegrator = new SemanticIntegrator();
|
|
@@ -23356,13 +23774,13 @@ function _constructCognition({ simulation, willId, config, randomSeed, executive
|
|
|
23356
23774
|
const accessGrants = new AccessGrants(resolvedEffectorNames);
|
|
23357
23775
|
const executiveEngine = new ExecutiveEngine({ executiveInterval, cooldownTicks: 5 });
|
|
23358
23776
|
executiveEngine.willId = willId;
|
|
23359
|
-
|
|
23360
|
-
executiveEngine.
|
|
23361
|
-
|
|
23362
|
-
|
|
23363
|
-
|
|
23364
|
-
|
|
23365
|
-
|
|
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;
|
|
23366
23784
|
if (config.testMode) executiveEngine.setTestMode(true);
|
|
23367
23785
|
executiveEngine.attachWorkingMemory(workingMemory);
|
|
23368
23786
|
executiveEngine.attachGoalManager(goalManager);
|
|
@@ -23645,7 +24063,7 @@ function resolveExecutiveInterval(config) {
|
|
|
23645
24063
|
}
|
|
23646
24064
|
|
|
23647
24065
|
// src/stem/guards/identity.coherence.ts
|
|
23648
|
-
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 };
|
|
23649
24067
|
var VALID_KINDS = /* @__PURE__ */ new Set(["contradiction", "false-capability", "injection", "incoherence", "other"]);
|
|
23650
24068
|
var SYSTEM_PROMPT = `You are a safety reviewer of profile/persona inputs for, an autonomous synthetic-mind (Called Wills) platform.
|
|
23651
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.
|
|
@@ -23685,12 +24103,17 @@ async function checkIdentityCoherence(input, reviewer) {
|
|
|
23685
24103
|
return { ok: !issues.some((i) => i.severity === "error"), ran: true, issues, raw: text };
|
|
23686
24104
|
}
|
|
23687
24105
|
async function reviewIdentityCoherence(input, opts = {}) {
|
|
23688
|
-
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" };
|
|
23689
24110
|
const director = new LLMDirector({
|
|
23690
24111
|
willId: opts.willId ?? "identity-coherence",
|
|
23691
|
-
model
|
|
24112
|
+
model,
|
|
23692
24113
|
maxOutputTokens: 512,
|
|
23693
|
-
|
|
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 ?? "",
|
|
23694
24117
|
provider,
|
|
23695
24118
|
sessionLogger: null,
|
|
23696
24119
|
baseUrl: process.env.WILL_LLM_BASE_URL ?? process.env.OPENAI_BASE_URL,
|
|
@@ -25438,6 +25861,10 @@ function reconcileInvocation(intentId, schema, result, tick, predicted = { rewar
|
|
|
25438
25861
|
mode: "external",
|
|
25439
25862
|
tick,
|
|
25440
25863
|
reconciled: true,
|
|
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 } : {},
|
|
25441
25868
|
...provenance.planId ? { planId: provenance.planId } : {},
|
|
25442
25869
|
...provenance.stepId ? { stepId: provenance.stepId } : {}
|
|
25443
25870
|
}
|
|
@@ -25447,8 +25874,49 @@ function clamp0112(n) {
|
|
|
25447
25874
|
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
25448
25875
|
}
|
|
25449
25876
|
|
|
25877
|
+
// src/stem/policy/verdict.recorder.ts
|
|
25878
|
+
var _sinks3 = /* @__PURE__ */ new Map();
|
|
25879
|
+
function getVerdictRecorder(willId) {
|
|
25880
|
+
return _sinks3.get(willId);
|
|
25881
|
+
}
|
|
25882
|
+
var _sources3 = /* @__PURE__ */ new Map();
|
|
25883
|
+
function getVerdictSource(willId) {
|
|
25884
|
+
return _sources3.get(willId);
|
|
25885
|
+
}
|
|
25886
|
+
|
|
25450
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
|
+
});
|
|
25893
|
+
var ESCALATION_TTL_TICKS = 30;
|
|
25451
25894
|
var effectorController = class {
|
|
25895
|
+
/** The Policy Decision Point consulted before an invocation reaches the world.
|
|
25896
|
+
* Defaults to the no-op arbiter, so an unconfigured Will is byte-identical. */
|
|
25897
|
+
_arbiter = NULL_ARBITER;
|
|
25898
|
+
/**
|
|
25899
|
+
* Denials queued during a step's flush, drained at the NEXT tick boundary
|
|
25900
|
+
* (POLICY_REAFFERENCE P1). Keyed by willId — harness state, exactly like
|
|
25901
|
+
* `pendingEffectorInvocations`; never simulation state, so it does not touch
|
|
25902
|
+
* `simulation.step` determinism and is regenerated on any re-execution.
|
|
25903
|
+
*/
|
|
25904
|
+
_pendingRefusals = /* @__PURE__ */ new Map();
|
|
25905
|
+
/** Escalations awaiting their first application (mark intent + voice the ask). */
|
|
25906
|
+
_newEscalations = /* @__PURE__ */ new Map();
|
|
25907
|
+
/** Escalations currently held, keyed by intent id — the resolvable set. */
|
|
25908
|
+
_activeEscalations = /* @__PURE__ */ new Map();
|
|
25909
|
+
/** Host answers awaiting application at the next tick boundary. */
|
|
25910
|
+
_pendingResolutions = /* @__PURE__ */ new Map();
|
|
25911
|
+
/**
|
|
25912
|
+
* Install a Policy Decision Point (POLICY_REAFFERENCE P0). Passing null
|
|
25913
|
+
* restores the no-op default. The arbiter sees only the proposed act — never
|
|
25914
|
+
* simulation state — and its verdict decides whether the invocation is
|
|
25915
|
+
* handed to the host at all.
|
|
25916
|
+
*/
|
|
25917
|
+
setArbiter(arbiter) {
|
|
25918
|
+
this._arbiter = arbiter ?? NULL_ARBITER;
|
|
25919
|
+
}
|
|
25452
25920
|
/**
|
|
25453
25921
|
* Update the set of allowed communication effectors at runtime via AccessGrants
|
|
25454
25922
|
* (the permission / sense gate the senses + reply path read).
|
|
@@ -25464,6 +25932,239 @@ var effectorController = class {
|
|
|
25464
25932
|
* echoes it on its result-ack, and `confirmExecution` uses it to find the intent.
|
|
25465
25933
|
*/
|
|
25466
25934
|
bufferInvocation(instance, payload) {
|
|
25935
|
+
const willId = instance.config.id;
|
|
25936
|
+
const source = getVerdictSource(willId);
|
|
25937
|
+
if (source) {
|
|
25938
|
+
const invocation2 = toPolicyInvocation(instance, payload);
|
|
25939
|
+
const record = source.verdictFor(invocation2.tick, invocation2.intentId);
|
|
25940
|
+
if (record) this._applyVerdict(instance, payload, invocation2, recordToVerdict(record));
|
|
25941
|
+
else this._buffer(instance, payload);
|
|
25942
|
+
return;
|
|
25943
|
+
}
|
|
25944
|
+
if (isNullArbiter(this._arbiter)) {
|
|
25945
|
+
this._buffer(instance, payload);
|
|
25946
|
+
return;
|
|
25947
|
+
}
|
|
25948
|
+
const invocation = toPolicyInvocation(instance, payload);
|
|
25949
|
+
let verdict;
|
|
25950
|
+
try {
|
|
25951
|
+
verdict = this._arbiter.evaluate(invocation);
|
|
25952
|
+
} catch (err) {
|
|
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);
|
|
25955
|
+
return;
|
|
25956
|
+
}
|
|
25957
|
+
if (verdict instanceof Promise) {
|
|
25958
|
+
void verdict.then(
|
|
25959
|
+
(v) => this._recordAndApply(instance, payload, invocation, v),
|
|
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
|
+
}
|
|
25964
|
+
);
|
|
25965
|
+
return;
|
|
25966
|
+
}
|
|
25967
|
+
this._recordAndApply(instance, payload, invocation, verdict);
|
|
25968
|
+
}
|
|
25969
|
+
/** Capture the verdict on the tape (if a recorder is attached), then enforce it. */
|
|
25970
|
+
_recordAndApply(instance, payload, invocation, verdict) {
|
|
25971
|
+
const sink = getVerdictRecorder(instance.config.id);
|
|
25972
|
+
sink?.recordVerdict({
|
|
25973
|
+
tick: invocation.tick,
|
|
25974
|
+
willId: instance.config.id,
|
|
25975
|
+
intentId: invocation.intentId,
|
|
25976
|
+
schema: invocation.schema,
|
|
25977
|
+
arbiter: this._arbiter.name,
|
|
25978
|
+
decision: verdict.decision,
|
|
25979
|
+
...verdict.reasonCode ? { reasonCode: verdict.reasonCode } : {},
|
|
25980
|
+
...verdict.finality ? { finality: verdict.finality } : {},
|
|
25981
|
+
...verdict.counterfactual ? { counterfactual: verdict.counterfactual } : {},
|
|
25982
|
+
timestamp: Date.now()
|
|
25983
|
+
});
|
|
25984
|
+
this._applyVerdict(instance, payload, invocation, verdict);
|
|
25985
|
+
}
|
|
25986
|
+
/**
|
|
25987
|
+
* Enforce a verdict (POLICY_REAFFERENCE P1).
|
|
25988
|
+
*
|
|
25989
|
+
* • allow → hand the invocation to the world.
|
|
25990
|
+
* • deny → queue a refusal ack, applied at the next tick boundary via
|
|
25991
|
+
* `confirmExecution` — the same lifecycle as a host rejection,
|
|
25992
|
+
* so the mind meets *world resistance*, not a permission dialog.
|
|
25993
|
+
* • escalate → raise a held escalation (POLICY_REAFFERENCE P4): the intent is
|
|
25994
|
+
* held (the executor stops timing it out), the Will voices a
|
|
25995
|
+
* first-person ask once, and a host resolution later approves
|
|
25996
|
+
* (dispatch) or denies (refuse). Unresolved, it degrades to a
|
|
25997
|
+
* refusal at ESCALATION_TTL_TICKS.
|
|
25998
|
+
*
|
|
25999
|
+
* P1's refusal reconciles as a plain FAILURE — safe, but the wrong learning
|
|
26000
|
+
* signal (forbidden ≠ unskilled). P2 routes it to affordance AVAILABILITY
|
|
26001
|
+
* instead of competence.
|
|
26002
|
+
*/
|
|
26003
|
+
_applyVerdict(instance, payload, invocation, verdict) {
|
|
26004
|
+
if (verdict.decision === "allow") {
|
|
26005
|
+
this._buffer(instance, payload);
|
|
26006
|
+
return;
|
|
26007
|
+
}
|
|
26008
|
+
const cf = verdict.counterfactual;
|
|
26009
|
+
logger.info(
|
|
26010
|
+
`[policy] ${verdict.decision.toUpperCase()} "${invocation.schema}" intent "${invocation.intentId}" \u2014 ${verdict.reasonCode ?? "no reason code"}` + (verdict.finality ? ` (${verdict.finality})` : "") + (cf ? ` [${cf.field}: requested ${JSON.stringify(cf.requested)}, allowed ${JSON.stringify(cf.allowed)}]` : "")
|
|
26011
|
+
);
|
|
26012
|
+
if (verdict.decision === "deny") {
|
|
26013
|
+
const queue = this._pendingRefusals.get(instance.config.id) ?? [];
|
|
26014
|
+
queue.push({
|
|
26015
|
+
intentId: invocation.intentId,
|
|
26016
|
+
schema: invocation.schema,
|
|
26017
|
+
reasonCode: verdict.reasonCode ?? "POLICY_DENIED",
|
|
26018
|
+
finality: finalityOf(verdict),
|
|
26019
|
+
...verdict.counterfactual ? { counterfactual: verdict.counterfactual } : {}
|
|
26020
|
+
});
|
|
26021
|
+
this._pendingRefusals.set(instance.config.id, queue);
|
|
26022
|
+
return;
|
|
26023
|
+
}
|
|
26024
|
+
const escalations = this._newEscalations.get(instance.config.id) ?? [];
|
|
26025
|
+
escalations.push({
|
|
26026
|
+
intentId: invocation.intentId,
|
|
26027
|
+
schema: invocation.schema,
|
|
26028
|
+
reasonCode: verdict.reasonCode ?? "APPROVAL_REQUIRED",
|
|
26029
|
+
payload,
|
|
26030
|
+
expiresAt: 0
|
|
26031
|
+
// stamped when applied (we don't have the current tick here)
|
|
26032
|
+
});
|
|
26033
|
+
this._newEscalations.set(instance.config.id, escalations);
|
|
26034
|
+
}
|
|
26035
|
+
/**
|
|
26036
|
+
* Record a host's answer to an escalation (POLICY_REAFFERENCE P4). Applied at
|
|
26037
|
+
* the next tick boundary so every simulation-state write stays on the boundary:
|
|
26038
|
+
* approve dispatches the held invocation to the world; deny refuses it. A
|
|
26039
|
+
* no-op if the intent id is not (or no longer) an active escalation.
|
|
26040
|
+
*/
|
|
26041
|
+
resolveEscalation(instance, intentId, approved) {
|
|
26042
|
+
const queue = this._pendingResolutions.get(instance.config.id) ?? [];
|
|
26043
|
+
queue.push({ intentId, approved });
|
|
26044
|
+
this._pendingResolutions.set(instance.config.id, queue);
|
|
26045
|
+
}
|
|
26046
|
+
/**
|
|
26047
|
+
* Apply queued policy refusals as failure acks (POLICY_REAFFERENCE P1).
|
|
26048
|
+
* Called by the tick loop at the same boundary as inbound acks — BEFORE the
|
|
26049
|
+
* step, stamped to this tick — so a denial reconciled here is the exact
|
|
26050
|
+
* lifecycle of a host rejection that arrived between ticks.
|
|
26051
|
+
*/
|
|
26052
|
+
applyPolicyOutcomes(instance) {
|
|
26053
|
+
const tick = instance.tickCount;
|
|
26054
|
+
this._applyResolutions(instance);
|
|
26055
|
+
this._expireEscalations(instance, tick);
|
|
26056
|
+
this._applyNewEscalations(instance, tick);
|
|
26057
|
+
this._applyRefusals(instance);
|
|
26058
|
+
}
|
|
26059
|
+
/** Drain queued refusals into failure acks (POLICY_REAFFERENCE P1). */
|
|
26060
|
+
_applyRefusals(instance) {
|
|
26061
|
+
const queue = this._pendingRefusals.get(instance.config.id);
|
|
26062
|
+
if (!queue || queue.length === 0) return;
|
|
26063
|
+
this._pendingRefusals.set(instance.config.id, []);
|
|
26064
|
+
for (const refusal of queue)
|
|
26065
|
+
this.confirmExecution(instance, refusal.intentId, {
|
|
26066
|
+
success: false,
|
|
26067
|
+
refused: true,
|
|
26068
|
+
finality: refusal.finality,
|
|
26069
|
+
...refusal.counterfactual ? { counterfactual: refusal.counterfactual } : {},
|
|
26070
|
+
description: `refused by policy: ${refusal.reasonCode} (${refusal.finality})`
|
|
26071
|
+
});
|
|
26072
|
+
}
|
|
26073
|
+
/** Raise each newly-escalated intent (POLICY_REAFFERENCE P4): mark it held in
|
|
26074
|
+
* simulation state, voice the ask ONCE, and move it to the resolvable set. */
|
|
26075
|
+
_applyNewEscalations(instance, tick) {
|
|
26076
|
+
const pending = this._newEscalations.get(instance.config.id);
|
|
26077
|
+
if (!pending || pending.length === 0) return;
|
|
26078
|
+
this._newEscalations.set(instance.config.id, []);
|
|
26079
|
+
const active = this._activeEscalations.get(instance.config.id) ?? /* @__PURE__ */ new Map();
|
|
26080
|
+
for (const esc of pending) {
|
|
26081
|
+
esc.expiresAt = tick + ESCALATION_TTL_TICKS;
|
|
26082
|
+
this._markEscalated(instance, esc.intentId, esc.expiresAt);
|
|
26083
|
+
this._voiceEscalation(instance, esc);
|
|
26084
|
+
active.set(esc.intentId, esc);
|
|
26085
|
+
}
|
|
26086
|
+
this._activeEscalations.set(instance.config.id, active);
|
|
26087
|
+
}
|
|
26088
|
+
/** Apply host answers to active escalations (POLICY_REAFFERENCE P4). */
|
|
26089
|
+
_applyResolutions(instance) {
|
|
26090
|
+
const queue = this._pendingResolutions.get(instance.config.id);
|
|
26091
|
+
if (!queue || queue.length === 0) return;
|
|
26092
|
+
this._pendingResolutions.set(instance.config.id, []);
|
|
26093
|
+
const active = this._activeEscalations.get(instance.config.id);
|
|
26094
|
+
for (const { intentId, approved } of queue) {
|
|
26095
|
+
const esc = active?.get(intentId);
|
|
26096
|
+
if (!esc) continue;
|
|
26097
|
+
active.delete(intentId);
|
|
26098
|
+
this._clearEscalated(instance, intentId);
|
|
26099
|
+
if (approved) {
|
|
26100
|
+
this._buffer(instance, esc.payload);
|
|
26101
|
+
logger.info(`[policy] escalation APPROVED \u2192 dispatching "${esc.schema}" intent "${intentId}"`);
|
|
26102
|
+
} else {
|
|
26103
|
+
this._queueRefusal(instance, esc.intentId, esc.schema, esc.reasonCode, "class");
|
|
26104
|
+
logger.info(`[policy] escalation DENIED \u2192 refusing "${esc.schema}" intent "${intentId}"`);
|
|
26105
|
+
}
|
|
26106
|
+
}
|
|
26107
|
+
}
|
|
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
|
+
*/
|
|
26118
|
+
_expireEscalations(instance, tick) {
|
|
26119
|
+
const active = this._activeEscalations.get(instance.config.id);
|
|
26120
|
+
if (!active || active.size === 0) return;
|
|
26121
|
+
for (const [intentId, esc] of active) {
|
|
26122
|
+
if (tick < esc.expiresAt) continue;
|
|
26123
|
+
active.delete(intentId);
|
|
26124
|
+
this._clearEscalated(instance, intentId);
|
|
26125
|
+
this._queueRefusal(instance, esc.intentId, esc.schema, "ESCALATION_EXPIRED", "parameter");
|
|
26126
|
+
logger.info(`[policy] escalation EXPIRED \u2192 refusing "${esc.schema}" intent "${intentId}"`);
|
|
26127
|
+
}
|
|
26128
|
+
}
|
|
26129
|
+
/** Push a refusal onto the queue drained by _applyRefusals this same tick. */
|
|
26130
|
+
_queueRefusal(instance, intentId, schema, reasonCode, finality) {
|
|
26131
|
+
const queue = this._pendingRefusals.get(instance.config.id) ?? [];
|
|
26132
|
+
queue.push({ intentId, schema, reasonCode, finality });
|
|
26133
|
+
this._pendingRefusals.set(instance.config.id, queue);
|
|
26134
|
+
}
|
|
26135
|
+
/** Mark the awaiting intent held: the executor stops timing it out (P4). */
|
|
26136
|
+
_markEscalated(instance, intentId, expiresAt) {
|
|
26137
|
+
const intent = instance.simulation.stateManager.snapshot().entities.get(intentId);
|
|
26138
|
+
if (!intent || intent.type !== "agency.intent") return;
|
|
26139
|
+
instance.simulation.stateManager.setEntity({
|
|
26140
|
+
id: intent.id,
|
|
26141
|
+
type: intent.type,
|
|
26142
|
+
metadata: { ...intent.metadata ?? {}, escalated: true, escalationExpiresAt: expiresAt }
|
|
26143
|
+
});
|
|
26144
|
+
}
|
|
26145
|
+
/** Release the hold so the executor resumes normal timeout for this intent. */
|
|
26146
|
+
_clearEscalated(instance, intentId) {
|
|
26147
|
+
const intent = instance.simulation.stateManager.snapshot().entities.get(intentId);
|
|
26148
|
+
if (!intent || intent.type !== "agency.intent") return;
|
|
26149
|
+
const meta = { ...intent.metadata ?? {} };
|
|
26150
|
+
delete meta["escalated"];
|
|
26151
|
+
delete meta["escalationExpiresAt"];
|
|
26152
|
+
instance.simulation.stateManager.setEntity({ id: intent.id, type: intent.type, metadata: meta });
|
|
26153
|
+
}
|
|
26154
|
+
/** Voice the escalation as a first-person broadcast ask — once, at raise time. */
|
|
26155
|
+
_voiceEscalation(instance, esc) {
|
|
26156
|
+
try {
|
|
26157
|
+
instance.cognition.outboxWriter.enqueue({
|
|
26158
|
+
targetEntityId: "*",
|
|
26159
|
+
content: escalationAsk(esc.schema, esc.reasonCode),
|
|
26160
|
+
effectorName: "broadcast"
|
|
26161
|
+
});
|
|
26162
|
+
} catch (err) {
|
|
26163
|
+
logger.warn(`[policy] escalation voice failed for "${esc.schema}": ${errMsg2(err)}`);
|
|
26164
|
+
}
|
|
26165
|
+
}
|
|
26166
|
+
/** Queue an approved invocation for the delivery layer. */
|
|
26167
|
+
_buffer(instance, payload) {
|
|
25467
26168
|
const intentId = payload.intentId ?? "";
|
|
25468
26169
|
instance.pendingEffectorInvocations.push({
|
|
25469
26170
|
id: intentId,
|
|
@@ -25538,6 +26239,38 @@ var effectorController = class {
|
|
|
25538
26239
|
function num5(v, fallback) {
|
|
25539
26240
|
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
25540
26241
|
}
|
|
26242
|
+
function escalationAsk(schema, reasonCode) {
|
|
26243
|
+
const meaning = ESCALATION_MEANINGS[reasonCode] ?? "I need your approval before I can do this";
|
|
26244
|
+
return `I want to ${schema}, but ${meaning}. May I go ahead?`;
|
|
26245
|
+
}
|
|
26246
|
+
var ESCALATION_MEANINGS = {
|
|
26247
|
+
APPROVAL_REQUIRED: "I need your approval before I can on my own",
|
|
26248
|
+
WRITE_REQUIRES_APPROVAL: "it writes to the world and I shouldn't on my own",
|
|
26249
|
+
PAYMENT_REQUIRES_APPROVAL: "it moves money and I must not do that unattended",
|
|
26250
|
+
DEPLOY_REQUIRES_APPROVAL: "it ships something and needs a human to sign off"
|
|
26251
|
+
};
|
|
26252
|
+
function errMsg2(err) {
|
|
26253
|
+
return err instanceof Error ? err.message : String(err);
|
|
26254
|
+
}
|
|
26255
|
+
function recordToVerdict(record) {
|
|
26256
|
+
return {
|
|
26257
|
+
decision: record.decision,
|
|
26258
|
+
...record.reasonCode ? { reasonCode: record.reasonCode } : {},
|
|
26259
|
+
...record.finality ? { finality: record.finality } : {},
|
|
26260
|
+
...record.counterfactual ? { counterfactual: record.counterfactual } : {}
|
|
26261
|
+
};
|
|
26262
|
+
}
|
|
26263
|
+
function toPolicyInvocation(instance, payload) {
|
|
26264
|
+
return {
|
|
26265
|
+
willId: instance.config.id,
|
|
26266
|
+
intentId: payload.intentId ?? "",
|
|
26267
|
+
schema: payload.schema ?? "",
|
|
26268
|
+
parameters: payload.parameters ?? {},
|
|
26269
|
+
...typeof payload.targetEntityId === "string" ? { targetEntityId: payload.targetEntityId } : {},
|
|
26270
|
+
...typeof payload.description === "string" ? { description: payload.description } : {},
|
|
26271
|
+
tick: payload.tick ?? 0
|
|
26272
|
+
};
|
|
26273
|
+
}
|
|
25541
26274
|
|
|
25542
26275
|
// src/stem/tracts/sensory.controller.ts
|
|
25543
26276
|
var SensoryController = class {
|
|
@@ -25941,7 +26674,7 @@ var WillStem = class {
|
|
|
25941
26674
|
willId: config.id,
|
|
25942
26675
|
willName: config.name,
|
|
25943
26676
|
anatomy: config.anatomy ?? "mind",
|
|
25944
|
-
model: config.model ?? null,
|
|
26677
|
+
model: config.llm?.model ?? null,
|
|
25945
26678
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25946
26679
|
});
|
|
25947
26680
|
instance._eventBusUnsub = simulation.eventBus.subscribeAll((event, context) => {
|
|
@@ -26340,6 +27073,15 @@ var WillStem = class {
|
|
|
26340
27073
|
confirmEffectorExecution(id, invocationId, result) {
|
|
26341
27074
|
this._effector.confirmExecution(this._get(id), invocationId, result);
|
|
26342
27075
|
}
|
|
27076
|
+
/**
|
|
27077
|
+
* Resolve a policy escalation the Will raised (POLICY_REAFFERENCE P4).
|
|
27078
|
+
* `approved` dispatches the held invocation to the world; otherwise it is
|
|
27079
|
+
* refused. Applied at the next tick boundary. `invocationId` is the awaiting
|
|
27080
|
+
* `agency.intent` id the escalation ask referenced.
|
|
27081
|
+
*/
|
|
27082
|
+
resolveEscalation(id, invocationId, approved) {
|
|
27083
|
+
this._effector.resolveEscalation(this._get(id), invocationId, approved);
|
|
27084
|
+
}
|
|
26343
27085
|
// ── Messaging / outbox (11.1) ────────────────────────────────────────────
|
|
26344
27086
|
// Delegates to OutboxController (R5-c). `_get(id)` validates the Will exists
|
|
26345
27087
|
// and supplies the WillInstance; the outbox ops touch only instance fields.
|
|
@@ -26453,7 +27195,7 @@ var WillStem = class {
|
|
|
26453
27195
|
createdAt: inst.createdAt,
|
|
26454
27196
|
lastTickAt: inst.lastTickAt,
|
|
26455
27197
|
anatomy: inst.config.anatomy ?? "mind",
|
|
26456
|
-
model: inst.config.model
|
|
27198
|
+
model: inst.config.llm?.model
|
|
26457
27199
|
}));
|
|
26458
27200
|
}
|
|
26459
27201
|
// ── Tick loop (internal) ───────────────────────────────────
|
|
@@ -26470,6 +27212,7 @@ var WillStem = class {
|
|
|
26470
27212
|
outbox: this._outbox,
|
|
26471
27213
|
sensory: this._sensory
|
|
26472
27214
|
});
|
|
27215
|
+
this._effector.applyPolicyOutcomes(instance);
|
|
26473
27216
|
await instance.simulation.step(1);
|
|
26474
27217
|
instance.tickCount++;
|
|
26475
27218
|
instance.lastTickAt = /* @__PURE__ */ new Date();
|
|
@@ -26785,6 +27528,19 @@ var SocketIoTransport = class {
|
|
|
26785
27528
|
|
|
26786
27529
|
// src/sdk/will.ts
|
|
26787
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
|
+
}
|
|
26788
27544
|
var Will = class _Will {
|
|
26789
27545
|
/** The underlying WillStem — drop here for the full contract. */
|
|
26790
27546
|
stem;
|
|
@@ -27008,9 +27764,13 @@ var Will = class _Will {
|
|
|
27008
27764
|
}
|
|
27009
27765
|
// ── Internals ──────────────────────────────────────────────
|
|
27010
27766
|
_buildConfig(id, opts) {
|
|
27011
|
-
const mode = opts.llm ?? (
|
|
27767
|
+
const mode = opts.llm ?? detectProvider();
|
|
27012
27768
|
const useMock = mode === "mock";
|
|
27013
|
-
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
|
+
};
|
|
27014
27774
|
return {
|
|
27015
27775
|
id,
|
|
27016
27776
|
name: opts.name,
|
|
@@ -27021,7 +27781,6 @@ var Will = class _Will {
|
|
|
27021
27781
|
style: opts.identity.style ?? ""
|
|
27022
27782
|
},
|
|
27023
27783
|
anatomy: opts.anatomy ?? "mind",
|
|
27024
|
-
model: opts.model,
|
|
27025
27784
|
llm: llmConfig,
|
|
27026
27785
|
testMode: useMock,
|
|
27027
27786
|
persistentMemory: opts.persist ?? false,
|
|
@@ -27129,6 +27888,6 @@ function slug(s) {
|
|
|
27129
27888
|
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "will";
|
|
27130
27889
|
}
|
|
27131
27890
|
|
|
27132
|
-
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 };
|
|
27133
27892
|
//# sourceMappingURL=index.js.map
|
|
27134
27893
|
//# sourceMappingURL=index.js.map
|