open-multi-agent-kit 0.90.3 → 0.90.5
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/CHANGELOG.md +38 -0
- package/README.md +16 -14
- package/dist/config.d.ts +3 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +27 -19
- package/dist/config.js.map +1 -1
- package/dist/core/adaptorch-bridge.d.ts +250 -0
- package/dist/core/adaptorch-bridge.d.ts.map +1 -0
- package/dist/core/adaptorch-bridge.js +466 -0
- package/dist/core/adaptorch-bridge.js.map +1 -0
- package/dist/core/agent-session.d.ts +97 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +194 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bang-skill-invocation.d.ts.map +1 -1
- package/dist/core/bang-skill-invocation.js +149 -0
- package/dist/core/bang-skill-invocation.js.map +1 -1
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js +15 -2
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
- package/dist/core/context-budget-v2-planner.js +10 -2
- package/dist/core/context-budget-v2-planner.js.map +1 -1
- package/dist/core/context-budget-v2-types.d.ts +7 -1
- package/dist/core/context-budget-v2-types.d.ts.map +1 -1
- package/dist/core/context-budget-v2-types.js.map +1 -1
- package/dist/core/domain-loadouts.d.ts.map +1 -1
- package/dist/core/domain-loadouts.js +1 -1
- package/dist/core/domain-loadouts.js.map +1 -1
- package/dist/core/reasoning-router-bias.d.ts +107 -0
- package/dist/core/reasoning-router-bias.d.ts.map +1 -0
- package/dist/core/reasoning-router-bias.js +278 -0
- package/dist/core/reasoning-router-bias.js.map +1 -0
- package/dist/core/reasoning-router-resolver.d.ts +44 -0
- package/dist/core/reasoning-router-resolver.d.ts.map +1 -0
- package/dist/core/reasoning-router-resolver.js +79 -0
- package/dist/core/reasoning-router-resolver.js.map +1 -0
- package/dist/core/reasoning-router-v4-weights.d.ts +80 -0
- package/dist/core/reasoning-router-v4-weights.d.ts.map +1 -0
- package/dist/core/reasoning-router-v4-weights.js +68 -0
- package/dist/core/reasoning-router-v4-weights.js.map +1 -0
- package/dist/core/reasoning-router-v4.d.ts +118 -0
- package/dist/core/reasoning-router-v4.d.ts.map +1 -0
- package/dist/core/reasoning-router-v4.js +585 -0
- package/dist/core/reasoning-router-v4.js.map +1 -0
- package/dist/core/router-feedback-collector.d.ts +98 -0
- package/dist/core/router-feedback-collector.d.ts.map +1 -0
- package/dist/core/router-feedback-collector.js +236 -0
- package/dist/core/router-feedback-collector.js.map +1 -0
- package/dist/core/settings-manager.d.ts +28 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +16 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +4 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/modes/interactive/components/control-panel-runtime-status.d.ts +15 -0
- package/dist/modes/interactive/components/control-panel-runtime-status.d.ts.map +1 -1
- package/dist/modes/interactive/components/control-panel-runtime-status.js +95 -5
- package/dist/modes/interactive/components/control-panel-runtime-status.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +60 -17
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/loadout-domains/README.md +8 -6
- package/docs/loadout-domains/ai-agent-ops.md +3 -2
- package/docs/loadout-domains/backend-api.md +3 -2
- package/docs/loadout-domains/data-science.md +3 -2
- package/docs/loadout-domains/devops-infra.md +2 -1
- package/docs/loadout-domains/docs-writing.md +7 -2
- package/docs/loadout-domains/frontend-ui.md +42 -11
- package/docs/loadout-domains/korean-document.md +83 -0
- package/docs/loadout-domains/mobile.md +1 -1
- package/docs/loadout-domains/qa-testing.md +4 -2
- package/docs/loadout-domains/security-audit.md +2 -1
- package/docs/loadout-domains/visual-qa.md +94 -0
- package/docs/skills.md +4 -1
- package/docs/usage.md +36 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/agent-capability-router.test.ts +123 -0
- package/examples/extensions/subagent/agent-capability-router.ts +274 -0
- package/examples/extensions/subagent/agents/planner.md +3 -0
- package/examples/extensions/subagent/agents/reviewer.md +3 -0
- package/examples/extensions/subagent/agents/scout.md +3 -0
- package/examples/extensions/subagent/agents/worker.md +2 -0
- package/examples/extensions/subagent/agents.ts +14 -0
- package/examples/extensions/subagent/capabilities.ts +368 -0
- package/examples/extensions/subagent/domain-profiles.ts +1089 -0
- package/examples/extensions/subagent/index.ts +71 -3
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/12-full-control.ts +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
- package/dist/agents/jailbreak/index.d.ts +0 -7
- package/dist/agents/jailbreak/index.d.ts.map +0 -1
- package/dist/agents/jailbreak/index.js +0 -7
- package/dist/agents/jailbreak/index.js.map +0 -1
- package/dist/agents/jailbreak/subagent-f.d.ts +0 -44
- package/dist/agents/jailbreak/subagent-f.d.ts.map +0 -1
- package/dist/agents/jailbreak/subagent-f.js +0 -60
- package/dist/agents/jailbreak/subagent-f.js.map +0 -1
- package/dist/cli/jailbreak-args.d.ts +0 -26
- package/dist/cli/jailbreak-args.d.ts.map +0 -1
- package/dist/cli/jailbreak-args.js +0 -86
- package/dist/cli/jailbreak-args.js.map +0 -1
- package/dist/commands/jailbreak.d.ts +0 -49
- package/dist/commands/jailbreak.d.ts.map +0 -1
- package/dist/commands/jailbreak.js +0 -201
- package/dist/commands/jailbreak.js.map +0 -1
- package/dist/core/compactor.d.ts +0 -42
- package/dist/core/compactor.d.ts.map +0 -1
- package/dist/core/compactor.js +0 -128
- package/dist/core/compactor.js.map +0 -1
- package/dist/core/token-optimizer.d.ts +0 -58
- package/dist/core/token-optimizer.d.ts.map +0 -1
- package/dist/core/token-optimizer.js +0 -179
- package/dist/core/token-optimizer.js.map +0 -1
- package/dist/encoding/index.d.ts +0 -6
- package/dist/encoding/index.d.ts.map +0 -1
- package/dist/encoding/index.js +0 -6
- package/dist/encoding/index.js.map +0 -1
- package/dist/encoding/l4-encoder.d.ts +0 -96
- package/dist/encoding/l4-encoder.d.ts.map +0 -1
- package/dist/encoding/l4-encoder.js +0 -474
- package/dist/encoding/l4-encoder.js.map +0 -1
- package/dist/fuzzing/index.d.ts +0 -6
- package/dist/fuzzing/index.d.ts.map +0 -1
- package/dist/fuzzing/index.js +0 -6
- package/dist/fuzzing/index.js.map +0 -1
- package/dist/fuzzing/mutation-engine.d.ts +0 -93
- package/dist/fuzzing/mutation-engine.d.ts.map +0 -1
- package/dist/fuzzing/mutation-engine.js +0 -244
- package/dist/fuzzing/mutation-engine.js.map +0 -1
- package/dist/jailbreak-extension.d.ts +0 -48
- package/dist/jailbreak-extension.d.ts.map +0 -1
- package/dist/jailbreak-extension.js +0 -47
- package/dist/jailbreak-extension.js.map +0 -1
- package/dist/modules/index.d.ts +0 -8
- package/dist/modules/index.d.ts.map +0 -1
- package/dist/modules/index.js +0 -7
- package/dist/modules/index.js.map +0 -1
- package/dist/modules/lrl-bypass.d.ts +0 -40
- package/dist/modules/lrl-bypass.d.ts.map +0 -1
- package/dist/modules/lrl-bypass.js +0 -84
- package/dist/modules/lrl-bypass.js.map +0 -1
- package/dist/modules/lrl-deps.d.ts +0 -38
- package/dist/modules/lrl-deps.d.ts.map +0 -1
- package/dist/modules/lrl-deps.js +0 -128
- package/dist/modules/lrl-deps.js.map +0 -1
- package/dist/multiturn/easl-chain.d.ts +0 -113
- package/dist/multiturn/easl-chain.d.ts.map +0 -1
- package/dist/multiturn/easl-chain.js +0 -351
- package/dist/multiturn/easl-chain.js.map +0 -1
- package/dist/multiturn/index.d.ts +0 -6
- package/dist/multiturn/index.d.ts.map +0 -1
- package/dist/multiturn/index.js +0 -6
- package/dist/multiturn/index.js.map +0 -1
- package/dist/routing/fallback-router.d.ts +0 -123
- package/dist/routing/fallback-router.d.ts.map +0 -1
- package/dist/routing/fallback-router.js +0 -355
- package/dist/routing/fallback-router.js.map +0 -1
- package/dist/routing/index.d.ts +0 -6
- package/dist/routing/index.d.ts.map +0 -1
- package/dist/routing/index.js +0 -6
- package/dist/routing/index.js.map +0 -1
- package/dist/types/jailbreak.d.ts +0 -246
- package/dist/types/jailbreak.d.ts.map +0 -1
- package/dist/types/jailbreak.js +0 -8
- package/dist/types/jailbreak.js.map +0 -1
- package/dist/utils/jailbreak/common.d.ts +0 -63
- package/dist/utils/jailbreak/common.d.ts.map +0 -1
- package/dist/utils/jailbreak/common.js +0 -130
- package/dist/utils/jailbreak/common.js.map +0 -1
- package/dist/utils/jailbreak/index.d.ts +0 -7
- package/dist/utils/jailbreak/index.d.ts.map +0 -1
- package/dist/utils/jailbreak/index.js +0 -7
- package/dist/utils/jailbreak/index.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-router-bias.d.ts","sourceRoot":"","sources":["../../src/core/reasoning-router-bias.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAKN,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAE5B,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,MAAM,gCAAgC,CAAC;AAExC,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,gEAAgE;AAChE,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,qEAAqE;AACrE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAElD,8GAA4G;AAC5G,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,cAAc,EAAE,uBAAuB,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,uBAAuB,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAe,SAAQ,iBAAiB;IACxD,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wFAAwF;IACxF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,aAAa,EAAE,yBAAyB,CAAC;IAClD,oGAAoG;IACpG,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,iGAAiG;IACjG,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iHAAiH;IACjH,QAAQ,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,0BAA0B;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,qBAAqB,CAAC;CAC/C;AAgDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,OAAO,GAAE,0BAA+B,GACtC,kBAAkB,CA0DpB;AAED,6GAA6G;AAC7G,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,EAAE,iBAAiB,GAAG,eAAe,CAMzG;AAwDD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAaxE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAYhF;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAQ9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,GAAE,qBAA4B,GAAG,MAAM,CAE9F","sourcesContent":["/**\n * Reasoning-router bias compiler — privacy-safe learning ledger.\n *\n * Pure, deterministic compiler from v4 feedback records (see\n * router-feedback-collector.ts) to a bounded, per-cell reasoning-level bias\n * snapshot. No clock, randomness, network, or model calls: the same input array\n * always produces the same output (`JSON.stringify` byte-identical), regardless\n * of input record order. `parseRouterBiasSnapshot` performs no filesystem\n * access itself; the actual disk read lives in agent-session.ts.\n */\n\nimport { createHash } from \"crypto\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.ts\";\nimport {\n\tisRouterFeedbackRecord,\n\tROUTER_FEEDBACK_LANE_TYPES,\n\tROUTER_FEEDBACK_LEN_BUCKETS,\n\tROUTER_FEEDBACK_TASK_CLASSES,\n\ttype RouterFeedbackLaneType,\n\ttype RouterFeedbackLenBucket,\n\ttype RouterFeedbackRecord,\n\ttype RouterFeedbackTaskClass,\n\ttype RouterFeedbackVersion,\n} from \"./router-feedback-collector.ts\";\n\n/** Minimum non-neutral (\"strong\") record count per cell before any bias applies. */\nexport const BIAS_STRONG_THRESHOLD = 5;\n\n/** Bias magnitude bound, matching the resolver's bias clamp. */\nexport const BIAS_MAX_STEPS = 2;\n\n/** Bounded per-cell bias, in ladder steps. Never outside [-2, 2]. */\nexport type RouterBiasSteps = -2 | -1 | 0 | 1 | 2;\n\n/** The feature tuple a bias cell is keyed on — identical dimensions the router already computes locally. */\nexport interface RouterBiasCellKey {\n\treadonly predictedClass: RouterFeedbackTaskClass;\n\treadonly laneType: RouterFeedbackLaneType;\n\treadonly lenBucket: RouterFeedbackLenBucket;\n\treadonly hadFence: boolean;\n\treadonly hadDiff: boolean;\n}\n\nexport interface RouterBiasCell extends RouterBiasCellKey {\n\treadonly biasSteps: RouterBiasSteps;\n\t/** Non-neutral (up/down-signaling) record count for this cell. */\n\treadonly nStrong: number;\n\t/** Total record count for this cell, including neutral (accepted/same/pass) records. */\n\treadonly nTotal: number;\n}\n\nexport interface RouterBiasSnapshot {\n\treadonly schemaVersion: \"router-bias-snapshot/v1\";\n\t/** sha256 hex digest of the canonicalized valid-record set actually compiled; not a prompt hash. */\n\treadonly sourceRecordDigest: string;\n\t/** Count of records that were validated and included in this snapshot. */\n\treadonly consideredCount: number;\n\t/** Count of records excluded from this snapshot (schema-invalid, or router-version mismatch). */\n\treadonly droppedCount: number;\n\t/** Bias cells, sorted deterministically. Empty when there is no ledger data or no cell reaches the threshold. */\n\treadonly biasCells: readonly RouterBiasCell[];\n}\n\nexport interface CompileBiasSnapshotOptions {\n\t/**\n\t * Restrict compilation to one router version. The only supported runtime\n\t * router version is v4; records tagged with any other value are invalid and\n\t * counted in `droppedCount`.\n\t */\n\treadonly routerVersion?: RouterFeedbackVersion;\n}\n\nfunction cellKeyString(key: RouterBiasCellKey): string {\n\treturn [key.predictedClass, key.laneType, String(key.lenBucket), String(key.hadFence), String(key.hadDiff)].join(\n\t\t\"|\",\n\t);\n}\n\n/** +1 \"needed more effort\", -1 \"needed less effort\", 0 neutral (no directional bias contribution). */\nfunction outcomeDirection(outcome: RouterFeedbackRecord[\"outcome\"]): -1 | 0 | 1 {\n\tif (outcome === \"up\" || outcome === \"fail\" || outcome === \"debug-follow-up\") return 1;\n\tif (outcome === \"down\") return -1;\n\treturn 0;\n}\n\n/** Fixed-key-order JSON so identical records always canonicalize to identical bytes. */\nfunction canonicalRecordJson(record: RouterFeedbackRecord): string {\n\treturn JSON.stringify({\n\t\trouterVersion: record.routerVersion,\n\t\tlaneType: record.laneType,\n\t\tpredictedClass: record.predictedClass,\n\t\tresolvedLevel: record.resolvedLevel,\n\t\tacceptedLevel: record.acceptedLevel,\n\t\tsignal: record.signal,\n\t\toutcome: record.outcome,\n\t\tlenBucket: record.lenBucket,\n\t\thadFence: record.hadFence,\n\t\thadDiff: record.hadDiff,\n\t});\n}\n\ninterface CellAccumulator extends RouterBiasCellKey {\n\tnTotal: number;\n\tupVotes: number;\n\tdownVotes: number;\n}\n\nfunction computeBiasSteps(upVotes: number, downVotes: number, nStrong: number): RouterBiasSteps {\n\tif (nStrong < BIAS_STRONG_THRESHOLD) return 0;\n\tconst majority = upVotes - downVotes;\n\tif (majority === 0) return 0;\n\n\tconst dominance = Math.abs(majority) / nStrong;\n\tconst magnitude = dominance >= 0.75 ? BIAS_MAX_STEPS : 1;\n\tconst signedMagnitude = majority > 0 ? magnitude : -magnitude;\n\treturn Math.max(-BIAS_MAX_STEPS, Math.min(BIAS_MAX_STEPS, signedMagnitude)) as RouterBiasSteps;\n}\n\n/**\n * Deterministically compile a bounded bias snapshot from raw (possibly\n * malformed) ledger entries. Unknown-shaped entries — including ones with\n * extra keys, missing keys, or out-of-range enum values — are dropped and\n * counted, never thrown. Records tagged with a `routerVersion` other than the\n * target version are also dropped and counted (never merged across router\n * versions into one snapshot). An empty or all-dropped input compiles to an\n * empty-bias snapshot (`biasCells: []`), which is exactly the shipped\n * no-learning behavior — this makes \"empty ledger -> zero bias\" true by\n * construction, not by a special case.\n *\n * Pure: same `records` (any order) -> same `RouterBiasSnapshot` (byte-\n * identical `JSON.stringify` output), because cells are grouped by an\n * order-independent key and the emitted cell/record ordering is always\n * sorted before serialization.\n */\nexport function compileBiasSnapshot(\n\trecords: readonly unknown[],\n\toptions: CompileBiasSnapshotOptions = {},\n): RouterBiasSnapshot {\n\tconst routerVersion = options.routerVersion ?? \"v4\";\n\tconst valid: RouterFeedbackRecord[] = [];\n\tlet droppedCount = 0;\n\n\tfor (const entry of records) {\n\t\tif (isRouterFeedbackRecord(entry) && entry.routerVersion === routerVersion) {\n\t\t\tvalid.push(entry);\n\t\t} else {\n\t\t\tdroppedCount += 1;\n\t\t}\n\t}\n\n\tconst cells = new Map<string, CellAccumulator>();\n\tfor (const record of valid) {\n\t\tconst key: RouterBiasCellKey = {\n\t\t\tpredictedClass: record.predictedClass,\n\t\t\tlaneType: record.laneType,\n\t\t\tlenBucket: record.lenBucket,\n\t\t\thadFence: record.hadFence,\n\t\t\thadDiff: record.hadDiff,\n\t\t};\n\t\tconst mapKey = cellKeyString(key);\n\t\tconst existing = cells.get(mapKey) ?? { ...key, nTotal: 0, upVotes: 0, downVotes: 0 };\n\t\texisting.nTotal += 1;\n\t\tconst direction = outcomeDirection(record.outcome);\n\t\tif (direction === 1) existing.upVotes += 1;\n\t\telse if (direction === -1) existing.downVotes += 1;\n\t\tcells.set(mapKey, existing);\n\t}\n\n\tconst biasCells: RouterBiasCell[] = [];\n\tfor (const mapKey of [...cells.keys()].sort()) {\n\t\tconst acc = cells.get(mapKey);\n\t\tif (!acc) continue;\n\t\tconst nStrong = acc.upVotes + acc.downVotes;\n\t\tbiasCells.push({\n\t\t\tpredictedClass: acc.predictedClass,\n\t\t\tlaneType: acc.laneType,\n\t\t\tlenBucket: acc.lenBucket,\n\t\t\thadFence: acc.hadFence,\n\t\t\thadDiff: acc.hadDiff,\n\t\t\tbiasSteps: computeBiasSteps(acc.upVotes, acc.downVotes, nStrong),\n\t\t\tnStrong,\n\t\t\tnTotal: acc.nTotal,\n\t\t});\n\t}\n\n\tconst canonicalValid = valid.map(canonicalRecordJson).sort();\n\tconst sourceRecordDigest = createHash(\"sha256\").update(canonicalValid.join(\"\\n\")).digest(\"hex\");\n\n\treturn {\n\t\tschemaVersion: \"router-bias-snapshot/v1\",\n\t\tsourceRecordDigest,\n\t\tconsideredCount: valid.length,\n\t\tdroppedCount,\n\t\tbiasCells,\n\t};\n}\n\n/** Look up the compiled bias for one feature cell; 0 (no bias) when the cell is absent from the snapshot. */\nexport function getBiasStepsForCell(snapshot: RouterBiasSnapshot, key: RouterBiasCellKey): RouterBiasSteps {\n\tconst wantKey = cellKeyString(key);\n\tfor (const cell of snapshot.biasCells) {\n\t\tif (cellKeyString(cell) === wantKey) return cell.biasSteps;\n\t}\n\treturn 0;\n}\n\n// ============================================================================\n// Snapshot parser / loader (Goal 010 Lane I)\n// ============================================================================\n//\n// Everything below lets a caller safely turn untrusted on-disk JSON text into\n// a validated RouterBiasSnapshot. Same posture as isRouterFeedbackRecord in\n// router-feedback-collector.ts: an exact positive key-set allowlist (not a\n// denylist), bounded per-field checks reusing the ledger's own closed enums,\n// and \"never throw, return null/false instead\" on any malformed input.\n\n/** Bounded bias-step values a compiled cell may carry; mirrors RouterBiasSteps. */\nconst ROUTER_BIAS_STEPS_VALUES: readonly RouterBiasSteps[] = [-2, -1, 0, 1, 2];\n\n/** Exact allowed key set for one compiled `RouterBiasCell`, sorted for canonical set-equality comparison. */\nconst ROUTER_BIAS_CELL_KEYS: readonly string[] = [\n\t\"predictedClass\",\n\t\"laneType\",\n\t\"lenBucket\",\n\t\"hadFence\",\n\t\"hadDiff\",\n\t\"biasSteps\",\n\t\"nStrong\",\n\t\"nTotal\",\n].sort();\n\n/** Exact allowed key set for a `RouterBiasSnapshot`, sorted for canonical set-equality comparison. */\nconst ROUTER_BIAS_SNAPSHOT_KEYS: readonly string[] = [\n\t\"schemaVersion\",\n\t\"sourceRecordDigest\",\n\t\"consideredCount\",\n\t\"droppedCount\",\n\t\"biasCells\",\n].sort();\n\n/** sha256 hex digest shape check for `sourceRecordDigest` (never trusts the value is actually a real digest of anything, only that it has the right shape). */\nconst SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction hasExactKeySet(value: Record<string, unknown>, allowedSortedKeys: readonly string[]): boolean {\n\tconst keys = Object.keys(value).sort();\n\tif (keys.length !== allowedSortedKeys.length) return false;\n\tfor (let i = 0; i < keys.length; i++) {\n\t\tif (keys[i] !== allowedSortedKeys[i]) return false;\n\t}\n\treturn true;\n}\n\nfunction isBoundedNonNegativeInteger(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isInteger(value) && value >= 0;\n}\n\n/**\n * Strict runtime validator for one compiled bias cell. Exact eight-key\n * allowlist plus bounded per-field checks, reusing the same closed enums as\n * `isRouterFeedbackRecord` (`ROUTER_FEEDBACK_TASK_CLASSES`,\n * `ROUTER_FEEDBACK_LANE_TYPES`, `ROUTER_FEEDBACK_LEN_BUCKETS`) so this\n * validator cannot silently drift from the ledger's own schema. `nStrong` is\n * additionally required to never exceed `nTotal` (true by construction for\n * every cell `compileBiasSnapshot` can produce; a cheap sanity bound against\n * a hand-tampered file).\n */\nexport function isRouterBiasCell(value: unknown): value is RouterBiasCell {\n\tif (!isPlainObject(value)) return false;\n\tif (!hasExactKeySet(value, ROUTER_BIAS_CELL_KEYS)) return false;\n\tif (!ROUTER_FEEDBACK_TASK_CLASSES.includes(value.predictedClass as RouterFeedbackTaskClass)) return false;\n\tif (!ROUTER_FEEDBACK_LANE_TYPES.includes(value.laneType as RouterFeedbackLaneType)) return false;\n\tif (!ROUTER_FEEDBACK_LEN_BUCKETS.includes(value.lenBucket as RouterFeedbackLenBucket)) return false;\n\tif (typeof value.hadFence !== \"boolean\") return false;\n\tif (typeof value.hadDiff !== \"boolean\") return false;\n\tif (!ROUTER_BIAS_STEPS_VALUES.includes(value.biasSteps as RouterBiasSteps)) return false;\n\tif (!isBoundedNonNegativeInteger(value.nStrong)) return false;\n\tif (!isBoundedNonNegativeInteger(value.nTotal)) return false;\n\tif ((value.nStrong as number) > (value.nTotal as number)) return false;\n\treturn true;\n}\n\n/**\n * Strict runtime validator for a compiled bias snapshot. Exact five-key\n * allowlist; `biasCells` must be an array whose every element passes\n * `isRouterBiasCell`. Never trusts on-disk content: any unexpected shape,\n * extra key, or out-of-range value returns `false` rather than throwing.\n */\nexport function isRouterBiasSnapshot(value: unknown): value is RouterBiasSnapshot {\n\tif (!isPlainObject(value)) return false;\n\tif (!hasExactKeySet(value, ROUTER_BIAS_SNAPSHOT_KEYS)) return false;\n\tif (value.schemaVersion !== \"router-bias-snapshot/v1\") return false;\n\tif (typeof value.sourceRecordDigest !== \"string\" || !SHA256_HEX_PATTERN.test(value.sourceRecordDigest)) return false;\n\tif (!isBoundedNonNegativeInteger(value.consideredCount)) return false;\n\tif (!isBoundedNonNegativeInteger(value.droppedCount)) return false;\n\tif (!Array.isArray(value.biasCells)) return false;\n\tfor (const cell of value.biasCells) {\n\t\tif (!isRouterBiasCell(cell)) return false;\n\t}\n\treturn true;\n}\n\n/**\n * Safe parse of raw (untrusted, e.g. on-disk) JSON text into a validated\n * `RouterBiasSnapshot`. Never throws: malformed JSON or any schema violation\n * returns `null`. Performs no filesystem access itself — callers own the\n * actual read — so this module's zero-I/O determinism guarantee for\n * `compileBiasSnapshot`/`getBiasStepsForCell` is unaffected.\n */\nexport function parseRouterBiasSnapshot(raw: string): RouterBiasSnapshot | null {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\treturn null;\n\t}\n\treturn isRouterBiasSnapshot(parsed) ? parsed : null;\n}\n\n/**\n * Default compiled-bias-snapshot path:\n * `<agentDir>/router-feedback/weights/router-bias-snapshot.<version>.json` —\n * matches `compile-bias-snapshot.ts`'s own default `--out` naming exactly, so\n * a snapshot compiled offline for `version` is found here with no extra\n * configuration. Owner-only agent dir, never repo-local (mirrors\n * `getDefaultRouterFeedbackLedgerPath`). Defaults to \"v4\", the router version\n * this lane wires bias *consumption* for.\n */\nexport function getDefaultRouterBiasSnapshotPath(version: RouterFeedbackVersion = \"v4\"): string {\n\treturn join(getAgentDir(), \"router-feedback\", \"weights\", `router-bias-snapshot.${version}.json`);\n}\n"]}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reasoning-router bias compiler — privacy-safe learning ledger.
|
|
3
|
+
*
|
|
4
|
+
* Pure, deterministic compiler from v4 feedback records (see
|
|
5
|
+
* router-feedback-collector.ts) to a bounded, per-cell reasoning-level bias
|
|
6
|
+
* snapshot. No clock, randomness, network, or model calls: the same input array
|
|
7
|
+
* always produces the same output (`JSON.stringify` byte-identical), regardless
|
|
8
|
+
* of input record order. `parseRouterBiasSnapshot` performs no filesystem
|
|
9
|
+
* access itself; the actual disk read lives in agent-session.ts.
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from "crypto";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
import { getAgentDir } from "../config.js";
|
|
14
|
+
import { isRouterFeedbackRecord, ROUTER_FEEDBACK_LANE_TYPES, ROUTER_FEEDBACK_LEN_BUCKETS, ROUTER_FEEDBACK_TASK_CLASSES, } from "./router-feedback-collector.js";
|
|
15
|
+
/** Minimum non-neutral ("strong") record count per cell before any bias applies. */
|
|
16
|
+
export const BIAS_STRONG_THRESHOLD = 5;
|
|
17
|
+
/** Bias magnitude bound, matching the resolver's bias clamp. */
|
|
18
|
+
export const BIAS_MAX_STEPS = 2;
|
|
19
|
+
function cellKeyString(key) {
|
|
20
|
+
return [key.predictedClass, key.laneType, String(key.lenBucket), String(key.hadFence), String(key.hadDiff)].join("|");
|
|
21
|
+
}
|
|
22
|
+
/** +1 "needed more effort", -1 "needed less effort", 0 neutral (no directional bias contribution). */
|
|
23
|
+
function outcomeDirection(outcome) {
|
|
24
|
+
if (outcome === "up" || outcome === "fail" || outcome === "debug-follow-up")
|
|
25
|
+
return 1;
|
|
26
|
+
if (outcome === "down")
|
|
27
|
+
return -1;
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
/** Fixed-key-order JSON so identical records always canonicalize to identical bytes. */
|
|
31
|
+
function canonicalRecordJson(record) {
|
|
32
|
+
return JSON.stringify({
|
|
33
|
+
routerVersion: record.routerVersion,
|
|
34
|
+
laneType: record.laneType,
|
|
35
|
+
predictedClass: record.predictedClass,
|
|
36
|
+
resolvedLevel: record.resolvedLevel,
|
|
37
|
+
acceptedLevel: record.acceptedLevel,
|
|
38
|
+
signal: record.signal,
|
|
39
|
+
outcome: record.outcome,
|
|
40
|
+
lenBucket: record.lenBucket,
|
|
41
|
+
hadFence: record.hadFence,
|
|
42
|
+
hadDiff: record.hadDiff,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function computeBiasSteps(upVotes, downVotes, nStrong) {
|
|
46
|
+
if (nStrong < BIAS_STRONG_THRESHOLD)
|
|
47
|
+
return 0;
|
|
48
|
+
const majority = upVotes - downVotes;
|
|
49
|
+
if (majority === 0)
|
|
50
|
+
return 0;
|
|
51
|
+
const dominance = Math.abs(majority) / nStrong;
|
|
52
|
+
const magnitude = dominance >= 0.75 ? BIAS_MAX_STEPS : 1;
|
|
53
|
+
const signedMagnitude = majority > 0 ? magnitude : -magnitude;
|
|
54
|
+
return Math.max(-BIAS_MAX_STEPS, Math.min(BIAS_MAX_STEPS, signedMagnitude));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Deterministically compile a bounded bias snapshot from raw (possibly
|
|
58
|
+
* malformed) ledger entries. Unknown-shaped entries — including ones with
|
|
59
|
+
* extra keys, missing keys, or out-of-range enum values — are dropped and
|
|
60
|
+
* counted, never thrown. Records tagged with a `routerVersion` other than the
|
|
61
|
+
* target version are also dropped and counted (never merged across router
|
|
62
|
+
* versions into one snapshot). An empty or all-dropped input compiles to an
|
|
63
|
+
* empty-bias snapshot (`biasCells: []`), which is exactly the shipped
|
|
64
|
+
* no-learning behavior — this makes "empty ledger -> zero bias" true by
|
|
65
|
+
* construction, not by a special case.
|
|
66
|
+
*
|
|
67
|
+
* Pure: same `records` (any order) -> same `RouterBiasSnapshot` (byte-
|
|
68
|
+
* identical `JSON.stringify` output), because cells are grouped by an
|
|
69
|
+
* order-independent key and the emitted cell/record ordering is always
|
|
70
|
+
* sorted before serialization.
|
|
71
|
+
*/
|
|
72
|
+
export function compileBiasSnapshot(records, options = {}) {
|
|
73
|
+
const routerVersion = options.routerVersion ?? "v4";
|
|
74
|
+
const valid = [];
|
|
75
|
+
let droppedCount = 0;
|
|
76
|
+
for (const entry of records) {
|
|
77
|
+
if (isRouterFeedbackRecord(entry) && entry.routerVersion === routerVersion) {
|
|
78
|
+
valid.push(entry);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
droppedCount += 1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const cells = new Map();
|
|
85
|
+
for (const record of valid) {
|
|
86
|
+
const key = {
|
|
87
|
+
predictedClass: record.predictedClass,
|
|
88
|
+
laneType: record.laneType,
|
|
89
|
+
lenBucket: record.lenBucket,
|
|
90
|
+
hadFence: record.hadFence,
|
|
91
|
+
hadDiff: record.hadDiff,
|
|
92
|
+
};
|
|
93
|
+
const mapKey = cellKeyString(key);
|
|
94
|
+
const existing = cells.get(mapKey) ?? { ...key, nTotal: 0, upVotes: 0, downVotes: 0 };
|
|
95
|
+
existing.nTotal += 1;
|
|
96
|
+
const direction = outcomeDirection(record.outcome);
|
|
97
|
+
if (direction === 1)
|
|
98
|
+
existing.upVotes += 1;
|
|
99
|
+
else if (direction === -1)
|
|
100
|
+
existing.downVotes += 1;
|
|
101
|
+
cells.set(mapKey, existing);
|
|
102
|
+
}
|
|
103
|
+
const biasCells = [];
|
|
104
|
+
for (const mapKey of [...cells.keys()].sort()) {
|
|
105
|
+
const acc = cells.get(mapKey);
|
|
106
|
+
if (!acc)
|
|
107
|
+
continue;
|
|
108
|
+
const nStrong = acc.upVotes + acc.downVotes;
|
|
109
|
+
biasCells.push({
|
|
110
|
+
predictedClass: acc.predictedClass,
|
|
111
|
+
laneType: acc.laneType,
|
|
112
|
+
lenBucket: acc.lenBucket,
|
|
113
|
+
hadFence: acc.hadFence,
|
|
114
|
+
hadDiff: acc.hadDiff,
|
|
115
|
+
biasSteps: computeBiasSteps(acc.upVotes, acc.downVotes, nStrong),
|
|
116
|
+
nStrong,
|
|
117
|
+
nTotal: acc.nTotal,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const canonicalValid = valid.map(canonicalRecordJson).sort();
|
|
121
|
+
const sourceRecordDigest = createHash("sha256").update(canonicalValid.join("\n")).digest("hex");
|
|
122
|
+
return {
|
|
123
|
+
schemaVersion: "router-bias-snapshot/v1",
|
|
124
|
+
sourceRecordDigest,
|
|
125
|
+
consideredCount: valid.length,
|
|
126
|
+
droppedCount,
|
|
127
|
+
biasCells,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/** Look up the compiled bias for one feature cell; 0 (no bias) when the cell is absent from the snapshot. */
|
|
131
|
+
export function getBiasStepsForCell(snapshot, key) {
|
|
132
|
+
const wantKey = cellKeyString(key);
|
|
133
|
+
for (const cell of snapshot.biasCells) {
|
|
134
|
+
if (cellKeyString(cell) === wantKey)
|
|
135
|
+
return cell.biasSteps;
|
|
136
|
+
}
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
// ============================================================================
|
|
140
|
+
// Snapshot parser / loader (Goal 010 Lane I)
|
|
141
|
+
// ============================================================================
|
|
142
|
+
//
|
|
143
|
+
// Everything below lets a caller safely turn untrusted on-disk JSON text into
|
|
144
|
+
// a validated RouterBiasSnapshot. Same posture as isRouterFeedbackRecord in
|
|
145
|
+
// router-feedback-collector.ts: an exact positive key-set allowlist (not a
|
|
146
|
+
// denylist), bounded per-field checks reusing the ledger's own closed enums,
|
|
147
|
+
// and "never throw, return null/false instead" on any malformed input.
|
|
148
|
+
/** Bounded bias-step values a compiled cell may carry; mirrors RouterBiasSteps. */
|
|
149
|
+
const ROUTER_BIAS_STEPS_VALUES = [-2, -1, 0, 1, 2];
|
|
150
|
+
/** Exact allowed key set for one compiled `RouterBiasCell`, sorted for canonical set-equality comparison. */
|
|
151
|
+
const ROUTER_BIAS_CELL_KEYS = [
|
|
152
|
+
"predictedClass",
|
|
153
|
+
"laneType",
|
|
154
|
+
"lenBucket",
|
|
155
|
+
"hadFence",
|
|
156
|
+
"hadDiff",
|
|
157
|
+
"biasSteps",
|
|
158
|
+
"nStrong",
|
|
159
|
+
"nTotal",
|
|
160
|
+
].sort();
|
|
161
|
+
/** Exact allowed key set for a `RouterBiasSnapshot`, sorted for canonical set-equality comparison. */
|
|
162
|
+
const ROUTER_BIAS_SNAPSHOT_KEYS = [
|
|
163
|
+
"schemaVersion",
|
|
164
|
+
"sourceRecordDigest",
|
|
165
|
+
"consideredCount",
|
|
166
|
+
"droppedCount",
|
|
167
|
+
"biasCells",
|
|
168
|
+
].sort();
|
|
169
|
+
/** sha256 hex digest shape check for `sourceRecordDigest` (never trusts the value is actually a real digest of anything, only that it has the right shape). */
|
|
170
|
+
const SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/;
|
|
171
|
+
function isPlainObject(value) {
|
|
172
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
173
|
+
}
|
|
174
|
+
function hasExactKeySet(value, allowedSortedKeys) {
|
|
175
|
+
const keys = Object.keys(value).sort();
|
|
176
|
+
if (keys.length !== allowedSortedKeys.length)
|
|
177
|
+
return false;
|
|
178
|
+
for (let i = 0; i < keys.length; i++) {
|
|
179
|
+
if (keys[i] !== allowedSortedKeys[i])
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
function isBoundedNonNegativeInteger(value) {
|
|
185
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Strict runtime validator for one compiled bias cell. Exact eight-key
|
|
189
|
+
* allowlist plus bounded per-field checks, reusing the same closed enums as
|
|
190
|
+
* `isRouterFeedbackRecord` (`ROUTER_FEEDBACK_TASK_CLASSES`,
|
|
191
|
+
* `ROUTER_FEEDBACK_LANE_TYPES`, `ROUTER_FEEDBACK_LEN_BUCKETS`) so this
|
|
192
|
+
* validator cannot silently drift from the ledger's own schema. `nStrong` is
|
|
193
|
+
* additionally required to never exceed `nTotal` (true by construction for
|
|
194
|
+
* every cell `compileBiasSnapshot` can produce; a cheap sanity bound against
|
|
195
|
+
* a hand-tampered file).
|
|
196
|
+
*/
|
|
197
|
+
export function isRouterBiasCell(value) {
|
|
198
|
+
if (!isPlainObject(value))
|
|
199
|
+
return false;
|
|
200
|
+
if (!hasExactKeySet(value, ROUTER_BIAS_CELL_KEYS))
|
|
201
|
+
return false;
|
|
202
|
+
if (!ROUTER_FEEDBACK_TASK_CLASSES.includes(value.predictedClass))
|
|
203
|
+
return false;
|
|
204
|
+
if (!ROUTER_FEEDBACK_LANE_TYPES.includes(value.laneType))
|
|
205
|
+
return false;
|
|
206
|
+
if (!ROUTER_FEEDBACK_LEN_BUCKETS.includes(value.lenBucket))
|
|
207
|
+
return false;
|
|
208
|
+
if (typeof value.hadFence !== "boolean")
|
|
209
|
+
return false;
|
|
210
|
+
if (typeof value.hadDiff !== "boolean")
|
|
211
|
+
return false;
|
|
212
|
+
if (!ROUTER_BIAS_STEPS_VALUES.includes(value.biasSteps))
|
|
213
|
+
return false;
|
|
214
|
+
if (!isBoundedNonNegativeInteger(value.nStrong))
|
|
215
|
+
return false;
|
|
216
|
+
if (!isBoundedNonNegativeInteger(value.nTotal))
|
|
217
|
+
return false;
|
|
218
|
+
if (value.nStrong > value.nTotal)
|
|
219
|
+
return false;
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Strict runtime validator for a compiled bias snapshot. Exact five-key
|
|
224
|
+
* allowlist; `biasCells` must be an array whose every element passes
|
|
225
|
+
* `isRouterBiasCell`. Never trusts on-disk content: any unexpected shape,
|
|
226
|
+
* extra key, or out-of-range value returns `false` rather than throwing.
|
|
227
|
+
*/
|
|
228
|
+
export function isRouterBiasSnapshot(value) {
|
|
229
|
+
if (!isPlainObject(value))
|
|
230
|
+
return false;
|
|
231
|
+
if (!hasExactKeySet(value, ROUTER_BIAS_SNAPSHOT_KEYS))
|
|
232
|
+
return false;
|
|
233
|
+
if (value.schemaVersion !== "router-bias-snapshot/v1")
|
|
234
|
+
return false;
|
|
235
|
+
if (typeof value.sourceRecordDigest !== "string" || !SHA256_HEX_PATTERN.test(value.sourceRecordDigest))
|
|
236
|
+
return false;
|
|
237
|
+
if (!isBoundedNonNegativeInteger(value.consideredCount))
|
|
238
|
+
return false;
|
|
239
|
+
if (!isBoundedNonNegativeInteger(value.droppedCount))
|
|
240
|
+
return false;
|
|
241
|
+
if (!Array.isArray(value.biasCells))
|
|
242
|
+
return false;
|
|
243
|
+
for (const cell of value.biasCells) {
|
|
244
|
+
if (!isRouterBiasCell(cell))
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Safe parse of raw (untrusted, e.g. on-disk) JSON text into a validated
|
|
251
|
+
* `RouterBiasSnapshot`. Never throws: malformed JSON or any schema violation
|
|
252
|
+
* returns `null`. Performs no filesystem access itself — callers own the
|
|
253
|
+
* actual read — so this module's zero-I/O determinism guarantee for
|
|
254
|
+
* `compileBiasSnapshot`/`getBiasStepsForCell` is unaffected.
|
|
255
|
+
*/
|
|
256
|
+
export function parseRouterBiasSnapshot(raw) {
|
|
257
|
+
let parsed;
|
|
258
|
+
try {
|
|
259
|
+
parsed = JSON.parse(raw);
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
return isRouterBiasSnapshot(parsed) ? parsed : null;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Default compiled-bias-snapshot path:
|
|
268
|
+
* `<agentDir>/router-feedback/weights/router-bias-snapshot.<version>.json` —
|
|
269
|
+
* matches `compile-bias-snapshot.ts`'s own default `--out` naming exactly, so
|
|
270
|
+
* a snapshot compiled offline for `version` is found here with no extra
|
|
271
|
+
* configuration. Owner-only agent dir, never repo-local (mirrors
|
|
272
|
+
* `getDefaultRouterFeedbackLedgerPath`). Defaults to "v4", the router version
|
|
273
|
+
* this lane wires bias *consumption* for.
|
|
274
|
+
*/
|
|
275
|
+
export function getDefaultRouterBiasSnapshotPath(version = "v4") {
|
|
276
|
+
return join(getAgentDir(), "router-feedback", "weights", `router-bias-snapshot.${version}.json`);
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=reasoning-router-bias.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-router-bias.js","sourceRoot":"","sources":["../../src/core/reasoning-router-bias.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reasoning-router bias compiler — privacy-safe learning ledger.\n *\n * Pure, deterministic compiler from v4 feedback records (see\n * router-feedback-collector.ts) to a bounded, per-cell reasoning-level bias\n * snapshot. No clock, randomness, network, or model calls: the same input array\n * always produces the same output (`JSON.stringify` byte-identical), regardless\n * of input record order. `parseRouterBiasSnapshot` performs no filesystem\n * access itself; the actual disk read lives in agent-session.ts.\n */\n\nimport { createHash } from \"crypto\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.ts\";\nimport {\n\tisRouterFeedbackRecord,\n\tROUTER_FEEDBACK_LANE_TYPES,\n\tROUTER_FEEDBACK_LEN_BUCKETS,\n\tROUTER_FEEDBACK_TASK_CLASSES,\n\ttype RouterFeedbackLaneType,\n\ttype RouterFeedbackLenBucket,\n\ttype RouterFeedbackRecord,\n\ttype RouterFeedbackTaskClass,\n\ttype RouterFeedbackVersion,\n} from \"./router-feedback-collector.ts\";\n\n/** Minimum non-neutral (\"strong\") record count per cell before any bias applies. */\nexport const BIAS_STRONG_THRESHOLD = 5;\n\n/** Bias magnitude bound, matching the resolver's bias clamp. */\nexport const BIAS_MAX_STEPS = 2;\n\n/** Bounded per-cell bias, in ladder steps. Never outside [-2, 2]. */\nexport type RouterBiasSteps = -2 | -1 | 0 | 1 | 2;\n\n/** The feature tuple a bias cell is keyed on — identical dimensions the router already computes locally. */\nexport interface RouterBiasCellKey {\n\treadonly predictedClass: RouterFeedbackTaskClass;\n\treadonly laneType: RouterFeedbackLaneType;\n\treadonly lenBucket: RouterFeedbackLenBucket;\n\treadonly hadFence: boolean;\n\treadonly hadDiff: boolean;\n}\n\nexport interface RouterBiasCell extends RouterBiasCellKey {\n\treadonly biasSteps: RouterBiasSteps;\n\t/** Non-neutral (up/down-signaling) record count for this cell. */\n\treadonly nStrong: number;\n\t/** Total record count for this cell, including neutral (accepted/same/pass) records. */\n\treadonly nTotal: number;\n}\n\nexport interface RouterBiasSnapshot {\n\treadonly schemaVersion: \"router-bias-snapshot/v1\";\n\t/** sha256 hex digest of the canonicalized valid-record set actually compiled; not a prompt hash. */\n\treadonly sourceRecordDigest: string;\n\t/** Count of records that were validated and included in this snapshot. */\n\treadonly consideredCount: number;\n\t/** Count of records excluded from this snapshot (schema-invalid, or router-version mismatch). */\n\treadonly droppedCount: number;\n\t/** Bias cells, sorted deterministically. Empty when there is no ledger data or no cell reaches the threshold. */\n\treadonly biasCells: readonly RouterBiasCell[];\n}\n\nexport interface CompileBiasSnapshotOptions {\n\t/**\n\t * Restrict compilation to one router version. The only supported runtime\n\t * router version is v4; records tagged with any other value are invalid and\n\t * counted in `droppedCount`.\n\t */\n\treadonly routerVersion?: RouterFeedbackVersion;\n}\n\nfunction cellKeyString(key: RouterBiasCellKey): string {\n\treturn [key.predictedClass, key.laneType, String(key.lenBucket), String(key.hadFence), String(key.hadDiff)].join(\n\t\t\"|\",\n\t);\n}\n\n/** +1 \"needed more effort\", -1 \"needed less effort\", 0 neutral (no directional bias contribution). */\nfunction outcomeDirection(outcome: RouterFeedbackRecord[\"outcome\"]): -1 | 0 | 1 {\n\tif (outcome === \"up\" || outcome === \"fail\" || outcome === \"debug-follow-up\") return 1;\n\tif (outcome === \"down\") return -1;\n\treturn 0;\n}\n\n/** Fixed-key-order JSON so identical records always canonicalize to identical bytes. */\nfunction canonicalRecordJson(record: RouterFeedbackRecord): string {\n\treturn JSON.stringify({\n\t\trouterVersion: record.routerVersion,\n\t\tlaneType: record.laneType,\n\t\tpredictedClass: record.predictedClass,\n\t\tresolvedLevel: record.resolvedLevel,\n\t\tacceptedLevel: record.acceptedLevel,\n\t\tsignal: record.signal,\n\t\toutcome: record.outcome,\n\t\tlenBucket: record.lenBucket,\n\t\thadFence: record.hadFence,\n\t\thadDiff: record.hadDiff,\n\t});\n}\n\ninterface CellAccumulator extends RouterBiasCellKey {\n\tnTotal: number;\n\tupVotes: number;\n\tdownVotes: number;\n}\n\nfunction computeBiasSteps(upVotes: number, downVotes: number, nStrong: number): RouterBiasSteps {\n\tif (nStrong < BIAS_STRONG_THRESHOLD) return 0;\n\tconst majority = upVotes - downVotes;\n\tif (majority === 0) return 0;\n\n\tconst dominance = Math.abs(majority) / nStrong;\n\tconst magnitude = dominance >= 0.75 ? BIAS_MAX_STEPS : 1;\n\tconst signedMagnitude = majority > 0 ? magnitude : -magnitude;\n\treturn Math.max(-BIAS_MAX_STEPS, Math.min(BIAS_MAX_STEPS, signedMagnitude)) as RouterBiasSteps;\n}\n\n/**\n * Deterministically compile a bounded bias snapshot from raw (possibly\n * malformed) ledger entries. Unknown-shaped entries — including ones with\n * extra keys, missing keys, or out-of-range enum values — are dropped and\n * counted, never thrown. Records tagged with a `routerVersion` other than the\n * target version are also dropped and counted (never merged across router\n * versions into one snapshot). An empty or all-dropped input compiles to an\n * empty-bias snapshot (`biasCells: []`), which is exactly the shipped\n * no-learning behavior — this makes \"empty ledger -> zero bias\" true by\n * construction, not by a special case.\n *\n * Pure: same `records` (any order) -> same `RouterBiasSnapshot` (byte-\n * identical `JSON.stringify` output), because cells are grouped by an\n * order-independent key and the emitted cell/record ordering is always\n * sorted before serialization.\n */\nexport function compileBiasSnapshot(\n\trecords: readonly unknown[],\n\toptions: CompileBiasSnapshotOptions = {},\n): RouterBiasSnapshot {\n\tconst routerVersion = options.routerVersion ?? \"v4\";\n\tconst valid: RouterFeedbackRecord[] = [];\n\tlet droppedCount = 0;\n\n\tfor (const entry of records) {\n\t\tif (isRouterFeedbackRecord(entry) && entry.routerVersion === routerVersion) {\n\t\t\tvalid.push(entry);\n\t\t} else {\n\t\t\tdroppedCount += 1;\n\t\t}\n\t}\n\n\tconst cells = new Map<string, CellAccumulator>();\n\tfor (const record of valid) {\n\t\tconst key: RouterBiasCellKey = {\n\t\t\tpredictedClass: record.predictedClass,\n\t\t\tlaneType: record.laneType,\n\t\t\tlenBucket: record.lenBucket,\n\t\t\thadFence: record.hadFence,\n\t\t\thadDiff: record.hadDiff,\n\t\t};\n\t\tconst mapKey = cellKeyString(key);\n\t\tconst existing = cells.get(mapKey) ?? { ...key, nTotal: 0, upVotes: 0, downVotes: 0 };\n\t\texisting.nTotal += 1;\n\t\tconst direction = outcomeDirection(record.outcome);\n\t\tif (direction === 1) existing.upVotes += 1;\n\t\telse if (direction === -1) existing.downVotes += 1;\n\t\tcells.set(mapKey, existing);\n\t}\n\n\tconst biasCells: RouterBiasCell[] = [];\n\tfor (const mapKey of [...cells.keys()].sort()) {\n\t\tconst acc = cells.get(mapKey);\n\t\tif (!acc) continue;\n\t\tconst nStrong = acc.upVotes + acc.downVotes;\n\t\tbiasCells.push({\n\t\t\tpredictedClass: acc.predictedClass,\n\t\t\tlaneType: acc.laneType,\n\t\t\tlenBucket: acc.lenBucket,\n\t\t\thadFence: acc.hadFence,\n\t\t\thadDiff: acc.hadDiff,\n\t\t\tbiasSteps: computeBiasSteps(acc.upVotes, acc.downVotes, nStrong),\n\t\t\tnStrong,\n\t\t\tnTotal: acc.nTotal,\n\t\t});\n\t}\n\n\tconst canonicalValid = valid.map(canonicalRecordJson).sort();\n\tconst sourceRecordDigest = createHash(\"sha256\").update(canonicalValid.join(\"\\n\")).digest(\"hex\");\n\n\treturn {\n\t\tschemaVersion: \"router-bias-snapshot/v1\",\n\t\tsourceRecordDigest,\n\t\tconsideredCount: valid.length,\n\t\tdroppedCount,\n\t\tbiasCells,\n\t};\n}\n\n/** Look up the compiled bias for one feature cell; 0 (no bias) when the cell is absent from the snapshot. */\nexport function getBiasStepsForCell(snapshot: RouterBiasSnapshot, key: RouterBiasCellKey): RouterBiasSteps {\n\tconst wantKey = cellKeyString(key);\n\tfor (const cell of snapshot.biasCells) {\n\t\tif (cellKeyString(cell) === wantKey) return cell.biasSteps;\n\t}\n\treturn 0;\n}\n\n// ============================================================================\n// Snapshot parser / loader (Goal 010 Lane I)\n// ============================================================================\n//\n// Everything below lets a caller safely turn untrusted on-disk JSON text into\n// a validated RouterBiasSnapshot. Same posture as isRouterFeedbackRecord in\n// router-feedback-collector.ts: an exact positive key-set allowlist (not a\n// denylist), bounded per-field checks reusing the ledger's own closed enums,\n// and \"never throw, return null/false instead\" on any malformed input.\n\n/** Bounded bias-step values a compiled cell may carry; mirrors RouterBiasSteps. */\nconst ROUTER_BIAS_STEPS_VALUES: readonly RouterBiasSteps[] = [-2, -1, 0, 1, 2];\n\n/** Exact allowed key set for one compiled `RouterBiasCell`, sorted for canonical set-equality comparison. */\nconst ROUTER_BIAS_CELL_KEYS: readonly string[] = [\n\t\"predictedClass\",\n\t\"laneType\",\n\t\"lenBucket\",\n\t\"hadFence\",\n\t\"hadDiff\",\n\t\"biasSteps\",\n\t\"nStrong\",\n\t\"nTotal\",\n].sort();\n\n/** Exact allowed key set for a `RouterBiasSnapshot`, sorted for canonical set-equality comparison. */\nconst ROUTER_BIAS_SNAPSHOT_KEYS: readonly string[] = [\n\t\"schemaVersion\",\n\t\"sourceRecordDigest\",\n\t\"consideredCount\",\n\t\"droppedCount\",\n\t\"biasCells\",\n].sort();\n\n/** sha256 hex digest shape check for `sourceRecordDigest` (never trusts the value is actually a real digest of anything, only that it has the right shape). */\nconst SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction hasExactKeySet(value: Record<string, unknown>, allowedSortedKeys: readonly string[]): boolean {\n\tconst keys = Object.keys(value).sort();\n\tif (keys.length !== allowedSortedKeys.length) return false;\n\tfor (let i = 0; i < keys.length; i++) {\n\t\tif (keys[i] !== allowedSortedKeys[i]) return false;\n\t}\n\treturn true;\n}\n\nfunction isBoundedNonNegativeInteger(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isInteger(value) && value >= 0;\n}\n\n/**\n * Strict runtime validator for one compiled bias cell. Exact eight-key\n * allowlist plus bounded per-field checks, reusing the same closed enums as\n * `isRouterFeedbackRecord` (`ROUTER_FEEDBACK_TASK_CLASSES`,\n * `ROUTER_FEEDBACK_LANE_TYPES`, `ROUTER_FEEDBACK_LEN_BUCKETS`) so this\n * validator cannot silently drift from the ledger's own schema. `nStrong` is\n * additionally required to never exceed `nTotal` (true by construction for\n * every cell `compileBiasSnapshot` can produce; a cheap sanity bound against\n * a hand-tampered file).\n */\nexport function isRouterBiasCell(value: unknown): value is RouterBiasCell {\n\tif (!isPlainObject(value)) return false;\n\tif (!hasExactKeySet(value, ROUTER_BIAS_CELL_KEYS)) return false;\n\tif (!ROUTER_FEEDBACK_TASK_CLASSES.includes(value.predictedClass as RouterFeedbackTaskClass)) return false;\n\tif (!ROUTER_FEEDBACK_LANE_TYPES.includes(value.laneType as RouterFeedbackLaneType)) return false;\n\tif (!ROUTER_FEEDBACK_LEN_BUCKETS.includes(value.lenBucket as RouterFeedbackLenBucket)) return false;\n\tif (typeof value.hadFence !== \"boolean\") return false;\n\tif (typeof value.hadDiff !== \"boolean\") return false;\n\tif (!ROUTER_BIAS_STEPS_VALUES.includes(value.biasSteps as RouterBiasSteps)) return false;\n\tif (!isBoundedNonNegativeInteger(value.nStrong)) return false;\n\tif (!isBoundedNonNegativeInteger(value.nTotal)) return false;\n\tif ((value.nStrong as number) > (value.nTotal as number)) return false;\n\treturn true;\n}\n\n/**\n * Strict runtime validator for a compiled bias snapshot. Exact five-key\n * allowlist; `biasCells` must be an array whose every element passes\n * `isRouterBiasCell`. Never trusts on-disk content: any unexpected shape,\n * extra key, or out-of-range value returns `false` rather than throwing.\n */\nexport function isRouterBiasSnapshot(value: unknown): value is RouterBiasSnapshot {\n\tif (!isPlainObject(value)) return false;\n\tif (!hasExactKeySet(value, ROUTER_BIAS_SNAPSHOT_KEYS)) return false;\n\tif (value.schemaVersion !== \"router-bias-snapshot/v1\") return false;\n\tif (typeof value.sourceRecordDigest !== \"string\" || !SHA256_HEX_PATTERN.test(value.sourceRecordDigest)) return false;\n\tif (!isBoundedNonNegativeInteger(value.consideredCount)) return false;\n\tif (!isBoundedNonNegativeInteger(value.droppedCount)) return false;\n\tif (!Array.isArray(value.biasCells)) return false;\n\tfor (const cell of value.biasCells) {\n\t\tif (!isRouterBiasCell(cell)) return false;\n\t}\n\treturn true;\n}\n\n/**\n * Safe parse of raw (untrusted, e.g. on-disk) JSON text into a validated\n * `RouterBiasSnapshot`. Never throws: malformed JSON or any schema violation\n * returns `null`. Performs no filesystem access itself — callers own the\n * actual read — so this module's zero-I/O determinism guarantee for\n * `compileBiasSnapshot`/`getBiasStepsForCell` is unaffected.\n */\nexport function parseRouterBiasSnapshot(raw: string): RouterBiasSnapshot | null {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\treturn null;\n\t}\n\treturn isRouterBiasSnapshot(parsed) ? parsed : null;\n}\n\n/**\n * Default compiled-bias-snapshot path:\n * `<agentDir>/router-feedback/weights/router-bias-snapshot.<version>.json` —\n * matches `compile-bias-snapshot.ts`'s own default `--out` naming exactly, so\n * a snapshot compiled offline for `version` is found here with no extra\n * configuration. Owner-only agent dir, never repo-local (mirrors\n * `getDefaultRouterFeedbackLedgerPath`). Defaults to \"v4\", the router version\n * this lane wires bias *consumption* for.\n */\nexport function getDefaultRouterBiasSnapshotPath(version: RouterFeedbackVersion = \"v4\"): string {\n\treturn join(getAgentDir(), \"router-feedback\", \"weights\", `router-bias-snapshot.${version}.json`);\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared resolver core for the v4 reasoning router: canonical task-class ->
|
|
3
|
+
* ThinkingLevel rule table, lane-step adjustment, ladder-index clamping, and
|
|
4
|
+
* bounded bias/hint/escalation resolution logic.
|
|
5
|
+
*
|
|
6
|
+
* Deterministic, side-effect-free: no clock, randomness, or I/O.
|
|
7
|
+
*/
|
|
8
|
+
import type { ThinkingLevel } from "omk-agent-core";
|
|
9
|
+
/** The 7-class union used by the v4 classifier. */
|
|
10
|
+
export type ReasoningTaskClass = "trivial" | "simple-edit" | "code-gen" | "debug" | "refactor" | "review" | "plan";
|
|
11
|
+
/** The 6-lane union used by the v4 classifier. */
|
|
12
|
+
export type ReasoningLane = "planner" | "security" | "explorer" | "coder" | "reviewer" | "tester";
|
|
13
|
+
/** Reasoning ladder used for targets and clamping. Intentionally excludes "off". */
|
|
14
|
+
export declare const REASONING_LADDER: readonly ThinkingLevel[];
|
|
15
|
+
/** Static rule table: task class -> recommended ThinkingLevel (before lane adjustment, bias, hint, escalation, and clamping). */
|
|
16
|
+
export declare const TASK_CLASS_THINKING_LEVELS: Readonly<Record<ReasoningTaskClass, ThinkingLevel>>;
|
|
17
|
+
/** Lane adjustment in ladder steps: planner/security escalate, explorer de-escalates. */
|
|
18
|
+
export declare const LANE_STEP: Readonly<Partial<Record<ReasoningLane, 1 | -1>>>;
|
|
19
|
+
/** Maximum magnitude of the learning/consult bias (ladder steps). */
|
|
20
|
+
export declare const BIAS_MAX = 2;
|
|
21
|
+
/** Confidence floor at which an override hint is fused into the target. */
|
|
22
|
+
export declare const HINT_CONFIDENCE_THRESHOLD = 0.7;
|
|
23
|
+
/**
|
|
24
|
+
* Clamp a target ladder index to capability: the highest level in
|
|
25
|
+
* `availableLevels` that is <= the target; if no available level is at/below
|
|
26
|
+
* the target, return the lowest available reasoning level. Never invents a
|
|
27
|
+
* level outside `availableLevels`. `"off"` is only returned when
|
|
28
|
+
* `availableLevels` is empty (callers are expected to bypass the router
|
|
29
|
+
* entirely for models with `reasoning: false`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function clampToAvailable(targetIndex: number, availableLevels: readonly ThinkingLevel[]): ThinkingLevel;
|
|
32
|
+
/**
|
|
33
|
+
* Canonical resolver core for v4.
|
|
34
|
+
*
|
|
35
|
+
* Pipeline: base rule table -> lane step -> bounded bias [-BIAS_MAX,+BIAS_MAX]
|
|
36
|
+
* -> optional hint fusion (bounded ±BIAS_MAX steps toward hint.level, only
|
|
37
|
+
* when hint.confidence >= HINT_CONFIDENCE_THRESHOLD) -> non-negative
|
|
38
|
+
* escalationSteps bump -> clamp to availableLevels.
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveThinkingLevelCore(taskClass: ReasoningTaskClass, availableLevels: readonly ThinkingLevel[], laneType: ReasoningLane | undefined, bias: number, hint: {
|
|
41
|
+
level: ThinkingLevel;
|
|
42
|
+
confidence: number;
|
|
43
|
+
} | null, escalationSteps: number): ThinkingLevel;
|
|
44
|
+
//# sourceMappingURL=reasoning-router-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-router-resolver.d.ts","sourceRoot":"","sources":["../../src/core/reasoning-router-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,mDAAmD;AACnD,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEnH,kDAAkD;AAClD,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAElG,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAAyD,CAAC;AAE/G,iIAAiI;AACjI,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAQ1F,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAItE,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,2EAA2E;AAC3E,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAM7C;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,aAAa,EAAE,GAAG,aAAa,CAU9G;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACvC,SAAS,EAAE,kBAAkB,EAC7B,eAAe,EAAE,SAAS,aAAa,EAAE,EACzC,QAAQ,EAAE,aAAa,GAAG,SAAS,EACnC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EACzD,eAAe,EAAE,MAAM,GACrB,aAAa,CAoBf","sourcesContent":["/**\n * Shared resolver core for the v4 reasoning router: canonical task-class ->\n * ThinkingLevel rule table, lane-step adjustment, ladder-index clamping, and\n * bounded bias/hint/escalation resolution logic.\n *\n * Deterministic, side-effect-free: no clock, randomness, or I/O.\n */\n\nimport type { ThinkingLevel } from \"omk-agent-core\";\n\n/** The 7-class union used by the v4 classifier. */\nexport type ReasoningTaskClass = \"trivial\" | \"simple-edit\" | \"code-gen\" | \"debug\" | \"refactor\" | \"review\" | \"plan\";\n\n/** The 6-lane union used by the v4 classifier. */\nexport type ReasoningLane = \"planner\" | \"security\" | \"explorer\" | \"coder\" | \"reviewer\" | \"tester\";\n\n/** Reasoning ladder used for targets and clamping. Intentionally excludes \"off\". */\nexport const REASONING_LADDER: readonly ThinkingLevel[] = [\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n\n/** Static rule table: task class -> recommended ThinkingLevel (before lane adjustment, bias, hint, escalation, and clamping). */\nexport const TASK_CLASS_THINKING_LEVELS: Readonly<Record<ReasoningTaskClass, ThinkingLevel>> = {\n\ttrivial: \"minimal\",\n\t\"simple-edit\": \"low\",\n\t\"code-gen\": \"medium\",\n\tdebug: \"high\",\n\trefactor: \"high\",\n\treview: \"high\",\n\tplan: \"xhigh\",\n};\n\n/** Lane adjustment in ladder steps: planner/security escalate, explorer de-escalates. */\nexport const LANE_STEP: Readonly<Partial<Record<ReasoningLane, 1 | -1>>> = {\n\tplanner: 1,\n\tsecurity: 1,\n\texplorer: -1,\n};\n\n/** Maximum magnitude of the learning/consult bias (ladder steps). */\nexport const BIAS_MAX = 2;\n/** Confidence floor at which an override hint is fused into the target. */\nexport const HINT_CONFIDENCE_THRESHOLD = 0.7;\n\nfunction clampToLadderIndex(index: number): number {\n\treturn Math.max(0, Math.min(index, REASONING_LADDER.length - 1));\n}\n\n/**\n * Clamp a target ladder index to capability: the highest level in\n * `availableLevels` that is <= the target; if no available level is at/below\n * the target, return the lowest available reasoning level. Never invents a\n * level outside `availableLevels`. `\"off\"` is only returned when\n * `availableLevels` is empty (callers are expected to bypass the router\n * entirely for models with `reasoning: false`).\n */\nexport function clampToAvailable(targetIndex: number, availableLevels: readonly ThinkingLevel[]): ThinkingLevel {\n\tconst availableOnLadder = REASONING_LADDER.filter((level) => availableLevels.includes(level));\n\tif (availableOnLadder.length === 0) {\n\t\treturn availableLevels[0] ?? \"off\";\n\t}\n\tfor (let i = targetIndex; i >= 0; i--) {\n\t\tconst candidate = REASONING_LADDER[i];\n\t\tif (availableOnLadder.includes(candidate)) return candidate;\n\t}\n\treturn availableOnLadder[0];\n}\n\n/**\n * Canonical resolver core for v4.\n *\n * Pipeline: base rule table -> lane step -> bounded bias [-BIAS_MAX,+BIAS_MAX]\n * -> optional hint fusion (bounded ±BIAS_MAX steps toward hint.level, only\n * when hint.confidence >= HINT_CONFIDENCE_THRESHOLD) -> non-negative\n * escalationSteps bump -> clamp to availableLevels.\n */\nexport function resolveThinkingLevelCore(\n\ttaskClass: ReasoningTaskClass,\n\tavailableLevels: readonly ThinkingLevel[],\n\tlaneType: ReasoningLane | undefined,\n\tbias: number,\n\thint: { level: ThinkingLevel; confidence: number } | null,\n\tescalationSteps: number,\n): ThinkingLevel {\n\tconst baseIndex = REASONING_LADDER.indexOf(TASK_CLASS_THINKING_LEVELS[taskClass]);\n\tconst laneStep = laneType ? (LANE_STEP[laneType] ?? 0) : 0;\n\tconst biasClamped = Math.max(-BIAS_MAX, Math.min(BIAS_MAX, bias));\n\tlet targetIndex = clampToLadderIndex(baseIndex + laneStep + biasClamped);\n\n\tif (hint !== null && hint.confidence >= HINT_CONFIDENCE_THRESHOLD) {\n\t\tconst hintIndex = REASONING_LADDER.indexOf(hint.level);\n\t\tif (hintIndex >= 0) {\n\t\t\tconst delta = hintIndex - targetIndex;\n\t\t\tconst step = Math.max(-BIAS_MAX, Math.min(BIAS_MAX, delta));\n\t\t\ttargetIndex = clampToLadderIndex(targetIndex + step);\n\t\t}\n\t}\n\n\tif (escalationSteps > 0) {\n\t\ttargetIndex = clampToLadderIndex(targetIndex + escalationSteps);\n\t}\n\n\treturn clampToAvailable(targetIndex, availableLevels);\n}\n"]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared resolver core for the v4 reasoning router: canonical task-class ->
|
|
3
|
+
* ThinkingLevel rule table, lane-step adjustment, ladder-index clamping, and
|
|
4
|
+
* bounded bias/hint/escalation resolution logic.
|
|
5
|
+
*
|
|
6
|
+
* Deterministic, side-effect-free: no clock, randomness, or I/O.
|
|
7
|
+
*/
|
|
8
|
+
/** Reasoning ladder used for targets and clamping. Intentionally excludes "off". */
|
|
9
|
+
export const REASONING_LADDER = ["minimal", "low", "medium", "high", "xhigh", "max"];
|
|
10
|
+
/** Static rule table: task class -> recommended ThinkingLevel (before lane adjustment, bias, hint, escalation, and clamping). */
|
|
11
|
+
export const TASK_CLASS_THINKING_LEVELS = {
|
|
12
|
+
trivial: "minimal",
|
|
13
|
+
"simple-edit": "low",
|
|
14
|
+
"code-gen": "medium",
|
|
15
|
+
debug: "high",
|
|
16
|
+
refactor: "high",
|
|
17
|
+
review: "high",
|
|
18
|
+
plan: "xhigh",
|
|
19
|
+
};
|
|
20
|
+
/** Lane adjustment in ladder steps: planner/security escalate, explorer de-escalates. */
|
|
21
|
+
export const LANE_STEP = {
|
|
22
|
+
planner: 1,
|
|
23
|
+
security: 1,
|
|
24
|
+
explorer: -1,
|
|
25
|
+
};
|
|
26
|
+
/** Maximum magnitude of the learning/consult bias (ladder steps). */
|
|
27
|
+
export const BIAS_MAX = 2;
|
|
28
|
+
/** Confidence floor at which an override hint is fused into the target. */
|
|
29
|
+
export const HINT_CONFIDENCE_THRESHOLD = 0.7;
|
|
30
|
+
function clampToLadderIndex(index) {
|
|
31
|
+
return Math.max(0, Math.min(index, REASONING_LADDER.length - 1));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Clamp a target ladder index to capability: the highest level in
|
|
35
|
+
* `availableLevels` that is <= the target; if no available level is at/below
|
|
36
|
+
* the target, return the lowest available reasoning level. Never invents a
|
|
37
|
+
* level outside `availableLevels`. `"off"` is only returned when
|
|
38
|
+
* `availableLevels` is empty (callers are expected to bypass the router
|
|
39
|
+
* entirely for models with `reasoning: false`).
|
|
40
|
+
*/
|
|
41
|
+
export function clampToAvailable(targetIndex, availableLevels) {
|
|
42
|
+
const availableOnLadder = REASONING_LADDER.filter((level) => availableLevels.includes(level));
|
|
43
|
+
if (availableOnLadder.length === 0) {
|
|
44
|
+
return availableLevels[0] ?? "off";
|
|
45
|
+
}
|
|
46
|
+
for (let i = targetIndex; i >= 0; i--) {
|
|
47
|
+
const candidate = REASONING_LADDER[i];
|
|
48
|
+
if (availableOnLadder.includes(candidate))
|
|
49
|
+
return candidate;
|
|
50
|
+
}
|
|
51
|
+
return availableOnLadder[0];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Canonical resolver core for v4.
|
|
55
|
+
*
|
|
56
|
+
* Pipeline: base rule table -> lane step -> bounded bias [-BIAS_MAX,+BIAS_MAX]
|
|
57
|
+
* -> optional hint fusion (bounded ±BIAS_MAX steps toward hint.level, only
|
|
58
|
+
* when hint.confidence >= HINT_CONFIDENCE_THRESHOLD) -> non-negative
|
|
59
|
+
* escalationSteps bump -> clamp to availableLevels.
|
|
60
|
+
*/
|
|
61
|
+
export function resolveThinkingLevelCore(taskClass, availableLevels, laneType, bias, hint, escalationSteps) {
|
|
62
|
+
const baseIndex = REASONING_LADDER.indexOf(TASK_CLASS_THINKING_LEVELS[taskClass]);
|
|
63
|
+
const laneStep = laneType ? (LANE_STEP[laneType] ?? 0) : 0;
|
|
64
|
+
const biasClamped = Math.max(-BIAS_MAX, Math.min(BIAS_MAX, bias));
|
|
65
|
+
let targetIndex = clampToLadderIndex(baseIndex + laneStep + biasClamped);
|
|
66
|
+
if (hint !== null && hint.confidence >= HINT_CONFIDENCE_THRESHOLD) {
|
|
67
|
+
const hintIndex = REASONING_LADDER.indexOf(hint.level);
|
|
68
|
+
if (hintIndex >= 0) {
|
|
69
|
+
const delta = hintIndex - targetIndex;
|
|
70
|
+
const step = Math.max(-BIAS_MAX, Math.min(BIAS_MAX, delta));
|
|
71
|
+
targetIndex = clampToLadderIndex(targetIndex + step);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (escalationSteps > 0) {
|
|
75
|
+
targetIndex = clampToLadderIndex(targetIndex + escalationSteps);
|
|
76
|
+
}
|
|
77
|
+
return clampToAvailable(targetIndex, availableLevels);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=reasoning-router-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-router-resolver.js","sourceRoot":"","sources":["../../src/core/reasoning-router-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared resolver core for the v4 reasoning router: canonical task-class ->\n * ThinkingLevel rule table, lane-step adjustment, ladder-index clamping, and\n * bounded bias/hint/escalation resolution logic.\n *\n * Deterministic, side-effect-free: no clock, randomness, or I/O.\n */\n\nimport type { ThinkingLevel } from \"omk-agent-core\";\n\n/** The 7-class union used by the v4 classifier. */\nexport type ReasoningTaskClass = \"trivial\" | \"simple-edit\" | \"code-gen\" | \"debug\" | \"refactor\" | \"review\" | \"plan\";\n\n/** The 6-lane union used by the v4 classifier. */\nexport type ReasoningLane = \"planner\" | \"security\" | \"explorer\" | \"coder\" | \"reviewer\" | \"tester\";\n\n/** Reasoning ladder used for targets and clamping. Intentionally excludes \"off\". */\nexport const REASONING_LADDER: readonly ThinkingLevel[] = [\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n\n/** Static rule table: task class -> recommended ThinkingLevel (before lane adjustment, bias, hint, escalation, and clamping). */\nexport const TASK_CLASS_THINKING_LEVELS: Readonly<Record<ReasoningTaskClass, ThinkingLevel>> = {\n\ttrivial: \"minimal\",\n\t\"simple-edit\": \"low\",\n\t\"code-gen\": \"medium\",\n\tdebug: \"high\",\n\trefactor: \"high\",\n\treview: \"high\",\n\tplan: \"xhigh\",\n};\n\n/** Lane adjustment in ladder steps: planner/security escalate, explorer de-escalates. */\nexport const LANE_STEP: Readonly<Partial<Record<ReasoningLane, 1 | -1>>> = {\n\tplanner: 1,\n\tsecurity: 1,\n\texplorer: -1,\n};\n\n/** Maximum magnitude of the learning/consult bias (ladder steps). */\nexport const BIAS_MAX = 2;\n/** Confidence floor at which an override hint is fused into the target. */\nexport const HINT_CONFIDENCE_THRESHOLD = 0.7;\n\nfunction clampToLadderIndex(index: number): number {\n\treturn Math.max(0, Math.min(index, REASONING_LADDER.length - 1));\n}\n\n/**\n * Clamp a target ladder index to capability: the highest level in\n * `availableLevels` that is <= the target; if no available level is at/below\n * the target, return the lowest available reasoning level. Never invents a\n * level outside `availableLevels`. `\"off\"` is only returned when\n * `availableLevels` is empty (callers are expected to bypass the router\n * entirely for models with `reasoning: false`).\n */\nexport function clampToAvailable(targetIndex: number, availableLevels: readonly ThinkingLevel[]): ThinkingLevel {\n\tconst availableOnLadder = REASONING_LADDER.filter((level) => availableLevels.includes(level));\n\tif (availableOnLadder.length === 0) {\n\t\treturn availableLevels[0] ?? \"off\";\n\t}\n\tfor (let i = targetIndex; i >= 0; i--) {\n\t\tconst candidate = REASONING_LADDER[i];\n\t\tif (availableOnLadder.includes(candidate)) return candidate;\n\t}\n\treturn availableOnLadder[0];\n}\n\n/**\n * Canonical resolver core for v4.\n *\n * Pipeline: base rule table -> lane step -> bounded bias [-BIAS_MAX,+BIAS_MAX]\n * -> optional hint fusion (bounded ±BIAS_MAX steps toward hint.level, only\n * when hint.confidence >= HINT_CONFIDENCE_THRESHOLD) -> non-negative\n * escalationSteps bump -> clamp to availableLevels.\n */\nexport function resolveThinkingLevelCore(\n\ttaskClass: ReasoningTaskClass,\n\tavailableLevels: readonly ThinkingLevel[],\n\tlaneType: ReasoningLane | undefined,\n\tbias: number,\n\thint: { level: ThinkingLevel; confidence: number } | null,\n\tescalationSteps: number,\n): ThinkingLevel {\n\tconst baseIndex = REASONING_LADDER.indexOf(TASK_CLASS_THINKING_LEVELS[taskClass]);\n\tconst laneStep = laneType ? (LANE_STEP[laneType] ?? 0) : 0;\n\tconst biasClamped = Math.max(-BIAS_MAX, Math.min(BIAS_MAX, bias));\n\tlet targetIndex = clampToLadderIndex(baseIndex + laneStep + biasClamped);\n\n\tif (hint !== null && hint.confidence >= HINT_CONFIDENCE_THRESHOLD) {\n\t\tconst hintIndex = REASONING_LADDER.indexOf(hint.level);\n\t\tif (hintIndex >= 0) {\n\t\t\tconst delta = hintIndex - targetIndex;\n\t\t\tconst step = Math.max(-BIAS_MAX, Math.min(BIAS_MAX, delta));\n\t\t\ttargetIndex = clampToLadderIndex(targetIndex + step);\n\t\t}\n\t}\n\n\tif (escalationSteps > 0) {\n\t\ttargetIndex = clampToLadderIndex(targetIndex + escalationSteps);\n\t}\n\n\treturn clampToAvailable(targetIndex, availableLevels);\n}\n"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reasoning-router v4 — frozen types and calibratable weight schema.
|
|
3
|
+
*
|
|
4
|
+
* Pure, side-effect-free, erasable TypeScript. No runtime imports. This file
|
|
5
|
+
* owns the closed task/lane unions, named scorer weights, and calibrated default
|
|
6
|
+
* preset used by `/think auto`.
|
|
7
|
+
*
|
|
8
|
+
* `keywordFamily` is the class -> weight table for whole-prompt keyword-family
|
|
9
|
+
* patterns (see reasoning-router-v4.ts). `trivial` carries weight 0 because it
|
|
10
|
+
* is reached only via the zero-score fallback cascade.
|
|
11
|
+
*
|
|
12
|
+
* `secondClauseIntent` applies a bounded bonus when a second, distinct leading
|
|
13
|
+
* verb is found after a short-range conjunction split. `negationWindowChars`
|
|
14
|
+
* bounds negation gating so a nearby "don't/skip/without/no need" cue can
|
|
15
|
+
* suppress a whole-prompt signal without crossing a clause boundary.
|
|
16
|
+
*
|
|
17
|
+
* `lowConfidenceThreshold` / `highConfidenceThreshold` bound the confidence
|
|
18
|
+
* bands (`ClassifierVerdictV4.confidenceBand`) computed from
|
|
19
|
+
* `margin / topScore` in reasoning-router-v4.ts. They are metadata-only: they
|
|
20
|
+
* never change `taskClass`, only how `resolveThinkingLevelV4WithUncertainty`
|
|
21
|
+
* treats an already-decided class.
|
|
22
|
+
*/
|
|
23
|
+
/** Closed set of v4 task classes. */
|
|
24
|
+
export type TaskClassV4 = "trivial" | "simple-edit" | "code-gen" | "debug" | "refactor" | "review" | "plan";
|
|
25
|
+
/** Subagent lane types recognized by the v4 classifier and resolvers. */
|
|
26
|
+
export type ReasoningLaneTypeV4 = "planner" | "security" | "explorer" | "coder" | "reviewer" | "tester";
|
|
27
|
+
/** Fixed iteration order over TaskClassV4 (matches the type-declaration order; also v4's argmax tie-break precedence, highest first). */
|
|
28
|
+
export declare const TASK_CLASSES_V4: readonly TaskClassV4[];
|
|
29
|
+
/**
|
|
30
|
+
* Named weight schema for the v4 scorer (reasoning-router-v4.ts). Every field
|
|
31
|
+
* is a plain, deterministic integer/ratio; there is no clock, randomness, or
|
|
32
|
+
* I/O anywhere in this file or its consumer.
|
|
33
|
+
*/
|
|
34
|
+
export interface RouterWeightsV4 {
|
|
35
|
+
/** Bump to "code-gen" when a code fence or diff hunk is present. */
|
|
36
|
+
readonly codeFenceOrDiff: number;
|
|
37
|
+
/** Bump to the primary leading-clause intent class, if any. */
|
|
38
|
+
readonly leadingIntent: number;
|
|
39
|
+
/** Bump to a distinct second-clause intent class in a detected compound prompt (v4-new; default half of `leadingIntent`). */
|
|
40
|
+
readonly secondClauseIntent: number;
|
|
41
|
+
/** Bump to "simple-edit" when the composite local-edit feature fires. */
|
|
42
|
+
readonly localEdit: number;
|
|
43
|
+
/** Bump to "debug" when the composite diagnostic-evidence feature fires. */
|
|
44
|
+
readonly diagnosticEvidence: number;
|
|
45
|
+
/** Bump to "review" when the composite review-scope feature fires. */
|
|
46
|
+
readonly reviewScope: number;
|
|
47
|
+
/** Bump to "plan" when the composite plan-brief feature fires. */
|
|
48
|
+
readonly planBrief: number;
|
|
49
|
+
/** Bump to "plan" for release-bound operational runbooks (commit/push/tag/publish/changelog/CI bundles). */
|
|
50
|
+
readonly operationalRunbook: number;
|
|
51
|
+
/** Bump to "refactor" when the refactor-cue pattern matches anywhere in the prompt. */
|
|
52
|
+
readonly refactorCue: number;
|
|
53
|
+
/** Bump to "code-gen" when an implementation-object noun is present. */
|
|
54
|
+
readonly implementationObject: number;
|
|
55
|
+
/** Per-class whole-prompt keyword-family bump; trivial has no family, 0. */
|
|
56
|
+
readonly keywordFamily: Readonly<Record<TaskClassV4, number>>;
|
|
57
|
+
/** Bump to "code-gen" for a bare "add" keyword, gated by !localEdit. */
|
|
58
|
+
readonly addKeyword: number;
|
|
59
|
+
/** Bump to the class matching `history[0]`, if supplied (v4-new; 0 under DEFAULT_WEIGHTS_V4, inert until calibrated). */
|
|
60
|
+
readonly multiTurnPrior: number;
|
|
61
|
+
/** Linear per-bucket bump applied to debug/review/plan under context pressure (v4-new; 0 under DEFAULT_WEIGHTS_V4, inert until calibrated). */
|
|
62
|
+
readonly pressureBucket: number;
|
|
63
|
+
/** Bump to the class matching an externally supplied judge vote, if any (v4-new; 0 under DEFAULT_WEIGHTS_V4, inert until calibrated). */
|
|
64
|
+
readonly judgeVote: number;
|
|
65
|
+
/** Bounded look-back window (characters) for negation-cue gating, never crossing a .,;!? boundary. */
|
|
66
|
+
readonly negationWindowChars: number;
|
|
67
|
+
/** confidence <= this value bands as "low" (confidence is margin / topScore, in [0, 1]). */
|
|
68
|
+
readonly lowConfidenceThreshold: number;
|
|
69
|
+
/** confidence >= this value bands as "high". */
|
|
70
|
+
readonly highConfidenceThreshold: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Calibrated default preset. The named weights below are the production v4
|
|
74
|
+
* scorer configuration covered by
|
|
75
|
+
* test/suite/regressions/013-reasoning-router-v4-accuracy.test.ts. Extension
|
|
76
|
+
* fields (`multiTurnPrior`, `pressureBucket`, `judgeVote`) are wired but inert
|
|
77
|
+
* until a future calibration changes them.
|
|
78
|
+
*/
|
|
79
|
+
export declare const DEFAULT_WEIGHTS_V4: RouterWeightsV4;
|
|
80
|
+
//# sourceMappingURL=reasoning-router-v4-weights.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-router-v4-weights.d.ts","sourceRoot":"","sources":["../../src/core/reasoning-router-v4-weights.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,qCAAqC;AACrC,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE5G,yEAAyE;AACzE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAExG,yIAAyI;AACzI,eAAO,MAAM,eAAe,EAAE,SAAS,WAAW,EAQjD,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,6HAA6H;IAC7H,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4GAA4G;IAC5G,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,uFAAuF;IACvF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9D,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yHAAyH;IACzH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,+IAA+I;IAC/I,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,yIAAyI;IACzI,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sGAAsG;IACtG,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,4FAA4F;IAC5F,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,gDAAgD;IAChD,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;CACzC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,eA2BhC,CAAC","sourcesContent":["/**\n * Reasoning-router v4 — frozen types and calibratable weight schema.\n *\n * Pure, side-effect-free, erasable TypeScript. No runtime imports. This file\n * owns the closed task/lane unions, named scorer weights, and calibrated default\n * preset used by `/think auto`.\n *\n * `keywordFamily` is the class -> weight table for whole-prompt keyword-family\n * patterns (see reasoning-router-v4.ts). `trivial` carries weight 0 because it\n * is reached only via the zero-score fallback cascade.\n *\n * `secondClauseIntent` applies a bounded bonus when a second, distinct leading\n * verb is found after a short-range conjunction split. `negationWindowChars`\n * bounds negation gating so a nearby \"don't/skip/without/no need\" cue can\n * suppress a whole-prompt signal without crossing a clause boundary.\n *\n * `lowConfidenceThreshold` / `highConfidenceThreshold` bound the confidence\n * bands (`ClassifierVerdictV4.confidenceBand`) computed from\n * `margin / topScore` in reasoning-router-v4.ts. They are metadata-only: they\n * never change `taskClass`, only how `resolveThinkingLevelV4WithUncertainty`\n * treats an already-decided class.\n */\n\n/** Closed set of v4 task classes. */\nexport type TaskClassV4 = \"trivial\" | \"simple-edit\" | \"code-gen\" | \"debug\" | \"refactor\" | \"review\" | \"plan\";\n\n/** Subagent lane types recognized by the v4 classifier and resolvers. */\nexport type ReasoningLaneTypeV4 = \"planner\" | \"security\" | \"explorer\" | \"coder\" | \"reviewer\" | \"tester\";\n\n/** Fixed iteration order over TaskClassV4 (matches the type-declaration order; also v4's argmax tie-break precedence, highest first). */\nexport const TASK_CLASSES_V4: readonly TaskClassV4[] = [\n\t\"debug\",\n\t\"refactor\",\n\t\"review\",\n\t\"plan\",\n\t\"simple-edit\",\n\t\"code-gen\",\n\t\"trivial\",\n];\n\n/**\n * Named weight schema for the v4 scorer (reasoning-router-v4.ts). Every field\n * is a plain, deterministic integer/ratio; there is no clock, randomness, or\n * I/O anywhere in this file or its consumer.\n */\nexport interface RouterWeightsV4 {\n\t/** Bump to \"code-gen\" when a code fence or diff hunk is present. */\n\treadonly codeFenceOrDiff: number;\n\t/** Bump to the primary leading-clause intent class, if any. */\n\treadonly leadingIntent: number;\n\t/** Bump to a distinct second-clause intent class in a detected compound prompt (v4-new; default half of `leadingIntent`). */\n\treadonly secondClauseIntent: number;\n\t/** Bump to \"simple-edit\" when the composite local-edit feature fires. */\n\treadonly localEdit: number;\n\t/** Bump to \"debug\" when the composite diagnostic-evidence feature fires. */\n\treadonly diagnosticEvidence: number;\n\t/** Bump to \"review\" when the composite review-scope feature fires. */\n\treadonly reviewScope: number;\n\t/** Bump to \"plan\" when the composite plan-brief feature fires. */\n\treadonly planBrief: number;\n\t/** Bump to \"plan\" for release-bound operational runbooks (commit/push/tag/publish/changelog/CI bundles). */\n\treadonly operationalRunbook: number;\n\t/** Bump to \"refactor\" when the refactor-cue pattern matches anywhere in the prompt. */\n\treadonly refactorCue: number;\n\t/** Bump to \"code-gen\" when an implementation-object noun is present. */\n\treadonly implementationObject: number;\n\t/** Per-class whole-prompt keyword-family bump; trivial has no family, 0. */\n\treadonly keywordFamily: Readonly<Record<TaskClassV4, number>>;\n\t/** Bump to \"code-gen\" for a bare \"add\" keyword, gated by !localEdit. */\n\treadonly addKeyword: number;\n\t/** Bump to the class matching `history[0]`, if supplied (v4-new; 0 under DEFAULT_WEIGHTS_V4, inert until calibrated). */\n\treadonly multiTurnPrior: number;\n\t/** Linear per-bucket bump applied to debug/review/plan under context pressure (v4-new; 0 under DEFAULT_WEIGHTS_V4, inert until calibrated). */\n\treadonly pressureBucket: number;\n\t/** Bump to the class matching an externally supplied judge vote, if any (v4-new; 0 under DEFAULT_WEIGHTS_V4, inert until calibrated). */\n\treadonly judgeVote: number;\n\t/** Bounded look-back window (characters) for negation-cue gating, never crossing a .,;!? boundary. */\n\treadonly negationWindowChars: number;\n\t/** confidence <= this value bands as \"low\" (confidence is margin / topScore, in [0, 1]). */\n\treadonly lowConfidenceThreshold: number;\n\t/** confidence >= this value bands as \"high\". */\n\treadonly highConfidenceThreshold: number;\n}\n\n/**\n * Calibrated default preset. The named weights below are the production v4\n * scorer configuration covered by\n * test/suite/regressions/013-reasoning-router-v4-accuracy.test.ts. Extension\n * fields (`multiTurnPrior`, `pressureBucket`, `judgeVote`) are wired but inert\n * until a future calibration changes them.\n */\nexport const DEFAULT_WEIGHTS_V4: RouterWeightsV4 = {\n\tcodeFenceOrDiff: 4,\n\tleadingIntent: 8,\n\tsecondClauseIntent: 4,\n\tlocalEdit: 7,\n\tdiagnosticEvidence: 7,\n\treviewScope: 3,\n\tplanBrief: 8,\n\toperationalRunbook: 8,\n\trefactorCue: 6,\n\timplementationObject: 3,\n\tkeywordFamily: {\n\t\ttrivial: 0,\n\t\t\"simple-edit\": 4,\n\t\t\"code-gen\": 4,\n\t\tdebug: 4,\n\t\trefactor: 4,\n\t\treview: 4,\n\t\tplan: 4,\n\t},\n\taddKeyword: 1,\n\tmultiTurnPrior: 0,\n\tpressureBucket: 0,\n\tjudgeVote: 0,\n\tnegationWindowChars: 24,\n\tlowConfidenceThreshold: 0.35,\n\thighConfidenceThreshold: 0.7,\n};\n"]}
|