nexus-agents 8.85.2 → 8.86.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/dist/{ast-rule-runner-GDCEI7BW.js → ast-rule-runner-USLLC3XL.js} +4 -4
- package/dist/{child-mcp-config-HAXDNULH.js → child-mcp-config-K563PP6S.js} +3 -3
- package/dist/{chunk-QLYONNVR.js → chunk-2SQO4FET.js} +2 -2
- package/dist/{chunk-L3PGHZH7.js → chunk-4Q753QZM.js} +3 -3
- package/dist/{chunk-WIBR5HMY.js → chunk-5U4XTYAB.js} +2 -2
- package/dist/{chunk-YGOTLETF.js → chunk-64IJKT4I.js} +537 -402
- package/dist/chunk-64IJKT4I.js.map +1 -0
- package/dist/{chunk-CDHUCQ44.js → chunk-C4YTAB6K.js} +43 -22
- package/dist/chunk-C4YTAB6K.js.map +1 -0
- package/dist/{chunk-TYHKIUUX.js → chunk-CBZAMHBA.js} +2 -2
- package/dist/{chunk-FOODF33L.js → chunk-DSVMQVJZ.js} +4 -4
- package/dist/{chunk-XLYTLQAT.js → chunk-EVYZZL7J.js} +2 -2
- package/dist/{chunk-7NLTUPYN.js → chunk-FNL4J77P.js} +2 -2
- package/dist/chunk-FNL4J77P.js.map +1 -0
- package/dist/{chunk-GFFQHVSC.js → chunk-GKLTEUFU.js} +2 -2
- package/dist/{chunk-JHNS5FYV.js → chunk-IXKFIO6O.js} +2 -2
- package/dist/{chunk-4RHGZLKP.js → chunk-J6GIJFV4.js} +2 -2
- package/dist/{chunk-HSMW4KIV.js → chunk-KX7A23JM.js} +2 -2
- package/dist/{chunk-PB4SXWBA.js → chunk-KXFPJOPL.js} +2 -2
- package/dist/{chunk-Y2ARXXPF.js → chunk-NNYVXSQF.js} +2 -2
- package/dist/{chunk-K6VACGLH.js → chunk-NQA77WXP.js} +3 -3
- package/dist/{chunk-7L6KI365.js → chunk-O24ENDHN.js} +3 -3
- package/dist/{chunk-M6U6UHFA.js → chunk-OOIZ3IQD.js} +2 -2
- package/dist/{chunk-U33OOJOK.js → chunk-P6D7S7CY.js} +2 -2
- package/dist/{chunk-B3PR26HR.js → chunk-Q75P5CVJ.js} +2 -2
- package/dist/{chunk-P5BZ3DNM.js → chunk-QCDYJYGB.js} +7 -7
- package/dist/{chunk-WZRXZ6P2.js → chunk-QW6J2UV4.js} +43 -9
- package/dist/chunk-QW6J2UV4.js.map +1 -0
- package/dist/{chunk-4UHDDCRB.js → chunk-RQLIPUQK.js} +5 -1
- package/dist/{chunk-4UHDDCRB.js.map → chunk-RQLIPUQK.js.map} +1 -1
- package/dist/{chunk-3EK4V5JS.js → chunk-S6MNVNQB.js} +2 -2
- package/dist/{chunk-IKLDY3HI.js → chunk-SQ4M3JHW.js} +41 -39
- package/dist/chunk-SQ4M3JHW.js.map +1 -0
- package/dist/{chunk-Z33AE45S.js → chunk-V66MMBO4.js} +5 -5
- package/dist/{chunk-WKZSEYV7.js → chunk-XARB7ITD.js} +8 -8
- package/dist/{chunk-PBXABL7H.js → chunk-XNNNDQLK.js} +2 -2
- package/dist/{chunk-VYZRGIY5.js → chunk-XWPZZEBX.js} +4 -4
- package/dist/{chunk-ILGHDIYO.js → chunk-YG73EH43.js} +2 -2
- package/dist/{chunk-23DTC2UV.js → chunk-YPJRVA2H.js} +4 -4
- package/dist/{chunk-NL7I35TJ.js → chunk-ZE73MT66.js} +5 -5
- package/dist/chunk-ZE73MT66.js.map +1 -0
- package/dist/{chunk-ARWTQGSY.js → chunk-ZEOHUE2N.js} +10 -10
- package/dist/{chunk-KC3AZWVQ.js → chunk-ZSFRB2S4.js} +3 -3
- package/dist/{cli-circuit-breaker-TOE7GRYI.js → cli-circuit-breaker-4ASNTBDP.js} +4 -4
- package/dist/cli.js +43 -43
- package/dist/{composite-router-UD3FGSSS.js → composite-router-V45AK6WD.js} +3 -3
- package/dist/{consensus-vote-YW6LAIH7.js → consensus-vote-N2Z7FX3D.js} +17 -17
- package/dist/{context-retriever-CH3GEH2Z.js → context-retriever-NZOSGG6S.js} +9 -9
- package/dist/{doctor-deep-LMEUFKDA.js → doctor-deep-JRY3D7BP.js} +4 -4
- package/dist/{doctor-live-O25NTC5D.js → doctor-live-NSRHMHMG.js} +11 -11
- package/dist/{expert-bridge-ZQSYVUC2.js → expert-bridge-Q4SEPVTE.js} +5 -5
- package/dist/factory-JBK45P7G.js +15 -0
- package/dist/{factory-O4U4BJFT.js → factory-ZV4MR3VM.js} +10 -10
- package/dist/{improvement-review-RL6VHCCY.js → improvement-review-KHTIDV4R.js} +6 -6
- package/dist/index.d.ts +56 -6
- package/dist/index.js +31 -31
- package/dist/{init-opencode-RMMHBIQ2.js → init-opencode-6WGN5J6J.js} +6 -6
- package/dist/issue-triage-XVDXOZRW.js +16 -0
- package/dist/{learning-persistence-7BEPQEGN.js → learning-persistence-ER7E5Q6T.js} +6 -4
- package/dist/model-registry.generated.json +2949 -3961
- package/dist/{pr-reviewer-helpers-S53B7TYK.js → pr-reviewer-helpers-UMNMOUAO.js} +5 -5
- package/dist/{registry-command-TWHRKWDB.js → registry-command-QRBAKWRK.js} +3 -3
- package/dist/{repo-security-plan-HYJ7HTOS.js → repo-security-plan-UM6WX5PN.js} +4 -4
- package/dist/research-helpers-synthesize-DUOVXI5V.js +14 -0
- package/dist/{routing-memory-HQTEZF2S.js → routing-memory-OKYSDPXT.js} +3 -3
- package/dist/{session-memory-M5MEGJJZ.js → session-memory-ONUSRMYV.js} +4 -4
- package/dist/setup-config-BAZRBFN3.js +11 -0
- package/dist/{setup-custom-api-LRL6J7RN.js → setup-custom-api-LSMLWGKW.js} +5 -5
- package/dist/setup-data-dir-7PDX3R3Y.js +32 -0
- package/dist/{tool-memory-24PMDECK.js → tool-memory-Q5UWNVN3.js} +6 -6
- package/dist/{unified-registry-NODUOBMA.js → unified-registry-Y327PN6R.js} +11 -11
- package/dist/{weather-report-IGEXNIWL.js → weather-report-C2VIAC2Q.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-7NLTUPYN.js.map +0 -1
- package/dist/chunk-CDHUCQ44.js.map +0 -1
- package/dist/chunk-IKLDY3HI.js.map +0 -1
- package/dist/chunk-NL7I35TJ.js.map +0 -1
- package/dist/chunk-WZRXZ6P2.js.map +0 -1
- package/dist/chunk-YGOTLETF.js.map +0 -1
- package/dist/factory-ZOM2QJPC.js +0 -15
- package/dist/issue-triage-DQAOBZEP.js +0 -16
- package/dist/research-helpers-synthesize-G3WV7AHW.js +0 -14
- package/dist/setup-config-L5QGPDH6.js +0 -11
- package/dist/setup-data-dir-WZXMTSDN.js +0 -32
- /package/dist/{ast-rule-runner-GDCEI7BW.js.map → ast-rule-runner-USLLC3XL.js.map} +0 -0
- /package/dist/{child-mcp-config-HAXDNULH.js.map → child-mcp-config-K563PP6S.js.map} +0 -0
- /package/dist/{chunk-QLYONNVR.js.map → chunk-2SQO4FET.js.map} +0 -0
- /package/dist/{chunk-L3PGHZH7.js.map → chunk-4Q753QZM.js.map} +0 -0
- /package/dist/{chunk-WIBR5HMY.js.map → chunk-5U4XTYAB.js.map} +0 -0
- /package/dist/{chunk-TYHKIUUX.js.map → chunk-CBZAMHBA.js.map} +0 -0
- /package/dist/{chunk-FOODF33L.js.map → chunk-DSVMQVJZ.js.map} +0 -0
- /package/dist/{chunk-XLYTLQAT.js.map → chunk-EVYZZL7J.js.map} +0 -0
- /package/dist/{chunk-GFFQHVSC.js.map → chunk-GKLTEUFU.js.map} +0 -0
- /package/dist/{chunk-JHNS5FYV.js.map → chunk-IXKFIO6O.js.map} +0 -0
- /package/dist/{chunk-4RHGZLKP.js.map → chunk-J6GIJFV4.js.map} +0 -0
- /package/dist/{chunk-HSMW4KIV.js.map → chunk-KX7A23JM.js.map} +0 -0
- /package/dist/{chunk-PB4SXWBA.js.map → chunk-KXFPJOPL.js.map} +0 -0
- /package/dist/{chunk-Y2ARXXPF.js.map → chunk-NNYVXSQF.js.map} +0 -0
- /package/dist/{chunk-K6VACGLH.js.map → chunk-NQA77WXP.js.map} +0 -0
- /package/dist/{chunk-7L6KI365.js.map → chunk-O24ENDHN.js.map} +0 -0
- /package/dist/{chunk-M6U6UHFA.js.map → chunk-OOIZ3IQD.js.map} +0 -0
- /package/dist/{chunk-U33OOJOK.js.map → chunk-P6D7S7CY.js.map} +0 -0
- /package/dist/{chunk-B3PR26HR.js.map → chunk-Q75P5CVJ.js.map} +0 -0
- /package/dist/{chunk-P5BZ3DNM.js.map → chunk-QCDYJYGB.js.map} +0 -0
- /package/dist/{chunk-3EK4V5JS.js.map → chunk-S6MNVNQB.js.map} +0 -0
- /package/dist/{chunk-Z33AE45S.js.map → chunk-V66MMBO4.js.map} +0 -0
- /package/dist/{chunk-WKZSEYV7.js.map → chunk-XARB7ITD.js.map} +0 -0
- /package/dist/{chunk-PBXABL7H.js.map → chunk-XNNNDQLK.js.map} +0 -0
- /package/dist/{chunk-VYZRGIY5.js.map → chunk-XWPZZEBX.js.map} +0 -0
- /package/dist/{chunk-ILGHDIYO.js.map → chunk-YG73EH43.js.map} +0 -0
- /package/dist/{chunk-23DTC2UV.js.map → chunk-YPJRVA2H.js.map} +0 -0
- /package/dist/{chunk-ARWTQGSY.js.map → chunk-ZEOHUE2N.js.map} +0 -0
- /package/dist/{chunk-KC3AZWVQ.js.map → chunk-ZSFRB2S4.js.map} +0 -0
- /package/dist/{cli-circuit-breaker-TOE7GRYI.js.map → cli-circuit-breaker-4ASNTBDP.js.map} +0 -0
- /package/dist/{composite-router-UD3FGSSS.js.map → composite-router-V45AK6WD.js.map} +0 -0
- /package/dist/{consensus-vote-YW6LAIH7.js.map → consensus-vote-N2Z7FX3D.js.map} +0 -0
- /package/dist/{context-retriever-CH3GEH2Z.js.map → context-retriever-NZOSGG6S.js.map} +0 -0
- /package/dist/{doctor-deep-LMEUFKDA.js.map → doctor-deep-JRY3D7BP.js.map} +0 -0
- /package/dist/{doctor-live-O25NTC5D.js.map → doctor-live-NSRHMHMG.js.map} +0 -0
- /package/dist/{expert-bridge-ZQSYVUC2.js.map → expert-bridge-Q4SEPVTE.js.map} +0 -0
- /package/dist/{factory-O4U4BJFT.js.map → factory-JBK45P7G.js.map} +0 -0
- /package/dist/{factory-ZOM2QJPC.js.map → factory-ZV4MR3VM.js.map} +0 -0
- /package/dist/{improvement-review-RL6VHCCY.js.map → improvement-review-KHTIDV4R.js.map} +0 -0
- /package/dist/{init-opencode-RMMHBIQ2.js.map → init-opencode-6WGN5J6J.js.map} +0 -0
- /package/dist/{issue-triage-DQAOBZEP.js.map → issue-triage-XVDXOZRW.js.map} +0 -0
- /package/dist/{learning-persistence-7BEPQEGN.js.map → learning-persistence-ER7E5Q6T.js.map} +0 -0
- /package/dist/{pr-reviewer-helpers-S53B7TYK.js.map → pr-reviewer-helpers-UMNMOUAO.js.map} +0 -0
- /package/dist/{registry-command-TWHRKWDB.js.map → registry-command-QRBAKWRK.js.map} +0 -0
- /package/dist/{repo-security-plan-HYJ7HTOS.js.map → repo-security-plan-UM6WX5PN.js.map} +0 -0
- /package/dist/{research-helpers-synthesize-G3WV7AHW.js.map → research-helpers-synthesize-DUOVXI5V.js.map} +0 -0
- /package/dist/{routing-memory-HQTEZF2S.js.map → routing-memory-OKYSDPXT.js.map} +0 -0
- /package/dist/{session-memory-M5MEGJJZ.js.map → session-memory-ONUSRMYV.js.map} +0 -0
- /package/dist/{setup-config-L5QGPDH6.js.map → setup-config-BAZRBFN3.js.map} +0 -0
- /package/dist/{setup-custom-api-LRL6J7RN.js.map → setup-custom-api-LSMLWGKW.js.map} +0 -0
- /package/dist/{setup-data-dir-WZXMTSDN.js.map → setup-data-dir-7PDX3R3Y.js.map} +0 -0
- /package/dist/{tool-memory-24PMDECK.js.map → tool-memory-Q5UWNVN3.js.map} +0 -0
- /package/dist/{unified-registry-NODUOBMA.js.map → unified-registry-Y327PN6R.js.map} +0 -0
- /package/dist/{weather-report-IGEXNIWL.js.map → weather-report-C2VIAC2Q.js.map} +0 -0
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
ensureLearningDir,
|
|
3
3
|
getDecisionCostFile,
|
|
4
4
|
getModelSelectionShadowFile,
|
|
5
|
-
isPersistenceEnabled
|
|
6
|
-
|
|
5
|
+
isPersistenceEnabled,
|
|
6
|
+
isStrategyDistillationEnabled
|
|
7
|
+
} from "./chunk-RQLIPUQK.js";
|
|
7
8
|
import {
|
|
8
9
|
getNexusDataDir,
|
|
9
10
|
nexusDataPath,
|
|
@@ -1303,9 +1304,9 @@ var DEFAULT_MODEL_CAPABILITIES = {
|
|
|
1303
1304
|
},
|
|
1304
1305
|
{
|
|
1305
1306
|
id: "codex-5.2",
|
|
1306
|
-
displayName: "GPT-5.
|
|
1307
|
+
displayName: "GPT-5.6 Luna",
|
|
1307
1308
|
provider: "openai",
|
|
1308
|
-
contextWindow:
|
|
1309
|
+
contextWindow: 105e4,
|
|
1309
1310
|
outputModalities: ["text", "structured_json", "code"],
|
|
1310
1311
|
inputModalities: ["text", "image", "pdf", "code"],
|
|
1311
1312
|
toolCapabilities: [
|
|
@@ -1322,33 +1323,41 @@ var DEFAULT_MODEL_CAPABILITIES = {
|
|
|
1322
1323
|
"No native image/audio/video generation",
|
|
1323
1324
|
"Network access restricted in sandbox"
|
|
1324
1325
|
],
|
|
1325
|
-
//
|
|
1326
|
-
// serves (~/.codex/models_cache.json visibility=list
|
|
1327
|
-
//
|
|
1328
|
-
//
|
|
1329
|
-
//
|
|
1330
|
-
// this
|
|
1326
|
+
// 2026-09-23: repointed from gpt-5.3-codex-spark, which codex-cli 0.155.1
|
|
1327
|
+
// no longer serves (~/.codex/models_cache.json visibility=list; the
|
|
1328
|
+
// verify Codex Models check reported it). Same shape as #5091 option A:
|
|
1329
|
+
// the id stays (it holds the arm's outcome history), only the slug and
|
|
1330
|
+
// the metadata describing it move.
|
|
1331
|
+
// WHY gpt-5.6-luna: this id is the codex fast/spark tier. The served
|
|
1332
|
+
// fast models are the two "luna" variants (codex catalog: gpt-5.6-luna
|
|
1333
|
+
// "Older fast and efficient model"; gpt-6-luna "Fast and affordable model
|
|
1334
|
+
// for easier tasks"). gpt-6-luna is the cheaper of the two, so it takes
|
|
1335
|
+
// the mini id; this id takes gpt-5.6-luna.
|
|
1336
|
+
// Context window, max output and pricing are models.dev's figures for
|
|
1337
|
+
// this slug (snapshot 2026-09-21); the codex cache reports a 272K default
|
|
1338
|
+
// window. reasoning/codeGeneration/speed are tier positions CARRIED OVER
|
|
1339
|
+
// from the replaced entry, not a measurement of this slug; `cost` follows
|
|
1340
|
+
// the registry's price scale (9, as for $0.3/$1 openrouter-qwen-coder;
|
|
1341
|
+
// only the free model scores 10) — spark's 7 described a $1.75/$14 slug.
|
|
1331
1342
|
// ROUTING CONSEQUENCE: `resolve-model-for-tier` ranks the balanced tier
|
|
1332
1343
|
// by codeGeneration and breaks the 10/10/10 tie on the higher `cost`
|
|
1333
|
-
// score, so this entry (cost
|
|
1334
|
-
// codex-5.3 (5) and gpt-5.5 (4).
|
|
1335
|
-
//
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
qualityScores: { reasoning: 9, codeGeneration: 10, speed: 8, cost: 7 },
|
|
1341
|
-
maxOutputTokens: 32e3,
|
|
1344
|
+
// score, so this entry (now cost 9) still wins the balanced codex tier
|
|
1345
|
+
// over codex-5.3 (5) and gpt-5.5 (4). `resolve-model-for-tier.test.ts`
|
|
1346
|
+
// pins it so a rescore is a visible decision, not a side effect.
|
|
1347
|
+
notes: 'GPT-5.6 Luna (models.dev 2026-09-21; codex catalog: "older fast and efficient model"); fast codex tier; served by codex-cli 0.155.1; 1.05M context (codex cache default window 272K)',
|
|
1348
|
+
pricing: { inputPer1M: 0.2, outputPer1M: 1.2 },
|
|
1349
|
+
qualityScores: { reasoning: 9, codeGeneration: 10, speed: 8, cost: 9 },
|
|
1350
|
+
maxOutputTokens: 128e3,
|
|
1342
1351
|
cliName: "codex",
|
|
1343
|
-
cliModelName: "gpt-5.
|
|
1352
|
+
cliModelName: "gpt-5.6-luna",
|
|
1344
1353
|
unsupportedParameters: ["temperature"],
|
|
1345
1354
|
maxTokensParam: "max_completion_tokens"
|
|
1346
1355
|
},
|
|
1347
1356
|
{
|
|
1348
1357
|
id: "codex-5.1-mini",
|
|
1349
|
-
displayName: "GPT-
|
|
1358
|
+
displayName: "GPT-6 Luna",
|
|
1350
1359
|
provider: "openai",
|
|
1351
|
-
contextWindow:
|
|
1360
|
+
contextWindow: 105e4,
|
|
1352
1361
|
outputModalities: ["text", "structured_json", "code"],
|
|
1353
1362
|
inputModalities: ["text", "image", "code"],
|
|
1354
1363
|
toolCapabilities: [
|
|
@@ -1360,19 +1369,25 @@ var DEFAULT_MODEL_CAPABILITIES = {
|
|
|
1360
1369
|
],
|
|
1361
1370
|
specialFeatures: ["streaming"],
|
|
1362
1371
|
constraints: ["Sandboxed execution only", "No native image/audio/video generation"],
|
|
1363
|
-
//
|
|
1364
|
-
// (~/.codex/models_cache.json visibility=list)
|
|
1365
|
-
//
|
|
1366
|
-
//
|
|
1367
|
-
//
|
|
1368
|
-
//
|
|
1369
|
-
//
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
+
// 2026-09-23: repointed from gpt-5.4-mini, which codex-cli 0.155.1 no
|
|
1373
|
+
// longer serves (~/.codex/models_cache.json visibility=list); #5091 had
|
|
1374
|
+
// repointed it from o3-mini. WHY gpt-6-luna: it is the smallest served
|
|
1375
|
+
// model — $0.1/$0.5 per 1M, below gpt-5.6-luna's $0.2/$1.2 — and the
|
|
1376
|
+
// codex catalog calls it "Fast and affordable model for easier tasks".
|
|
1377
|
+
// SOURCE: the committed models.dev snapshot (2026-09-21) and generated
|
|
1378
|
+
// catalogue (2026-09-20) predate gpt-6-luna. Context window, max output
|
|
1379
|
+
// and pricing are the live models.dev / LiteLLM figures for
|
|
1380
|
+
// `openai/gpt-6-luna` as read by `pnpm build:registry` on 2026-09-23
|
|
1381
|
+
// (LiteLLM: 922K max input); the codex cache reports a 272K default
|
|
1382
|
+
// window. Quality scores are the codex arm's tier positions carried
|
|
1383
|
+
// over, not a measurement of this slug; `cost` stays 9 on the registry's
|
|
1384
|
+
// price scale (only the free model scores 10).
|
|
1385
|
+
notes: 'GPT-6 Luna (models.dev/LiteLLM 2026-09-23; codex catalog: "fast and affordable model for easier tasks"); compact codex tier; served by codex-cli 0.155.1; 1.05M context (codex cache default window 272K)',
|
|
1386
|
+
pricing: { inputPer1M: 0.1, outputPer1M: 0.5 },
|
|
1372
1387
|
qualityScores: { reasoning: 7, codeGeneration: 8, speed: 9, cost: 9 },
|
|
1373
1388
|
maxOutputTokens: 128e3,
|
|
1374
1389
|
cliName: "codex",
|
|
1375
|
-
cliModelName: "gpt-
|
|
1390
|
+
cliModelName: "gpt-6-luna",
|
|
1376
1391
|
unsupportedParameters: ["temperature"],
|
|
1377
1392
|
maxTokensParam: "max_completion_tokens"
|
|
1378
1393
|
},
|
|
@@ -2628,7 +2643,7 @@ function setDefaultRegistry(registry) {
|
|
|
2628
2643
|
}
|
|
2629
2644
|
async function reloadDefaultRegistry() {
|
|
2630
2645
|
globalRegistry = buildDefaultRegistry();
|
|
2631
|
-
const { resetGlobalRegistry } = await import("./unified-registry-
|
|
2646
|
+
const { resetGlobalRegistry } = await import("./unified-registry-Y327PN6R.js");
|
|
2632
2647
|
resetGlobalRegistry();
|
|
2633
2648
|
return globalRegistry;
|
|
2634
2649
|
}
|
|
@@ -9641,20 +9656,40 @@ var DistilledRuleStage = class {
|
|
|
9641
9656
|
config;
|
|
9642
9657
|
logger;
|
|
9643
9658
|
rulesAppliedCount = 0;
|
|
9659
|
+
firstRunChecked = false;
|
|
9644
9660
|
constructor(distiller, config, logger15) {
|
|
9645
9661
|
this.distiller = distiller;
|
|
9646
9662
|
this.config = { ...DEFAULT_CONFIG5, ...config };
|
|
9647
9663
|
this.logger = logger15 ?? createLogger({ component: "DistilledRuleStage" });
|
|
9648
9664
|
}
|
|
9649
9665
|
canHandle(ctx) {
|
|
9666
|
+
this.checkPersistedTriggerOnce();
|
|
9650
9667
|
const candidates = getRemainingCandidates(ctx);
|
|
9651
9668
|
if (candidates.length <= 1) return false;
|
|
9652
9669
|
const activeRules = this.distiller.getRules("active");
|
|
9653
9670
|
return activeRules.length > 0;
|
|
9654
9671
|
}
|
|
9655
9672
|
route(ctx) {
|
|
9673
|
+
this.checkPersistedTriggerOnce();
|
|
9656
9674
|
return Promise.resolve(this.routeSync(ctx));
|
|
9657
9675
|
}
|
|
9676
|
+
/**
|
|
9677
|
+
* The first time this stage runs, let the distiller distill from the
|
|
9678
|
+
* persisted outcome store (#6512). Without it, rules only ever came from 50
|
|
9679
|
+
* outcomes seen inside one process, which short-lived CLI processes never
|
|
9680
|
+
* reach. A failure here is logged and routing continues without rules.
|
|
9681
|
+
*/
|
|
9682
|
+
checkPersistedTriggerOnce() {
|
|
9683
|
+
if (this.firstRunChecked) return;
|
|
9684
|
+
this.firstRunChecked = true;
|
|
9685
|
+
try {
|
|
9686
|
+
this.distiller.checkPersistedTrigger();
|
|
9687
|
+
} catch (error) {
|
|
9688
|
+
this.logger.warn("Persisted-store distill trigger failed; routing without new rules", {
|
|
9689
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9690
|
+
});
|
|
9691
|
+
}
|
|
9692
|
+
}
|
|
9658
9693
|
recordOutcome(outcome) {
|
|
9659
9694
|
this.distiller.onOutcome();
|
|
9660
9695
|
this.logger.debug("Forwarded outcome to distiller", {
|
|
@@ -10182,367 +10217,58 @@ var DEFAULT_DISTILLER_CONFIG = {
|
|
|
10182
10217
|
ruleExpiryMs: 24 * 60 * 60 * 1e3
|
|
10183
10218
|
};
|
|
10184
10219
|
|
|
10185
|
-
// src/learning/
|
|
10186
|
-
|
|
10187
|
-
|
|
10188
|
-
|
|
10189
|
-
}
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
|
|
10199
|
-
|
|
10200
|
-
|
|
10201
|
-
|
|
10202
|
-
|
|
10203
|
-
|
|
10204
|
-
|
|
10205
|
-
|
|
10206
|
-
|
|
10207
|
-
|
|
10208
|
-
|
|
10209
|
-
|
|
10210
|
-
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
10229
|
-
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
|
|
10233
|
-
const idx = Math.ceil(p / 100 * sorted.length) - 1;
|
|
10234
|
-
return sorted[Math.max(0, idx)] ?? 0;
|
|
10235
|
-
}
|
|
10236
|
-
function detectFailurePatterns(groups, threshold) {
|
|
10237
|
-
const patterns = [];
|
|
10238
|
-
for (const g of groups) {
|
|
10239
|
-
const failCount = g.outcomes.filter((o) => !o.success).length;
|
|
10240
|
-
const failRate = failCount / g.outcomes.length;
|
|
10241
|
-
if (failRate >= threshold) {
|
|
10242
|
-
patterns.push({
|
|
10243
|
-
cli: g.cli,
|
|
10244
|
-
category: g.category,
|
|
10245
|
-
patternType: "failure-rate",
|
|
10246
|
-
action: failRate >= 0.8 ? "avoid" : "penalize",
|
|
10247
|
-
metric: failRate,
|
|
10248
|
-
observationCount: g.outcomes.length
|
|
10249
|
-
});
|
|
10250
|
-
}
|
|
10251
|
-
}
|
|
10252
|
-
return patterns;
|
|
10253
|
-
}
|
|
10254
|
-
function detectSuccessPatterns(groups, threshold) {
|
|
10255
|
-
const patterns = [];
|
|
10256
|
-
for (const g of groups) {
|
|
10257
|
-
const successCount = g.outcomes.filter((o) => o.success).length;
|
|
10258
|
-
const successRate3 = successCount / g.outcomes.length;
|
|
10259
|
-
if (successRate3 >= threshold) {
|
|
10260
|
-
patterns.push({
|
|
10261
|
-
cli: g.cli,
|
|
10262
|
-
category: g.category,
|
|
10263
|
-
patternType: "success-rate",
|
|
10264
|
-
action: "boost",
|
|
10265
|
-
metric: successRate3,
|
|
10266
|
-
observationCount: g.outcomes.length
|
|
10267
|
-
});
|
|
10268
|
-
}
|
|
10269
|
-
}
|
|
10270
|
-
return patterns;
|
|
10271
|
-
}
|
|
10272
|
-
function detectLatencyPatterns(groups, threshold) {
|
|
10273
|
-
const patterns = [];
|
|
10274
|
-
for (const g of groups) {
|
|
10275
|
-
const durations = g.outcomes.map((o) => o.durationMs).sort((a, b) => a - b);
|
|
10276
|
-
if (durations.length < 3) continue;
|
|
10277
|
-
const median = percentile(durations, 50);
|
|
10278
|
-
const p90 = percentile(durations, 90);
|
|
10279
|
-
if (median <= 0) continue;
|
|
10280
|
-
const ratio = p90 / median;
|
|
10281
|
-
if (ratio >= threshold) {
|
|
10282
|
-
patterns.push({
|
|
10283
|
-
cli: g.cli,
|
|
10284
|
-
category: g.category,
|
|
10285
|
-
patternType: "latency-spike",
|
|
10286
|
-
action: "penalize",
|
|
10287
|
-
metric: ratio,
|
|
10288
|
-
observationCount: g.outcomes.length
|
|
10289
|
-
});
|
|
10290
|
-
}
|
|
10291
|
-
}
|
|
10292
|
-
return patterns;
|
|
10293
|
-
}
|
|
10294
|
-
var StrategyDistiller = class {
|
|
10295
|
-
/** Protected so `PersistentStrategyDistiller` hydrates legacy rules under the live thresholds. */
|
|
10296
|
-
config;
|
|
10297
|
-
outcomeStore;
|
|
10298
|
-
logger;
|
|
10299
|
-
rules = /* @__PURE__ */ new Map();
|
|
10300
|
-
outcomeCounter = 0;
|
|
10301
|
-
lastDistillAt;
|
|
10302
|
-
constructor(outcomeStore, logger15, config) {
|
|
10303
|
-
this.outcomeStore = outcomeStore;
|
|
10304
|
-
this.config = { ...DEFAULT_DISTILLER_CONFIG, ...config };
|
|
10305
|
-
this.logger = logger15 ?? createLogger({ component: "StrategyDistiller" });
|
|
10306
|
-
}
|
|
10307
|
-
/** Called for each processed outcome. Triggers distillation at threshold. */
|
|
10308
|
-
onOutcome() {
|
|
10309
|
-
this.outcomeCounter++;
|
|
10310
|
-
if (this.outcomeCounter >= this.config.triggerThreshold) {
|
|
10311
|
-
this.distill();
|
|
10312
|
-
this.outcomeCounter = 0;
|
|
10313
|
-
}
|
|
10314
|
-
}
|
|
10315
|
-
/** Run distillation on current OutcomeStore data. */
|
|
10316
|
-
distill() {
|
|
10317
|
-
const now = getTimeProvider().now();
|
|
10318
|
-
const outcomes = this.outcomeStore.query();
|
|
10319
|
-
const groups = groupOutcomes(outcomes);
|
|
10320
|
-
this.expireRules(now);
|
|
10321
|
-
const failurePatterns = detectFailurePatterns(groups, this.config.failureRateThreshold);
|
|
10322
|
-
const successPatterns = detectSuccessPatterns(groups, this.config.successRateThreshold);
|
|
10323
|
-
const latencyPatterns = detectLatencyPatterns(groups, this.config.latencyRatioThreshold);
|
|
10324
|
-
const allPatterns = [...failurePatterns, ...successPatterns, ...latencyPatterns];
|
|
10325
|
-
for (const pattern of allPatterns) {
|
|
10326
|
-
this.upsertRule(pattern, now);
|
|
10327
|
-
}
|
|
10328
|
-
this.enforceMaxRules();
|
|
10329
|
-
this.lastDistillAt = now;
|
|
10330
|
-
this.logger.debug("Distillation complete", {
|
|
10331
|
-
rulesTotal: this.rules.size,
|
|
10332
|
-
patternsFound: allPatterns.length,
|
|
10333
|
-
outcomesAnalyzed: outcomes.length
|
|
10334
|
-
});
|
|
10335
|
-
}
|
|
10336
|
-
/** Get rules filtered by status. */
|
|
10337
|
-
getRules(status) {
|
|
10338
|
-
const all = [...this.rules.values()];
|
|
10339
|
-
if (status === void 0) return all;
|
|
10340
|
-
return all.filter((r) => r.status === status);
|
|
10341
|
-
}
|
|
10342
|
-
/** Get distiller statistics. */
|
|
10343
|
-
getStats() {
|
|
10344
|
-
const countByStatus = {
|
|
10345
|
-
draft: 0,
|
|
10346
|
-
active: 0,
|
|
10347
|
-
promoted: 0,
|
|
10348
|
-
expired: 0
|
|
10349
|
-
};
|
|
10350
|
-
for (const rule of this.rules.values()) {
|
|
10351
|
-
countByStatus[rule.status]++;
|
|
10352
|
-
}
|
|
10353
|
-
return {
|
|
10354
|
-
ruleCountByStatus: countByStatus,
|
|
10355
|
-
totalRules: this.rules.size,
|
|
10356
|
-
lastDistillAt: this.lastDistillAt,
|
|
10357
|
-
outcomesSinceLastDistill: this.outcomeCounter
|
|
10358
|
-
};
|
|
10359
|
-
}
|
|
10220
|
+
// src/learning/distiller-eligibility.ts
|
|
10221
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
|
|
10222
|
+
|
|
10223
|
+
// src/orchestration/outcomes/outcome-types.ts
|
|
10224
|
+
import { z as z14 } from "zod";
|
|
10225
|
+
var logger8 = createLogger({ component: "outcome-error-taxonomy" });
|
|
10226
|
+
var OutcomeSourceSchema = z14.enum(["delegate", "consensus", "manual"]);
|
|
10227
|
+
var OutcomeFailureCategorySchema = z14.enum([
|
|
10228
|
+
"timeout",
|
|
10229
|
+
"authentication",
|
|
10230
|
+
"rate_limit",
|
|
10231
|
+
"connection",
|
|
10232
|
+
"crash",
|
|
10233
|
+
"adapter_unavailable",
|
|
10234
|
+
"validation",
|
|
10235
|
+
"parse",
|
|
10236
|
+
"execution",
|
|
10237
|
+
"generic",
|
|
10238
|
+
"unknown"
|
|
10239
|
+
]);
|
|
10240
|
+
var OutcomeCliSchema = z14.union([CliNameSchema, ApiArmIdSchema, z14.literal("unknown")]);
|
|
10241
|
+
var OutcomeCliSourceSchema = z14.enum(["executed", "category-default"]);
|
|
10242
|
+
var TaskOutcomeSchema = z14.object({
|
|
10243
|
+
id: z14.string().min(1),
|
|
10244
|
+
cli: OutcomeCliSchema,
|
|
10245
|
+
/** How the CLI attribution was obtained. Absent on legacy, unmeasured records. */
|
|
10246
|
+
cliSource: OutcomeCliSourceSchema.optional(),
|
|
10247
|
+
category: TaskCategorySchema,
|
|
10248
|
+
model: z14.string().min(1),
|
|
10249
|
+
success: z14.boolean(),
|
|
10250
|
+
durationMs: z14.number().nonnegative(),
|
|
10251
|
+
timestamp: z14.string().min(1),
|
|
10252
|
+
qualitySignals: z14.array(z14.string()).optional(),
|
|
10253
|
+
failureCategory: OutcomeFailureCategorySchema.optional(),
|
|
10254
|
+
errorMessage: z14.string().max(500).optional(),
|
|
10255
|
+
source: OutcomeSourceSchema,
|
|
10256
|
+
/** Whether this outcome came from a triage-initiated retry (#1506). */
|
|
10257
|
+
wasRetried: z14.boolean().optional(),
|
|
10258
|
+
/** Triage action taken on the failure (#1506). */
|
|
10259
|
+
triageAction: z14.string().max(30).optional(),
|
|
10260
|
+
/** Routing stage that selected this CLI (#1785). */
|
|
10261
|
+
routingStage: z14.string().max(50).optional(),
|
|
10262
|
+
/** Number of retry attempts before this outcome (#1785). */
|
|
10263
|
+
retryCount: z14.number().int().nonnegative().optional(),
|
|
10264
|
+
/** Vendor resolved from `model` via ModelRegistry at write time (#2548). */
|
|
10265
|
+
vendor: z14.string().min(1).max(40).optional(),
|
|
10266
|
+
/** Family resolved from `model` via ModelRegistry at write time (#2548). */
|
|
10267
|
+
family: z14.string().min(1).max(80).optional(),
|
|
10360
10268
|
/**
|
|
10361
|
-
*
|
|
10362
|
-
*
|
|
10363
|
-
*
|
|
10364
|
-
* @deprecated No production caller (#5004 finding 4). `DistilledRuleStage`
|
|
10365
|
-
* is the single channel by which distilled rules reach routing; this
|
|
10366
|
-
* second channel into `RoutingMemory` is kept only so the deprecation is
|
|
10367
|
-
* non-breaking. Removal is tracked in #5467. Note the gate now compares
|
|
10368
|
-
* `confidence = support × effect` against `promotionConfidence`, so a rule
|
|
10369
|
-
* that would have promoted on sample size alone may no longer clear it.
|
|
10370
|
-
*/
|
|
10371
|
-
promote(routingMemory) {
|
|
10372
|
-
let promoted = 0;
|
|
10373
|
-
for (const [id, rule] of this.rules) {
|
|
10374
|
-
if (rule.status !== "active") continue;
|
|
10375
|
-
if (rule.observationCount < this.config.minObservationsForActive) continue;
|
|
10376
|
-
if (rule.confidence < this.config.promotionConfidence) continue;
|
|
10377
|
-
const performance2 = this.ruleToPerformance(rule);
|
|
10378
|
-
routingMemory.storePreference(rule.cli, rule.category, performance2);
|
|
10379
|
-
this.rules.set(id, { ...rule, status: "promoted", updatedAt: getTimeProvider().now() });
|
|
10380
|
-
promoted++;
|
|
10381
|
-
this.logger.info("Promoted distilled rule to RoutingMemory", {
|
|
10382
|
-
ruleId: id,
|
|
10383
|
-
cli: rule.cli,
|
|
10384
|
-
category: rule.category,
|
|
10385
|
-
confidence: rule.confidence
|
|
10386
|
-
});
|
|
10387
|
-
}
|
|
10388
|
-
return promoted;
|
|
10389
|
-
}
|
|
10390
|
-
// ==========================================================================
|
|
10391
|
-
// Protected — for subclass hydration (Issue #1009)
|
|
10392
|
-
// ==========================================================================
|
|
10393
|
-
/** Load pre-existing rules (e.g., from disk). Used by PersistentStrategyDistiller. */
|
|
10394
|
-
loadRules(rules) {
|
|
10395
|
-
for (const rule of rules) {
|
|
10396
|
-
this.rules.set(rule.id, rule);
|
|
10397
|
-
}
|
|
10398
|
-
}
|
|
10399
|
-
// ==========================================================================
|
|
10400
|
-
// Private
|
|
10401
|
-
// ==========================================================================
|
|
10402
|
-
upsertRule(pattern, now) {
|
|
10403
|
-
const id = ruleFingerprint(pattern.patternType, pattern.cli, pattern.category);
|
|
10404
|
-
const existing = this.rules.get(id);
|
|
10405
|
-
if (existing === void 0 && pattern.observationCount < this.config.minObservationsForDraft) {
|
|
10406
|
-
return;
|
|
10407
|
-
}
|
|
10408
|
-
const support = sigmoidConfidence(pattern.observationCount);
|
|
10409
|
-
const effect = effectFor(pattern.patternType, pattern.metric, this.config);
|
|
10410
|
-
const confidence = support * effect;
|
|
10411
|
-
const status = this.computeStatus(pattern.observationCount, existing?.status);
|
|
10412
|
-
if (existing !== void 0) {
|
|
10413
|
-
this.rules.set(id, {
|
|
10414
|
-
...existing,
|
|
10415
|
-
action: pattern.action,
|
|
10416
|
-
confidence,
|
|
10417
|
-
support,
|
|
10418
|
-
effect,
|
|
10419
|
-
observationCount: pattern.observationCount,
|
|
10420
|
-
metric: pattern.metric,
|
|
10421
|
-
status,
|
|
10422
|
-
updatedAt: now
|
|
10423
|
-
});
|
|
10424
|
-
} else {
|
|
10425
|
-
this.rules.set(id, {
|
|
10426
|
-
id,
|
|
10427
|
-
patternType: pattern.patternType,
|
|
10428
|
-
cli: pattern.cli,
|
|
10429
|
-
category: pattern.category,
|
|
10430
|
-
action: pattern.action,
|
|
10431
|
-
confidence,
|
|
10432
|
-
support,
|
|
10433
|
-
effect,
|
|
10434
|
-
observationCount: pattern.observationCount,
|
|
10435
|
-
metric: pattern.metric,
|
|
10436
|
-
status,
|
|
10437
|
-
createdAt: now,
|
|
10438
|
-
updatedAt: now,
|
|
10439
|
-
tainted: false
|
|
10440
|
-
});
|
|
10441
|
-
}
|
|
10442
|
-
}
|
|
10443
|
-
computeStatus(observations, existing) {
|
|
10444
|
-
if (existing === "promoted") return "promoted";
|
|
10445
|
-
if (observations >= this.config.minObservationsForActive) return "active";
|
|
10446
|
-
return "draft";
|
|
10447
|
-
}
|
|
10448
|
-
expireRules(now) {
|
|
10449
|
-
for (const [id, rule] of this.rules) {
|
|
10450
|
-
if (rule.status === "promoted") continue;
|
|
10451
|
-
if (now - rule.updatedAt > this.config.ruleExpiryMs) {
|
|
10452
|
-
this.rules.set(id, { ...rule, status: "expired", updatedAt: now });
|
|
10453
|
-
}
|
|
10454
|
-
}
|
|
10455
|
-
}
|
|
10456
|
-
enforceMaxRules() {
|
|
10457
|
-
if (this.rules.size <= this.config.maxRules) return;
|
|
10458
|
-
const sorted = [...this.rules.entries()].sort((a, b) => {
|
|
10459
|
-
if (a[1].status === "expired" && b[1].status !== "expired") return -1;
|
|
10460
|
-
if (b[1].status === "expired" && a[1].status !== "expired") return 1;
|
|
10461
|
-
return a[1].confidence - b[1].confidence;
|
|
10462
|
-
});
|
|
10463
|
-
const excess = this.rules.size - this.config.maxRules;
|
|
10464
|
-
for (let i = 0; i < excess; i++) {
|
|
10465
|
-
const entry = sorted[i];
|
|
10466
|
-
if (entry !== void 0) {
|
|
10467
|
-
this.rules.delete(entry[0]);
|
|
10468
|
-
}
|
|
10469
|
-
}
|
|
10470
|
-
}
|
|
10471
|
-
/** Convert rule metrics into ModelPerformance for RoutingMemory. */
|
|
10472
|
-
ruleToPerformance(rule) {
|
|
10473
|
-
const successRate3 = rule.patternType === "success-rate" ? rule.metric : 1 - rule.metric;
|
|
10474
|
-
return {
|
|
10475
|
-
avgQuality: rule.confidence,
|
|
10476
|
-
successRate: Math.max(0, Math.min(1, successRate3)),
|
|
10477
|
-
avgLatencyMs: 0,
|
|
10478
|
-
avgTokens: 0,
|
|
10479
|
-
observations: rule.observationCount
|
|
10480
|
-
};
|
|
10481
|
-
}
|
|
10482
|
-
};
|
|
10483
|
-
function createStrategyDistiller(outcomeStore, logger15, config) {
|
|
10484
|
-
return new StrategyDistiller(outcomeStore, logger15, config);
|
|
10485
|
-
}
|
|
10486
|
-
var persistentDistillerFactory;
|
|
10487
|
-
function registerPersistentDistillerFactory(factory) {
|
|
10488
|
-
persistentDistillerFactory = factory;
|
|
10489
|
-
}
|
|
10490
|
-
function createPersistentDistillerOrFallback(outcomeStore, logger15) {
|
|
10491
|
-
if (persistentDistillerFactory !== void 0) {
|
|
10492
|
-
return persistentDistillerFactory(outcomeStore, logger15);
|
|
10493
|
-
}
|
|
10494
|
-
return new StrategyDistiller(outcomeStore, logger15);
|
|
10495
|
-
}
|
|
10496
|
-
|
|
10497
|
-
// src/orchestration/outcomes/outcome-types.ts
|
|
10498
|
-
import { z as z14 } from "zod";
|
|
10499
|
-
var logger8 = createLogger({ component: "outcome-error-taxonomy" });
|
|
10500
|
-
var OutcomeSourceSchema = z14.enum(["delegate", "consensus", "manual"]);
|
|
10501
|
-
var OutcomeFailureCategorySchema = z14.enum([
|
|
10502
|
-
"timeout",
|
|
10503
|
-
"authentication",
|
|
10504
|
-
"rate_limit",
|
|
10505
|
-
"connection",
|
|
10506
|
-
"crash",
|
|
10507
|
-
"adapter_unavailable",
|
|
10508
|
-
"validation",
|
|
10509
|
-
"parse",
|
|
10510
|
-
"execution",
|
|
10511
|
-
"generic",
|
|
10512
|
-
"unknown"
|
|
10513
|
-
]);
|
|
10514
|
-
var OutcomeCliSchema = z14.union([CliNameSchema, ApiArmIdSchema, z14.literal("unknown")]);
|
|
10515
|
-
var OutcomeCliSourceSchema = z14.enum(["executed", "category-default"]);
|
|
10516
|
-
var TaskOutcomeSchema = z14.object({
|
|
10517
|
-
id: z14.string().min(1),
|
|
10518
|
-
cli: OutcomeCliSchema,
|
|
10519
|
-
/** How the CLI attribution was obtained. Absent on legacy, unmeasured records. */
|
|
10520
|
-
cliSource: OutcomeCliSourceSchema.optional(),
|
|
10521
|
-
category: TaskCategorySchema,
|
|
10522
|
-
model: z14.string().min(1),
|
|
10523
|
-
success: z14.boolean(),
|
|
10524
|
-
durationMs: z14.number().nonnegative(),
|
|
10525
|
-
timestamp: z14.string().min(1),
|
|
10526
|
-
qualitySignals: z14.array(z14.string()).optional(),
|
|
10527
|
-
failureCategory: OutcomeFailureCategorySchema.optional(),
|
|
10528
|
-
errorMessage: z14.string().max(500).optional(),
|
|
10529
|
-
source: OutcomeSourceSchema,
|
|
10530
|
-
/** Whether this outcome came from a triage-initiated retry (#1506). */
|
|
10531
|
-
wasRetried: z14.boolean().optional(),
|
|
10532
|
-
/** Triage action taken on the failure (#1506). */
|
|
10533
|
-
triageAction: z14.string().max(30).optional(),
|
|
10534
|
-
/** Routing stage that selected this CLI (#1785). */
|
|
10535
|
-
routingStage: z14.string().max(50).optional(),
|
|
10536
|
-
/** Number of retry attempts before this outcome (#1785). */
|
|
10537
|
-
retryCount: z14.number().int().nonnegative().optional(),
|
|
10538
|
-
/** Vendor resolved from `model` via ModelRegistry at write time (#2548). */
|
|
10539
|
-
vendor: z14.string().min(1).max(40).optional(),
|
|
10540
|
-
/** Family resolved from `model` via ModelRegistry at write time (#2548). */
|
|
10541
|
-
family: z14.string().min(1).max(80).optional(),
|
|
10542
|
-
/**
|
|
10543
|
-
* Voter role for `source: 'consensus'` outcomes (#2662) — `architect`,
|
|
10544
|
-
* `security`, etc. Absent on non-consensus outcomes. Lets the
|
|
10545
|
-
* stratified outcome report break results down by voter role.
|
|
10269
|
+
* Voter role for `source: 'consensus'` outcomes (#2662) — `architect`,
|
|
10270
|
+
* `security`, etc. Absent on non-consensus outcomes. Lets the
|
|
10271
|
+
* stratified outcome report break results down by voter role.
|
|
10546
10272
|
*/
|
|
10547
10273
|
voterRole: z14.string().min(1).max(40).optional(),
|
|
10548
10274
|
/**
|
|
@@ -10766,6 +10492,411 @@ function categorizeOutcomeErrorMessage(msg) {
|
|
|
10766
10492
|
return classifyText(msg.toLowerCase());
|
|
10767
10493
|
}
|
|
10768
10494
|
|
|
10495
|
+
// src/learning/distiller-eligibility.ts
|
|
10496
|
+
var ROUTABLE_CLIS = new Set(CLI_NAMES);
|
|
10497
|
+
function isDistillerEligible(outcome) {
|
|
10498
|
+
if (outcome.source !== "delegate") return false;
|
|
10499
|
+
if (outcome.cliSource !== "executed") return false;
|
|
10500
|
+
if (!(outcome.durationMs > 0)) return false;
|
|
10501
|
+
return ROUTABLE_CLIS.has(outcome.cli);
|
|
10502
|
+
}
|
|
10503
|
+
function countEligibleSince(outcomes, sinceMs) {
|
|
10504
|
+
let count = 0;
|
|
10505
|
+
for (const outcome of outcomes) {
|
|
10506
|
+
if (!isDistillerEligible(outcome)) continue;
|
|
10507
|
+
if (sinceMs !== void 0 && !(Date.parse(outcome.timestamp) > sinceMs)) continue;
|
|
10508
|
+
count++;
|
|
10509
|
+
}
|
|
10510
|
+
return count;
|
|
10511
|
+
}
|
|
10512
|
+
var eligibilityFieldsSchema;
|
|
10513
|
+
function getEligibilityFieldsSchema() {
|
|
10514
|
+
eligibilityFieldsSchema ??= TaskOutcomeSchema.pick({
|
|
10515
|
+
source: true,
|
|
10516
|
+
cli: true,
|
|
10517
|
+
cliSource: true,
|
|
10518
|
+
durationMs: true
|
|
10519
|
+
});
|
|
10520
|
+
return eligibilityFieldsSchema;
|
|
10521
|
+
}
|
|
10522
|
+
function parseLine(line) {
|
|
10523
|
+
try {
|
|
10524
|
+
const parsed = getEligibilityFieldsSchema().safeParse(JSON.parse(line));
|
|
10525
|
+
return parsed.success ? parsed.data : void 0;
|
|
10526
|
+
} catch {
|
|
10527
|
+
return void 0;
|
|
10528
|
+
}
|
|
10529
|
+
}
|
|
10530
|
+
function countEligibleOutcomesInFile(filePath) {
|
|
10531
|
+
if (!existsSync6(filePath)) return 0;
|
|
10532
|
+
let count = 0;
|
|
10533
|
+
for (const line of readFileSync6(filePath, "utf-8").split("\n")) {
|
|
10534
|
+
if (line.trim().length === 0) continue;
|
|
10535
|
+
const fields = parseLine(line);
|
|
10536
|
+
if (fields !== void 0 && isDistillerEligible(fields)) count++;
|
|
10537
|
+
}
|
|
10538
|
+
return count;
|
|
10539
|
+
}
|
|
10540
|
+
|
|
10541
|
+
// src/learning/strategy-distiller.ts
|
|
10542
|
+
function sigmoidConfidence(observations, center = 30) {
|
|
10543
|
+
const raw = 1 / (1 + Math.exp(-(observations - center) / 5));
|
|
10544
|
+
return Math.max(0, Math.min(1, raw));
|
|
10545
|
+
}
|
|
10546
|
+
function clampUnit(value) {
|
|
10547
|
+
if (!Number.isFinite(value)) return 0;
|
|
10548
|
+
return Math.max(0, Math.min(1, value));
|
|
10549
|
+
}
|
|
10550
|
+
function effectFor(patternType, metric, thresholds) {
|
|
10551
|
+
switch (patternType) {
|
|
10552
|
+
case "failure-rate":
|
|
10553
|
+
return clampUnit(
|
|
10554
|
+
(metric - thresholds.failureRateThreshold) / (1 - thresholds.failureRateThreshold)
|
|
10555
|
+
);
|
|
10556
|
+
case "success-rate":
|
|
10557
|
+
return clampUnit(
|
|
10558
|
+
(metric - thresholds.successRateThreshold) / (1 - thresholds.successRateThreshold)
|
|
10559
|
+
);
|
|
10560
|
+
case "latency-spike":
|
|
10561
|
+
return clampUnit(
|
|
10562
|
+
(metric - thresholds.latencyRatioThreshold) / thresholds.latencyRatioThreshold
|
|
10563
|
+
);
|
|
10564
|
+
}
|
|
10565
|
+
}
|
|
10566
|
+
function ruleFingerprint(patternType, cli, category) {
|
|
10567
|
+
return `${patternType}:${cli}:${category}`;
|
|
10568
|
+
}
|
|
10569
|
+
function groupOutcomes(outcomes) {
|
|
10570
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
10571
|
+
for (const o of outcomes) {
|
|
10572
|
+
const key = `${o.cli}:${o.category}`;
|
|
10573
|
+
const list = map2.get(key);
|
|
10574
|
+
if (list !== void 0) {
|
|
10575
|
+
list.push(o);
|
|
10576
|
+
} else {
|
|
10577
|
+
map2.set(key, [o]);
|
|
10578
|
+
}
|
|
10579
|
+
}
|
|
10580
|
+
const groups = [];
|
|
10581
|
+
for (const [key, list] of map2) {
|
|
10582
|
+
const [cli, category] = key.split(":");
|
|
10583
|
+
groups.push({ cli, category, outcomes: list });
|
|
10584
|
+
}
|
|
10585
|
+
return groups;
|
|
10586
|
+
}
|
|
10587
|
+
function percentile(sorted, p) {
|
|
10588
|
+
if (sorted.length === 0) return 0;
|
|
10589
|
+
const idx = Math.ceil(p / 100 * sorted.length) - 1;
|
|
10590
|
+
return sorted[Math.max(0, idx)] ?? 0;
|
|
10591
|
+
}
|
|
10592
|
+
function detectFailurePatterns(groups, threshold) {
|
|
10593
|
+
const patterns = [];
|
|
10594
|
+
for (const g of groups) {
|
|
10595
|
+
const failCount = g.outcomes.filter((o) => !o.success).length;
|
|
10596
|
+
const failRate = failCount / g.outcomes.length;
|
|
10597
|
+
if (failRate >= threshold) {
|
|
10598
|
+
patterns.push({
|
|
10599
|
+
cli: g.cli,
|
|
10600
|
+
category: g.category,
|
|
10601
|
+
patternType: "failure-rate",
|
|
10602
|
+
action: failRate >= 0.8 ? "avoid" : "penalize",
|
|
10603
|
+
metric: failRate,
|
|
10604
|
+
observationCount: g.outcomes.length
|
|
10605
|
+
});
|
|
10606
|
+
}
|
|
10607
|
+
}
|
|
10608
|
+
return patterns;
|
|
10609
|
+
}
|
|
10610
|
+
function detectSuccessPatterns(groups, threshold) {
|
|
10611
|
+
const patterns = [];
|
|
10612
|
+
for (const g of groups) {
|
|
10613
|
+
const successCount = g.outcomes.filter((o) => o.success).length;
|
|
10614
|
+
const successRate3 = successCount / g.outcomes.length;
|
|
10615
|
+
if (successRate3 >= threshold) {
|
|
10616
|
+
patterns.push({
|
|
10617
|
+
cli: g.cli,
|
|
10618
|
+
category: g.category,
|
|
10619
|
+
patternType: "success-rate",
|
|
10620
|
+
action: "boost",
|
|
10621
|
+
metric: successRate3,
|
|
10622
|
+
observationCount: g.outcomes.length
|
|
10623
|
+
});
|
|
10624
|
+
}
|
|
10625
|
+
}
|
|
10626
|
+
return patterns;
|
|
10627
|
+
}
|
|
10628
|
+
function detectLatencyPatterns(groups, threshold) {
|
|
10629
|
+
const patterns = [];
|
|
10630
|
+
for (const g of groups) {
|
|
10631
|
+
const durations = g.outcomes.map((o) => o.durationMs).sort((a, b) => a - b);
|
|
10632
|
+
if (durations.length < 3) continue;
|
|
10633
|
+
const median = percentile(durations, 50);
|
|
10634
|
+
const p90 = percentile(durations, 90);
|
|
10635
|
+
if (median <= 0) continue;
|
|
10636
|
+
const ratio = p90 / median;
|
|
10637
|
+
if (ratio >= threshold) {
|
|
10638
|
+
patterns.push({
|
|
10639
|
+
cli: g.cli,
|
|
10640
|
+
category: g.category,
|
|
10641
|
+
patternType: "latency-spike",
|
|
10642
|
+
action: "penalize",
|
|
10643
|
+
metric: ratio,
|
|
10644
|
+
observationCount: g.outcomes.length
|
|
10645
|
+
});
|
|
10646
|
+
}
|
|
10647
|
+
}
|
|
10648
|
+
return patterns;
|
|
10649
|
+
}
|
|
10650
|
+
var StrategyDistiller = class {
|
|
10651
|
+
/** Protected so `PersistentStrategyDistiller` hydrates legacy rules under the live thresholds. */
|
|
10652
|
+
config;
|
|
10653
|
+
outcomeStore;
|
|
10654
|
+
logger;
|
|
10655
|
+
rules = /* @__PURE__ */ new Map();
|
|
10656
|
+
outcomeCounter = 0;
|
|
10657
|
+
lastDistillAt;
|
|
10658
|
+
eligibleAtLastDistill;
|
|
10659
|
+
persistedTriggerChecked = false;
|
|
10660
|
+
constructor(outcomeStore, logger15, config) {
|
|
10661
|
+
this.outcomeStore = outcomeStore;
|
|
10662
|
+
this.config = { ...DEFAULT_DISTILLER_CONFIG, ...config };
|
|
10663
|
+
this.logger = logger15 ?? createLogger({ component: "StrategyDistiller" });
|
|
10664
|
+
}
|
|
10665
|
+
/**
|
|
10666
|
+
* Called for each processed outcome. Triggers distillation at threshold.
|
|
10667
|
+
* This counter is per-process; `checkPersistedTrigger` covers the
|
|
10668
|
+
* cross-process case (#6512).
|
|
10669
|
+
*/
|
|
10670
|
+
onOutcome() {
|
|
10671
|
+
this.outcomeCounter++;
|
|
10672
|
+
if (this.outcomeCounter >= this.config.triggerThreshold) {
|
|
10673
|
+
this.distill();
|
|
10674
|
+
this.outcomeCounter = 0;
|
|
10675
|
+
}
|
|
10676
|
+
}
|
|
10677
|
+
/**
|
|
10678
|
+
* First-route trigger against the persisted store (#6512).
|
|
10679
|
+
*
|
|
10680
|
+
* The in-process counter above only fires after `triggerThreshold` outcomes
|
|
10681
|
+
* in ONE process, which short-lived CLI processes never reach. This checks
|
|
10682
|
+
* the store instead: distill when it holds at least `triggerThreshold`
|
|
10683
|
+
* eligible outcomes newer than the last distill (a hydrated snapshot's
|
|
10684
|
+
* `savedAt`; no snapshot means all of them count). With no snapshot and zero
|
|
10685
|
+
* eligible outcomes it distills anyway, so the empty state is recorded as
|
|
10686
|
+
* "0 eligible, 0 rules, at <time>" rather than left as "never".
|
|
10687
|
+
*
|
|
10688
|
+
* Before deciding, rules past `ruleExpiryMs` are expired, so a hydrated rule
|
|
10689
|
+
* that is out of date is never applied.
|
|
10690
|
+
*
|
|
10691
|
+
* Runs at most once per instance; `DistilledRuleStage` calls it when it first
|
|
10692
|
+
* runs, so constructing a distiller stays cheap. Returns whether it distilled.
|
|
10693
|
+
*/
|
|
10694
|
+
checkPersistedTrigger() {
|
|
10695
|
+
if (this.persistedTriggerChecked) return false;
|
|
10696
|
+
this.persistedTriggerChecked = true;
|
|
10697
|
+
this.expireRules(getTimeProvider().now());
|
|
10698
|
+
const newer = countEligibleSince(this.outcomeStore.query(), this.lastDistillAt);
|
|
10699
|
+
const recordEmpty = this.lastDistillAt === void 0 && newer === 0;
|
|
10700
|
+
if (newer < this.config.triggerThreshold && !recordEmpty) return false;
|
|
10701
|
+
this.logger.debug("Persisted-store distill trigger fired", {
|
|
10702
|
+
eligibleSinceLastDistill: newer,
|
|
10703
|
+
triggerThreshold: this.config.triggerThreshold
|
|
10704
|
+
});
|
|
10705
|
+
this.distill();
|
|
10706
|
+
return true;
|
|
10707
|
+
}
|
|
10708
|
+
/** Run distillation on the eligible outcomes in the OutcomeStore (#6512). */
|
|
10709
|
+
distill() {
|
|
10710
|
+
const now = getTimeProvider().now();
|
|
10711
|
+
const outcomes = this.outcomeStore.query().filter(isDistillerEligible);
|
|
10712
|
+
const groups = groupOutcomes(outcomes);
|
|
10713
|
+
this.expireRules(now);
|
|
10714
|
+
const failurePatterns = detectFailurePatterns(groups, this.config.failureRateThreshold);
|
|
10715
|
+
const successPatterns = detectSuccessPatterns(groups, this.config.successRateThreshold);
|
|
10716
|
+
const latencyPatterns = detectLatencyPatterns(groups, this.config.latencyRatioThreshold);
|
|
10717
|
+
const allPatterns = [...failurePatterns, ...successPatterns, ...latencyPatterns];
|
|
10718
|
+
for (const pattern of allPatterns) {
|
|
10719
|
+
this.upsertRule(pattern, now);
|
|
10720
|
+
}
|
|
10721
|
+
this.enforceMaxRules();
|
|
10722
|
+
this.lastDistillAt = now;
|
|
10723
|
+
this.eligibleAtLastDistill = outcomes.length;
|
|
10724
|
+
this.logger.debug("Distillation complete", {
|
|
10725
|
+
rulesTotal: this.rules.size,
|
|
10726
|
+
patternsFound: allPatterns.length,
|
|
10727
|
+
outcomesAnalyzed: outcomes.length
|
|
10728
|
+
});
|
|
10729
|
+
}
|
|
10730
|
+
/** Get rules filtered by status. */
|
|
10731
|
+
getRules(status) {
|
|
10732
|
+
const all = [...this.rules.values()];
|
|
10733
|
+
if (status === void 0) return all;
|
|
10734
|
+
return all.filter((r) => r.status === status);
|
|
10735
|
+
}
|
|
10736
|
+
/** Get distiller statistics. */
|
|
10737
|
+
getStats() {
|
|
10738
|
+
const countByStatus = {
|
|
10739
|
+
draft: 0,
|
|
10740
|
+
active: 0,
|
|
10741
|
+
promoted: 0,
|
|
10742
|
+
expired: 0
|
|
10743
|
+
};
|
|
10744
|
+
for (const rule of this.rules.values()) {
|
|
10745
|
+
countByStatus[rule.status]++;
|
|
10746
|
+
}
|
|
10747
|
+
return {
|
|
10748
|
+
ruleCountByStatus: countByStatus,
|
|
10749
|
+
totalRules: this.rules.size,
|
|
10750
|
+
lastDistillAt: this.lastDistillAt,
|
|
10751
|
+
outcomesSinceLastDistill: this.outcomeCounter,
|
|
10752
|
+
eligibleOutcomesAtLastDistill: this.eligibleAtLastDistill
|
|
10753
|
+
};
|
|
10754
|
+
}
|
|
10755
|
+
/**
|
|
10756
|
+
* Promote high-confidence rules to RoutingMemory.
|
|
10757
|
+
* Rules must be active, with sufficient observations and confidence.
|
|
10758
|
+
*
|
|
10759
|
+
* @deprecated No production caller (#5004 finding 4). `DistilledRuleStage`
|
|
10760
|
+
* is the single channel by which distilled rules reach routing; this
|
|
10761
|
+
* second channel into `RoutingMemory` is kept only so the deprecation is
|
|
10762
|
+
* non-breaking. Removal is tracked in #5467. Note the gate now compares
|
|
10763
|
+
* `confidence = support × effect` against `promotionConfidence`, so a rule
|
|
10764
|
+
* that would have promoted on sample size alone may no longer clear it.
|
|
10765
|
+
*/
|
|
10766
|
+
promote(routingMemory) {
|
|
10767
|
+
let promoted = 0;
|
|
10768
|
+
for (const [id, rule] of this.rules) {
|
|
10769
|
+
if (rule.status !== "active") continue;
|
|
10770
|
+
if (rule.observationCount < this.config.minObservationsForActive) continue;
|
|
10771
|
+
if (rule.confidence < this.config.promotionConfidence) continue;
|
|
10772
|
+
const performance2 = this.ruleToPerformance(rule);
|
|
10773
|
+
routingMemory.storePreference(rule.cli, rule.category, performance2);
|
|
10774
|
+
this.rules.set(id, { ...rule, status: "promoted", updatedAt: getTimeProvider().now() });
|
|
10775
|
+
promoted++;
|
|
10776
|
+
this.logger.info("Promoted distilled rule to RoutingMemory", {
|
|
10777
|
+
ruleId: id,
|
|
10778
|
+
cli: rule.cli,
|
|
10779
|
+
category: rule.category,
|
|
10780
|
+
confidence: rule.confidence
|
|
10781
|
+
});
|
|
10782
|
+
}
|
|
10783
|
+
return promoted;
|
|
10784
|
+
}
|
|
10785
|
+
// ==========================================================================
|
|
10786
|
+
// Protected — for subclass hydration (Issue #1009)
|
|
10787
|
+
// ==========================================================================
|
|
10788
|
+
/** Load pre-existing rules (e.g., from disk). Used by PersistentStrategyDistiller. */
|
|
10789
|
+
loadRules(rules) {
|
|
10790
|
+
for (const rule of rules) {
|
|
10791
|
+
this.rules.set(rule.id, rule);
|
|
10792
|
+
}
|
|
10793
|
+
}
|
|
10794
|
+
/**
|
|
10795
|
+
* Restore last-distill state from a persisted snapshot (#6512), so the
|
|
10796
|
+
* first-route trigger counts only outcomes newer than that snapshot.
|
|
10797
|
+
*/
|
|
10798
|
+
restoreDistillState(lastDistillAt, eligibleOutcomes) {
|
|
10799
|
+
this.lastDistillAt = lastDistillAt;
|
|
10800
|
+
this.eligibleAtLastDistill = eligibleOutcomes;
|
|
10801
|
+
}
|
|
10802
|
+
// ==========================================================================
|
|
10803
|
+
// Private
|
|
10804
|
+
// ==========================================================================
|
|
10805
|
+
upsertRule(pattern, now) {
|
|
10806
|
+
const id = ruleFingerprint(pattern.patternType, pattern.cli, pattern.category);
|
|
10807
|
+
const existing = this.rules.get(id);
|
|
10808
|
+
if (existing === void 0 && pattern.observationCount < this.config.minObservationsForDraft) {
|
|
10809
|
+
return;
|
|
10810
|
+
}
|
|
10811
|
+
const support = sigmoidConfidence(pattern.observationCount);
|
|
10812
|
+
const effect = effectFor(pattern.patternType, pattern.metric, this.config);
|
|
10813
|
+
const confidence = support * effect;
|
|
10814
|
+
const status = this.computeStatus(pattern.observationCount, existing?.status);
|
|
10815
|
+
if (existing !== void 0) {
|
|
10816
|
+
this.rules.set(id, {
|
|
10817
|
+
...existing,
|
|
10818
|
+
action: pattern.action,
|
|
10819
|
+
confidence,
|
|
10820
|
+
support,
|
|
10821
|
+
effect,
|
|
10822
|
+
observationCount: pattern.observationCount,
|
|
10823
|
+
metric: pattern.metric,
|
|
10824
|
+
status,
|
|
10825
|
+
updatedAt: now
|
|
10826
|
+
});
|
|
10827
|
+
} else {
|
|
10828
|
+
this.rules.set(id, {
|
|
10829
|
+
id,
|
|
10830
|
+
patternType: pattern.patternType,
|
|
10831
|
+
cli: pattern.cli,
|
|
10832
|
+
category: pattern.category,
|
|
10833
|
+
action: pattern.action,
|
|
10834
|
+
confidence,
|
|
10835
|
+
support,
|
|
10836
|
+
effect,
|
|
10837
|
+
observationCount: pattern.observationCount,
|
|
10838
|
+
metric: pattern.metric,
|
|
10839
|
+
status,
|
|
10840
|
+
createdAt: now,
|
|
10841
|
+
updatedAt: now,
|
|
10842
|
+
tainted: false
|
|
10843
|
+
});
|
|
10844
|
+
}
|
|
10845
|
+
}
|
|
10846
|
+
computeStatus(observations, existing) {
|
|
10847
|
+
if (existing === "promoted") return "promoted";
|
|
10848
|
+
if (observations >= this.config.minObservationsForActive) return "active";
|
|
10849
|
+
return "draft";
|
|
10850
|
+
}
|
|
10851
|
+
expireRules(now) {
|
|
10852
|
+
for (const [id, rule] of this.rules) {
|
|
10853
|
+
if (rule.status === "promoted") continue;
|
|
10854
|
+
if (now - rule.updatedAt > this.config.ruleExpiryMs) {
|
|
10855
|
+
this.rules.set(id, { ...rule, status: "expired", updatedAt: now });
|
|
10856
|
+
}
|
|
10857
|
+
}
|
|
10858
|
+
}
|
|
10859
|
+
enforceMaxRules() {
|
|
10860
|
+
if (this.rules.size <= this.config.maxRules) return;
|
|
10861
|
+
const sorted = [...this.rules.entries()].sort((a, b) => {
|
|
10862
|
+
if (a[1].status === "expired" && b[1].status !== "expired") return -1;
|
|
10863
|
+
if (b[1].status === "expired" && a[1].status !== "expired") return 1;
|
|
10864
|
+
return a[1].confidence - b[1].confidence;
|
|
10865
|
+
});
|
|
10866
|
+
const excess = this.rules.size - this.config.maxRules;
|
|
10867
|
+
for (let i = 0; i < excess; i++) {
|
|
10868
|
+
const entry = sorted[i];
|
|
10869
|
+
if (entry !== void 0) {
|
|
10870
|
+
this.rules.delete(entry[0]);
|
|
10871
|
+
}
|
|
10872
|
+
}
|
|
10873
|
+
}
|
|
10874
|
+
/** Convert rule metrics into ModelPerformance for RoutingMemory. */
|
|
10875
|
+
ruleToPerformance(rule) {
|
|
10876
|
+
const successRate3 = rule.patternType === "success-rate" ? rule.metric : 1 - rule.metric;
|
|
10877
|
+
return {
|
|
10878
|
+
avgQuality: rule.confidence,
|
|
10879
|
+
successRate: Math.max(0, Math.min(1, successRate3)),
|
|
10880
|
+
avgLatencyMs: 0,
|
|
10881
|
+
avgTokens: 0,
|
|
10882
|
+
observations: rule.observationCount
|
|
10883
|
+
};
|
|
10884
|
+
}
|
|
10885
|
+
};
|
|
10886
|
+
function createStrategyDistiller(outcomeStore, logger15, config) {
|
|
10887
|
+
return new StrategyDistiller(outcomeStore, logger15, config);
|
|
10888
|
+
}
|
|
10889
|
+
var persistentDistillerFactory;
|
|
10890
|
+
function registerPersistentDistillerFactory(factory) {
|
|
10891
|
+
persistentDistillerFactory = factory;
|
|
10892
|
+
}
|
|
10893
|
+
function createPersistentDistillerOrFallback(outcomeStore, logger15) {
|
|
10894
|
+
if (persistentDistillerFactory !== void 0) {
|
|
10895
|
+
return persistentDistillerFactory(outcomeStore, logger15);
|
|
10896
|
+
}
|
|
10897
|
+
return new StrategyDistiller(outcomeStore, logger15);
|
|
10898
|
+
}
|
|
10899
|
+
|
|
10769
10900
|
// src/orchestration/outcomes/outcome-store.ts
|
|
10770
10901
|
var DEFAULT_MAX_ENTRIES2 = 1e4;
|
|
10771
10902
|
var DEFAULT_FAMILY_FALLBACK_THRESHOLD = 5;
|
|
@@ -12860,7 +12991,7 @@ function aggregateDecisionCosts(records, windowMs) {
|
|
|
12860
12991
|
import { z as z18 } from "zod";
|
|
12861
12992
|
|
|
12862
12993
|
// src/config/jsonl-store.ts
|
|
12863
|
-
import { appendFileSync as appendFileSync3, existsSync as
|
|
12994
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync } from "node:fs";
|
|
12864
12995
|
import { dirname as dirname5 } from "node:path";
|
|
12865
12996
|
var DIR_MODE = 448;
|
|
12866
12997
|
var JsonlStore = class {
|
|
@@ -12923,14 +13054,14 @@ var JsonlStore = class {
|
|
|
12923
13054
|
}
|
|
12924
13055
|
}
|
|
12925
13056
|
hydrate() {
|
|
12926
|
-
if (!
|
|
13057
|
+
if (!existsSync7(this.filePath)) {
|
|
12927
13058
|
this.logger.debug("No JSONL file found, starting fresh", { path: this.filePath });
|
|
12928
13059
|
return;
|
|
12929
13060
|
}
|
|
12930
13061
|
let loaded = 0;
|
|
12931
13062
|
let skipped = 0;
|
|
12932
13063
|
try {
|
|
12933
|
-
const content =
|
|
13064
|
+
const content = readFileSync7(this.filePath, "utf-8");
|
|
12934
13065
|
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
12935
13066
|
for (const line of lines) {
|
|
12936
13067
|
try {
|
|
@@ -15202,7 +15333,7 @@ function resolveModelForTier(cliName, tier, opts = {}) {
|
|
|
15202
15333
|
}
|
|
15203
15334
|
|
|
15204
15335
|
// src/cli-adapters/model-selection-shadow.ts
|
|
15205
|
-
import { appendFileSync as appendFileSync4, existsSync as
|
|
15336
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
|
|
15206
15337
|
import { z as z22 } from "zod";
|
|
15207
15338
|
function isRouteModelShadowEnabled() {
|
|
15208
15339
|
return parseBoolEnv("NEXUS_ROUTE_MODEL_SHADOW", false) && isPersistenceEnabled();
|
|
@@ -15253,11 +15384,11 @@ function persistModelSelectionShadowRecord(record) {
|
|
|
15253
15384
|
}
|
|
15254
15385
|
function readModelSelectionShadowRecords() {
|
|
15255
15386
|
const file = getModelSelectionShadowFile();
|
|
15256
|
-
if (!
|
|
15387
|
+
if (!existsSync8(file)) return [];
|
|
15257
15388
|
const records = [];
|
|
15258
15389
|
try {
|
|
15259
15390
|
const cutoff = Date.now() - READ_LOOKBACK_MS;
|
|
15260
|
-
const lines =
|
|
15391
|
+
const lines = readFileSync8(file, "utf-8").split("\n").filter((l) => l.trim().length > 0);
|
|
15261
15392
|
for (const line of lines) {
|
|
15262
15393
|
let parsed;
|
|
15263
15394
|
try {
|
|
@@ -15612,6 +15743,9 @@ function generateTraceId2() {
|
|
|
15612
15743
|
// src/cli-adapters/composite-router.ts
|
|
15613
15744
|
var FAILURE_QUALITY_SCORE = 0.3;
|
|
15614
15745
|
var DEFAULT_TOKEN_ESTIMATE = 1e3;
|
|
15746
|
+
function applyDistillationSwitch(config) {
|
|
15747
|
+
return isStrategyDistillationEnabled() ? config : { ...config, enableStrategyDistillation: false };
|
|
15748
|
+
}
|
|
15615
15749
|
var CompositeRouter = class _CompositeRouter {
|
|
15616
15750
|
config;
|
|
15617
15751
|
logger;
|
|
@@ -15694,7 +15828,7 @@ var CompositeRouter = class _CompositeRouter {
|
|
|
15694
15828
|
availableModelsCache,
|
|
15695
15829
|
...baseConfig
|
|
15696
15830
|
} = config ?? {};
|
|
15697
|
-
this.config = CompositeRouterConfigSchema.parse(baseConfig);
|
|
15831
|
+
this.config = applyDistillationSwitch(CompositeRouterConfigSchema.parse(baseConfig));
|
|
15698
15832
|
this.logger = logger15 ?? createLogger({ component: "CompositeRouter" });
|
|
15699
15833
|
this.pendingRoutingOutcomes = new PendingRoutingOutcomes(this.logger);
|
|
15700
15834
|
this.adapters = adapters;
|
|
@@ -15880,7 +16014,7 @@ var CompositeRouter = class _CompositeRouter {
|
|
|
15880
16014
|
*/
|
|
15881
16015
|
async consultUnifiedContext(task) {
|
|
15882
16016
|
try {
|
|
15883
|
-
const { getContextForTask, inferTaskCategory } = await import("./context-retriever-
|
|
16017
|
+
const { getContextForTask, inferTaskCategory } = await import("./context-retriever-NZOSGG6S.js");
|
|
15884
16018
|
const ctx = await getContextForTask({
|
|
15885
16019
|
task: task.content,
|
|
15886
16020
|
category: inferTaskCategory(task.content),
|
|
@@ -17463,6 +17597,7 @@ export {
|
|
|
17463
17597
|
RoutingMemory,
|
|
17464
17598
|
createRoutingMemory,
|
|
17465
17599
|
DEFAULT_DISTILLER_CONFIG,
|
|
17600
|
+
countEligibleOutcomesInFile,
|
|
17466
17601
|
sigmoidConfidence,
|
|
17467
17602
|
effectFor,
|
|
17468
17603
|
detectFailurePatterns,
|
|
@@ -17515,4 +17650,4 @@ export {
|
|
|
17515
17650
|
AgentCapability,
|
|
17516
17651
|
OrchestratorError
|
|
17517
17652
|
};
|
|
17518
|
-
//# sourceMappingURL=chunk-
|
|
17653
|
+
//# sourceMappingURL=chunk-64IJKT4I.js.map
|