open-multi-agent-kit 0.90.4 → 0.90.6
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 +54 -0
- package/README.md +9 -9
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/codexbar-cli.d.ts +39 -0
- package/dist/codexbar-cli.d.ts.map +1 -0
- package/dist/codexbar-cli.js +230 -0
- package/dist/codexbar-cli.js.map +1 -0
- package/dist/commands/doctor-provider.d.ts.map +1 -1
- package/dist/commands/doctor-provider.js +16 -3
- package/dist/commands/doctor-provider.js.map +1 -1
- package/dist/core/adaptorch-bridge.d.ts +14 -21
- package/dist/core/adaptorch-bridge.d.ts.map +1 -1
- package/dist/core/adaptorch-bridge.js +11 -18
- package/dist/core/adaptorch-bridge.js.map +1 -1
- package/dist/core/agent-session.d.ts +6 -39
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +37 -107
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/codexbar-adapter.d.ts +44 -0
- package/dist/core/codexbar-adapter.d.ts.map +1 -0
- package/dist/core/codexbar-adapter.js +211 -0
- package/dist/core/codexbar-adapter.js.map +1 -0
- package/dist/core/compaction/index.d.ts +2 -0
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/index.js +2 -0
- package/dist/core/compaction/index.js.map +1 -1
- package/dist/core/compaction/model-policy.d.ts +9 -0
- package/dist/core/compaction/model-policy.d.ts.map +1 -0
- package/dist/core/compaction/model-policy.js +37 -0
- package/dist/core/compaction/model-policy.js.map +1 -0
- package/dist/core/compaction/resume-policy.d.ts +6 -0
- package/dist/core/compaction/resume-policy.d.ts.map +1 -0
- package/dist/core/compaction/resume-policy.js +8 -0
- package/dist/core/compaction/resume-policy.js.map +1 -0
- 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 +68 -1
- package/dist/core/domain-loadouts.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +12 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +3 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +15 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/grok-harness-dispatch.d.ts +22 -0
- package/dist/core/grok-harness-dispatch.d.ts.map +1 -0
- package/dist/core/grok-harness-dispatch.js +44 -0
- package/dist/core/grok-harness-dispatch.js.map +1 -0
- package/dist/core/grok-harness.d.ts +25 -0
- package/dist/core/grok-harness.d.ts.map +1 -0
- package/dist/core/grok-harness.js +61 -0
- package/dist/core/grok-harness.js.map +1 -0
- package/dist/core/grok-playbook.d.ts +7 -0
- package/dist/core/grok-playbook.d.ts.map +1 -0
- package/dist/core/grok-playbook.js +34 -0
- package/dist/core/grok-playbook.js.map +1 -0
- package/dist/core/reasoning-router-bias.d.ts +11 -23
- package/dist/core/reasoning-router-bias.d.ts.map +1 -1
- package/dist/core/reasoning-router-bias.js +9 -18
- package/dist/core/reasoning-router-bias.js.map +1 -1
- 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 +28 -72
- package/dist/core/reasoning-router-v4-weights.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4-weights.js +16 -60
- package/dist/core/reasoning-router-v4-weights.js.map +1 -1
- package/dist/core/reasoning-router-v4.d.ts +25 -70
- package/dist/core/reasoning-router-v4.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4.js +46 -144
- package/dist/core/reasoning-router-v4.js.map +1 -1
- package/dist/core/router-feedback-collector.d.ts +13 -16
- package/dist/core/router-feedback-collector.d.ts.map +1 -1
- package/dist/core/router-feedback-collector.js +5 -8
- package/dist/core/router-feedback-collector.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +31 -12
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +4 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +16 -98
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +3 -2
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +42 -17
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/correctness-wall.md +117 -0
- package/docs/grok-harness.md +63 -0
- package/docs/loadout-domains/README.md +2 -1
- package/docs/loadout-domains/ai-agent-ops.md +8 -1
- package/docs/loadout-domains/grok-harness.md +74 -0
- package/docs/providers.md +4 -0
- package/docs/usage.md +11 -12
- package/examples/extensions/correctness-wall/LOADOUT.md +87 -0
- package/examples/extensions/correctness-wall/README.md +111 -0
- package/examples/extensions/correctness-wall/adjudication-fixture.ts +151 -0
- package/examples/extensions/correctness-wall/index.ts +470 -0
- package/examples/extensions/correctness-wall/repair-state.ts +77 -0
- package/examples/extensions/correctness-wall/wall-cache.ts +81 -0
- 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 +5 -5
- 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/reasoning-router-v2.d.ts +0 -94
- package/dist/core/reasoning-router-v2.d.ts.map +0 -1
- package/dist/core/reasoning-router-v2.js +0 -288
- package/dist/core/reasoning-router-v2.js.map +0 -1
- package/dist/core/reasoning-router-v3.d.ts +0 -15
- package/dist/core/reasoning-router-v3.d.ts.map +0 -1
- package/dist/core/reasoning-router-v3.js +0 -193
- package/dist/core/reasoning-router-v3.js.map +0 -1
- package/dist/core/reasoning-router-weights.d.ts +0 -155
- package/dist/core/reasoning-router-weights.d.ts.map +0 -1
- package/dist/core/reasoning-router-weights.js +0 -204
- package/dist/core/reasoning-router-weights.js.map +0 -1
- package/dist/core/reasoning-router.d.ts +0 -44
- package/dist/core/reasoning-router.d.ts.map +0 -1
- package/dist/core/reasoning-router.js +0 -138
- package/dist/core/reasoning-router.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
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reasoning-router v2 — frozen types, feature vector, weight schema, the
|
|
3
|
-
* V1_COMPAT_WEIGHTS v1-exact oracle (Req 1.4), and the DEFAULT_WEIGHTS
|
|
4
|
-
* calibrated production preset (Goal 004 Lane I5b).
|
|
5
|
-
*
|
|
6
|
-
* Pure, side-effect-free, erasable TypeScript. No runtime imports.
|
|
7
|
-
*
|
|
8
|
-
* ============================================================================
|
|
9
|
-
* WEIGHT PRESETS — V1_COMPAT_WEIGHTS (v1 oracle) + DEFAULT_WEIGHTS (calibrated)
|
|
10
|
-
* ============================================================================
|
|
11
|
-
* The v1 classifier (reasoning-router.ts) is a first-match keyword cascade with
|
|
12
|
-
* FIXED precedence: debug > refactor > review > plan > simple-edit > code-gen,
|
|
13
|
-
* then fence/diff, then length, then lane fallback, then default code-gen.
|
|
14
|
-
*
|
|
15
|
-
* We reproduce that precedence as DIGIT-POSITION weights. The keyword family at
|
|
16
|
-
* precedence rank r contributes exactly 10^(6-r) to ITS OWN class score when its
|
|
17
|
-
* pattern matches (strongKeyword diagonal, 0/1 match indicator):
|
|
18
|
-
*
|
|
19
|
-
* debug r=1 -> 10^5 = 100000
|
|
20
|
-
* refactor r=2 -> 10^4 = 10000
|
|
21
|
-
* review r=3 -> 10^3 = 1000
|
|
22
|
-
* plan r=4 -> 10^2 = 100
|
|
23
|
-
* simple-edit r=5 -> 10^1 = 10
|
|
24
|
-
* code-gen r=6 -> 10^0 = 1
|
|
25
|
-
*
|
|
26
|
-
* Why this is exact for v1's keyword step: a higher-precedence family always
|
|
27
|
-
* outscores the SUM of every lower-precedence family (100000 > 10000+1000+100+
|
|
28
|
-
* 10+1 = 11111), so argmax over scoreClass recovers "highest-precedence match
|
|
29
|
-
* wins" with no possibility of ties between matched families.
|
|
30
|
-
*
|
|
31
|
-
* v1 steps 2-6 (fence/diff -> code-gen; len<40 -> trivial; len>=2400 -> plan;
|
|
32
|
-
* lane fallback; default code-gen) are NOT linear in the v2 feature vector
|
|
33
|
-
* (v1 uses hard 40/2400 thresholds; a single lenBucket weight is monotonic and
|
|
34
|
-
* cannot reproduce both). They are therefore reproduced by classifyTaskV2's
|
|
35
|
-
* zero-score cascade: when no keyword and no fence/diff signal fires (every
|
|
36
|
-
* scoreClass term is 0 under DEFAULT_WEIGHTS), classifyTaskV2 applies v1 steps
|
|
37
|
-
* 3-6 verbatim. The fence/diff flags live on the code-gen weight row (boolean
|
|
38
|
-
* weight flag AND feature -> +1) so a fence/diff-only turn still scores code-gen
|
|
39
|
-
* > 0 and is selected by argmax, matching v1 step 2. trivial carries no keyword
|
|
40
|
-
* weight (it is not a keyword family) and is reached only via the cascade.
|
|
41
|
-
*
|
|
42
|
-
* Two presets ship from this encoding (both with thresholds 0, so the
|
|
43
|
-
* sticky / consult / low-margin-judge paths stay INACTIVE):
|
|
44
|
-
* - V1_COMPAT_WEIGHTS: STRONG-only diagonal, every WEAK coefficient 0. This is
|
|
45
|
-
* the frozen Req 1.4 oracle — classifyTaskV2(input, V1_COMPAT_WEIGHTS) ===
|
|
46
|
-
* classifyTask(input) bit-for-bit on the v1 corpus. Do NOT recalibrate it.
|
|
47
|
-
* - DEFAULT_WEIGHTS: the calibrated production preset. Identical STRONG
|
|
48
|
-
* diagonal PLUS a constant WEAK diagonal of 1 per keyword class, so an
|
|
49
|
-
* ambiguous WEAK token (debug "fix", refactor "simplify", review "assess" /
|
|
50
|
-
* "lgtm", code-gen "add", plan "break down") contributes a small positive
|
|
51
|
-
* signal to its own class WITHOUT dominating a stronger sibling-class signal
|
|
52
|
-
* (every STRONG weight >= 1; higher-precedence STRONG weights strictly > 1).
|
|
53
|
-
* classifyTaskV2(input, DEFAULT_WEIGHTS) is strictly better than v1 on the
|
|
54
|
-
* gold set (CWA(v2) > CWA(v1)); see laneI5b-calibration.md.
|
|
55
|
-
* ============================================================================
|
|
56
|
-
*/
|
|
57
|
-
/** Closed set of v2 task classes (same labels as v1 TaskClass). */
|
|
58
|
-
export type TaskClassV2 = "trivial" | "simple-edit" | "code-gen" | "debug" | "refactor" | "review" | "plan";
|
|
59
|
-
/** Subagent lane types recognized by the v2 classifier and resolver. */
|
|
60
|
-
export type ReasoningLaneTypeV2 = "planner" | "security" | "explorer" | "coder" | "reviewer" | "tester";
|
|
61
|
-
/** Fixed iteration order over TaskClassV2 (matches the type-declaration order). */
|
|
62
|
-
export declare const TASK_CLASSES_V2: readonly TaskClassV2[];
|
|
63
|
-
/**
|
|
64
|
-
* Feature vector for one turn, extracted by classifyTaskV2's caller layer.
|
|
65
|
-
*
|
|
66
|
-
* - strongKeyword[c] / weakKeyword[c]: per-class match counts (0 when absent).
|
|
67
|
-
* For v1-compat, strongKeyword[c] is the 0/1 result of v1's pattern.test.
|
|
68
|
-
* - codeFence / diffHunk: prompt-embedded code-fence / diff-marker booleans.
|
|
69
|
-
* - lenBucket: floor(log2(trimmedLen + 1)) clamped to [0, 7].
|
|
70
|
-
* - multiTurnPrior: most-recent historical class (N=8 ring buffer) or null.
|
|
71
|
-
* - pressureBucket: context-pressure band (0..<0.5, 1..<0.75, 2..<0.9, 3..>=0.9).
|
|
72
|
-
* - judgeVote: optional tier-2 LLM-judge label, or null.
|
|
73
|
-
*/
|
|
74
|
-
export interface RouterFeatures {
|
|
75
|
-
strongKeyword: Partial<Record<TaskClassV2, number>>;
|
|
76
|
-
weakKeyword: Partial<Record<TaskClassV2, number>>;
|
|
77
|
-
codeFence: boolean;
|
|
78
|
-
diffHunk: boolean;
|
|
79
|
-
lenBucket: number;
|
|
80
|
-
multiTurnPrior: TaskClassV2 | null;
|
|
81
|
-
pressureBucket: number;
|
|
82
|
-
judgeVote: TaskClassV2 | null;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Tunable weight surface for the v2 router.
|
|
86
|
-
*
|
|
87
|
-
* classWeights[c] is a RouterFeatures-shaped row of per-feature coefficients for
|
|
88
|
-
* class c. stickyThreshold / consultThreshold / marginThreshold steer
|
|
89
|
-
* classifyTaskV2's hysteresis, AdaptOrch consultation, and low-confidence margin
|
|
90
|
-
* gate respectively. laneStep mirrors v1's resolver ladder adjustment per lane.
|
|
91
|
-
*/
|
|
92
|
-
export interface RouterWeights {
|
|
93
|
-
classWeights: Readonly<Record<TaskClassV2, RouterFeatures>>;
|
|
94
|
-
stickyThreshold: number;
|
|
95
|
-
consultThreshold: number;
|
|
96
|
-
marginThreshold: number;
|
|
97
|
-
laneStep: Readonly<Partial<Record<ReasoningLaneTypeV2, 1 | -1>>>;
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Pure integer-arithmetic scorer. Returns S(c) = sum of weighted features for
|
|
101
|
-
* class c under weights w:
|
|
102
|
-
* - keyword terms: sum over classes c2 of strong/weak weight * feature count.
|
|
103
|
-
* - boolean signal terms (codeFence, diffHunk): +1 when both the weight flag
|
|
104
|
-
* AND the feature are set (magnitude is a unit; the flag gates the class).
|
|
105
|
-
* - numeric terms (lenBucket, pressureBucket): weight * feature product.
|
|
106
|
-
* - class-equality terms (multiTurnPrior, judgeVote): +1 on exact match with a
|
|
107
|
-
* non-null feature.
|
|
108
|
-
*
|
|
109
|
-
* Deterministic: same (c, f, w) -> same number. No clock, randomness, or I/O.
|
|
110
|
-
*/
|
|
111
|
-
export declare function scoreClass(c: TaskClassV2, f: RouterFeatures, w: RouterWeights): number;
|
|
112
|
-
/**
|
|
113
|
-
* v1-compat oracle weight preset (Goal 004 Req 1.4 regression oracle).
|
|
114
|
-
*
|
|
115
|
-
* STRONG-only diagonal (10^(6-r), see file header); every WEAK coefficient is 0
|
|
116
|
-
* so WEAK keywords are inert. code-gen owns the codeFence/diffHunk flags so v1
|
|
117
|
-
* step 2 is captured inside scoreClass. Every length / prior / pressure / judge
|
|
118
|
-
* coefficient is zero (v1 has no linear length signal, no history, no
|
|
119
|
-
* context-pressure input, no judge). Thresholds are 0 so classifyTaskV2 never
|
|
120
|
-
* enters the sticky / consult / low-margin-judge paths.
|
|
121
|
-
*
|
|
122
|
-
* classifyTaskV2(input, V1_COMPAT_WEIGHTS) === classifyTask(input) bit-for-bit
|
|
123
|
-
* on the v1 corpus. Kept frozen as the oracle; do NOT recalibrate this preset.
|
|
124
|
-
*/
|
|
125
|
-
export declare const V1_COMPAT_WEIGHTS: RouterWeights;
|
|
126
|
-
/**
|
|
127
|
-
* Calibrated production preset (Goal 004 Lane I5b). Extends V1_COMPAT_WEIGHTS
|
|
128
|
-
* with a constant WEAK-keyword diagonal of 1 per keyword class.
|
|
129
|
-
*
|
|
130
|
-
* Weighting invariant:
|
|
131
|
-
* - STRONG keyword in class C: 10^(6-rank(C)) (unchanged from V1_COMPAT).
|
|
132
|
-
* - WEAK keyword in class C: 1 (constant, identical across classes).
|
|
133
|
-
*
|
|
134
|
-
* Because every STRONG weight (min 1 for code-gen) is >= the constant WEAK
|
|
135
|
-
* weight 1, and higher-precedence STRONG weights (>=10) strictly exceed it, a
|
|
136
|
-
* single STRONG hit in ANY class always beats or ties a lone WEAK hit, while two
|
|
137
|
-
* STRONG hits resolve by the precedence diagonal (10^(6-r)). Concrete
|
|
138
|
-
* consequences on the gold set:
|
|
139
|
-
* - "fix the typo": WEAK-debug(1) + STRONG-simple-edit(10) -> simple-edit wins
|
|
140
|
-
* (the Req 1.3 regression fix; v1 wrongly routed to debug).
|
|
141
|
-
* - "fix the race condition": WEAK-debug(1) alone -> debug beats the zero
|
|
142
|
-
* default (no len>=40 cascade to code-gen; v1 was already correct here, the
|
|
143
|
-
* STRONG-only oracle had regressed it).
|
|
144
|
-
* - "simplify the nested if-else": WEAK-refactor(1) alone -> refactor.
|
|
145
|
-
* - "lgtm, just double-check the tests": WEAK-review(1) alone -> review.
|
|
146
|
-
* - "crash the typo": STRONG-debug(100000) + STRONG-simple-edit(10) -> debug
|
|
147
|
-
* (a crash-dominated prompt stays debug; precedence diagonal preserved).
|
|
148
|
-
*
|
|
149
|
-
* classifyTaskV2(input, DEFAULT_WEIGHTS) is STRICTLY BETTER than v1 on the gold
|
|
150
|
-
* set (CWA(v2) > CWA(v1)); see .omk/goals/004-reasoning-router-v2-impl/
|
|
151
|
-
* laneI5b-calibration.md. Thresholds stay 0 so hysteresis/consult/margin paths
|
|
152
|
-
* remain inactive (Phase A).
|
|
153
|
-
*/
|
|
154
|
-
export declare const DEFAULT_WEIGHTS: RouterWeights;
|
|
155
|
-
//# sourceMappingURL=reasoning-router-weights.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"reasoning-router-weights.d.ts","sourceRoot":"","sources":["../../src/core/reasoning-router-weights.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,mEAAmE;AACnE,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE5G,wEAAwE;AACxE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAExG,mFAAmF;AACnF,eAAO,MAAM,eAAe,EAAE,SAAS,WAAW,EAQjD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,WAAW,GAAG,IAAI,CAAC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC7B,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;IAC5D,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACjE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,aAAa,GAAG,MAAM,CActF;AAuCD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,EAAE,aAc/B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,eAAe,EAAE,aAc7B,CAAC","sourcesContent":["/**\n * Reasoning-router v2 — frozen types, feature vector, weight schema, the\n * V1_COMPAT_WEIGHTS v1-exact oracle (Req 1.4), and the DEFAULT_WEIGHTS\n * calibrated production preset (Goal 004 Lane I5b).\n *\n * Pure, side-effect-free, erasable TypeScript. No runtime imports.\n *\n * ============================================================================\n * WEIGHT PRESETS — V1_COMPAT_WEIGHTS (v1 oracle) + DEFAULT_WEIGHTS (calibrated)\n * ============================================================================\n * The v1 classifier (reasoning-router.ts) is a first-match keyword cascade with\n * FIXED precedence: debug > refactor > review > plan > simple-edit > code-gen,\n * then fence/diff, then length, then lane fallback, then default code-gen.\n *\n * We reproduce that precedence as DIGIT-POSITION weights. The keyword family at\n * precedence rank r contributes exactly 10^(6-r) to ITS OWN class score when its\n * pattern matches (strongKeyword diagonal, 0/1 match indicator):\n *\n * debug r=1 -> 10^5 = 100000\n * refactor r=2 -> 10^4 = 10000\n * review r=3 -> 10^3 = 1000\n * plan r=4 -> 10^2 = 100\n * simple-edit r=5 -> 10^1 = 10\n * code-gen r=6 -> 10^0 = 1\n *\n * Why this is exact for v1's keyword step: a higher-precedence family always\n * outscores the SUM of every lower-precedence family (100000 > 10000+1000+100+\n * 10+1 = 11111), so argmax over scoreClass recovers \"highest-precedence match\n * wins\" with no possibility of ties between matched families.\n *\n * v1 steps 2-6 (fence/diff -> code-gen; len<40 -> trivial; len>=2400 -> plan;\n * lane fallback; default code-gen) are NOT linear in the v2 feature vector\n * (v1 uses hard 40/2400 thresholds; a single lenBucket weight is monotonic and\n * cannot reproduce both). They are therefore reproduced by classifyTaskV2's\n * zero-score cascade: when no keyword and no fence/diff signal fires (every\n * scoreClass term is 0 under DEFAULT_WEIGHTS), classifyTaskV2 applies v1 steps\n * 3-6 verbatim. The fence/diff flags live on the code-gen weight row (boolean\n * weight flag AND feature -> +1) so a fence/diff-only turn still scores code-gen\n * > 0 and is selected by argmax, matching v1 step 2. trivial carries no keyword\n * weight (it is not a keyword family) and is reached only via the cascade.\n *\n * Two presets ship from this encoding (both with thresholds 0, so the\n * sticky / consult / low-margin-judge paths stay INACTIVE):\n * - V1_COMPAT_WEIGHTS: STRONG-only diagonal, every WEAK coefficient 0. This is\n * the frozen Req 1.4 oracle — classifyTaskV2(input, V1_COMPAT_WEIGHTS) ===\n * classifyTask(input) bit-for-bit on the v1 corpus. Do NOT recalibrate it.\n * - DEFAULT_WEIGHTS: the calibrated production preset. Identical STRONG\n * diagonal PLUS a constant WEAK diagonal of 1 per keyword class, so an\n * ambiguous WEAK token (debug \"fix\", refactor \"simplify\", review \"assess\" /\n * \"lgtm\", code-gen \"add\", plan \"break down\") contributes a small positive\n * signal to its own class WITHOUT dominating a stronger sibling-class signal\n * (every STRONG weight >= 1; higher-precedence STRONG weights strictly > 1).\n * classifyTaskV2(input, DEFAULT_WEIGHTS) is strictly better than v1 on the\n * gold set (CWA(v2) > CWA(v1)); see laneI5b-calibration.md.\n * ============================================================================\n */\n\n/** Closed set of v2 task classes (same labels as v1 TaskClass). */\nexport type TaskClassV2 = \"trivial\" | \"simple-edit\" | \"code-gen\" | \"debug\" | \"refactor\" | \"review\" | \"plan\";\n\n/** Subagent lane types recognized by the v2 classifier and resolver. */\nexport type ReasoningLaneTypeV2 = \"planner\" | \"security\" | \"explorer\" | \"coder\" | \"reviewer\" | \"tester\";\n\n/** Fixed iteration order over TaskClassV2 (matches the type-declaration order). */\nexport const TASK_CLASSES_V2: readonly TaskClassV2[] = [\n\t\"trivial\",\n\t\"simple-edit\",\n\t\"code-gen\",\n\t\"debug\",\n\t\"refactor\",\n\t\"review\",\n\t\"plan\",\n];\n\n/**\n * Feature vector for one turn, extracted by classifyTaskV2's caller layer.\n *\n * - strongKeyword[c] / weakKeyword[c]: per-class match counts (0 when absent).\n * For v1-compat, strongKeyword[c] is the 0/1 result of v1's pattern.test.\n * - codeFence / diffHunk: prompt-embedded code-fence / diff-marker booleans.\n * - lenBucket: floor(log2(trimmedLen + 1)) clamped to [0, 7].\n * - multiTurnPrior: most-recent historical class (N=8 ring buffer) or null.\n * - pressureBucket: context-pressure band (0..<0.5, 1..<0.75, 2..<0.9, 3..>=0.9).\n * - judgeVote: optional tier-2 LLM-judge label, or null.\n */\nexport interface RouterFeatures {\n\tstrongKeyword: Partial<Record<TaskClassV2, number>>;\n\tweakKeyword: Partial<Record<TaskClassV2, number>>;\n\tcodeFence: boolean;\n\tdiffHunk: boolean;\n\tlenBucket: number;\n\tmultiTurnPrior: TaskClassV2 | null;\n\tpressureBucket: number;\n\tjudgeVote: TaskClassV2 | null;\n}\n\n/**\n * Tunable weight surface for the v2 router.\n *\n * classWeights[c] is a RouterFeatures-shaped row of per-feature coefficients for\n * class c. stickyThreshold / consultThreshold / marginThreshold steer\n * classifyTaskV2's hysteresis, AdaptOrch consultation, and low-confidence margin\n * gate respectively. laneStep mirrors v1's resolver ladder adjustment per lane.\n */\nexport interface RouterWeights {\n\tclassWeights: Readonly<Record<TaskClassV2, RouterFeatures>>;\n\tstickyThreshold: number;\n\tconsultThreshold: number;\n\tmarginThreshold: number;\n\tlaneStep: Readonly<Partial<Record<ReasoningLaneTypeV2, 1 | -1>>>;\n}\n\n/**\n * Pure integer-arithmetic scorer. Returns S(c) = sum of weighted features for\n * class c under weights w:\n * - keyword terms: sum over classes c2 of strong/weak weight * feature count.\n * - boolean signal terms (codeFence, diffHunk): +1 when both the weight flag\n * AND the feature are set (magnitude is a unit; the flag gates the class).\n * - numeric terms (lenBucket, pressureBucket): weight * feature product.\n * - class-equality terms (multiTurnPrior, judgeVote): +1 on exact match with a\n * non-null feature.\n *\n * Deterministic: same (c, f, w) -> same number. No clock, randomness, or I/O.\n */\nexport function scoreClass(c: TaskClassV2, f: RouterFeatures, w: RouterWeights): number {\n\tconst cw = w.classWeights[c];\n\tlet s = 0;\n\tfor (const c2 of TASK_CLASSES_V2) {\n\t\ts += (cw.strongKeyword[c2] ?? 0) * (f.strongKeyword[c2] ?? 0);\n\t\ts += (cw.weakKeyword[c2] ?? 0) * (f.weakKeyword[c2] ?? 0);\n\t}\n\tif (cw.codeFence && f.codeFence) s += 1;\n\tif (cw.diffHunk && f.diffHunk) s += 1;\n\ts += cw.lenBucket * f.lenBucket;\n\tif (f.multiTurnPrior !== null && cw.multiTurnPrior === f.multiTurnPrior) s += 1;\n\ts += cw.pressureBucket * f.pressureBucket;\n\tif (f.judgeVote !== null && cw.judgeVote === f.judgeVote) s += 1;\n\treturn s;\n}\n\n/**\n * All-neutral feature row (every coefficient inert). Used to build weight rows.\n */\nfunction neutralRow(): RouterFeatures {\n\treturn {\n\t\tstrongKeyword: {},\n\t\tweakKeyword: {},\n\t\tcodeFence: false,\n\t\tdiffHunk: false,\n\t\tlenBucket: 0,\n\t\tmultiTurnPrior: null,\n\t\tpressureBucket: 0,\n\t\tjudgeVote: null,\n\t};\n}\n\n/**\n * STRONG-keyword diagonal shared by both presets: family at precedence rank r\n * contributes 10^(6-r) to its own class (see file header). code-gen additionally\n * owns the codeFence/diffHunk flags so v1 step 2 is captured inside scoreClass.\n * Every WEAK / length / prior / pressure / judge coefficient is zero here.\n */\nconst STRONG_DIAGONAL: Readonly<Record<TaskClassV2, RouterFeatures>> = {\n\ttrivial: neutralRow(),\n\t\"simple-edit\": { ...neutralRow(), strongKeyword: { \"simple-edit\": 10 } },\n\t\"code-gen\": {\n\t\t...neutralRow(),\n\t\tstrongKeyword: { \"code-gen\": 1 },\n\t\tcodeFence: true,\n\t\tdiffHunk: true,\n\t},\n\tdebug: { ...neutralRow(), strongKeyword: { debug: 100000 } },\n\trefactor: { ...neutralRow(), strongKeyword: { refactor: 10000 } },\n\treview: { ...neutralRow(), strongKeyword: { review: 1000 } },\n\tplan: { ...neutralRow(), strongKeyword: { plan: 100 } },\n};\n\n/**\n * v1-compat oracle weight preset (Goal 004 Req 1.4 regression oracle).\n *\n * STRONG-only diagonal (10^(6-r), see file header); every WEAK coefficient is 0\n * so WEAK keywords are inert. code-gen owns the codeFence/diffHunk flags so v1\n * step 2 is captured inside scoreClass. Every length / prior / pressure / judge\n * coefficient is zero (v1 has no linear length signal, no history, no\n * context-pressure input, no judge). Thresholds are 0 so classifyTaskV2 never\n * enters the sticky / consult / low-margin-judge paths.\n *\n * classifyTaskV2(input, V1_COMPAT_WEIGHTS) === classifyTask(input) bit-for-bit\n * on the v1 corpus. Kept frozen as the oracle; do NOT recalibrate this preset.\n */\nexport const V1_COMPAT_WEIGHTS: RouterWeights = {\n\tclassWeights: {\n\t\ttrivial: { ...STRONG_DIAGONAL.trivial },\n\t\t\"simple-edit\": { ...STRONG_DIAGONAL[\"simple-edit\"] },\n\t\t\"code-gen\": { ...STRONG_DIAGONAL[\"code-gen\"] },\n\t\tdebug: { ...STRONG_DIAGONAL.debug },\n\t\trefactor: { ...STRONG_DIAGONAL.refactor },\n\t\treview: { ...STRONG_DIAGONAL.review },\n\t\tplan: { ...STRONG_DIAGONAL.plan },\n\t},\n\tstickyThreshold: 0,\n\tconsultThreshold: 0,\n\tmarginThreshold: 0,\n\tlaneStep: { planner: 1, security: 1, explorer: -1 },\n};\n\n/**\n * Calibrated production preset (Goal 004 Lane I5b). Extends V1_COMPAT_WEIGHTS\n * with a constant WEAK-keyword diagonal of 1 per keyword class.\n *\n * Weighting invariant:\n * - STRONG keyword in class C: 10^(6-rank(C)) (unchanged from V1_COMPAT).\n * - WEAK keyword in class C: 1 (constant, identical across classes).\n *\n * Because every STRONG weight (min 1 for code-gen) is >= the constant WEAK\n * weight 1, and higher-precedence STRONG weights (>=10) strictly exceed it, a\n * single STRONG hit in ANY class always beats or ties a lone WEAK hit, while two\n * STRONG hits resolve by the precedence diagonal (10^(6-r)). Concrete\n * consequences on the gold set:\n * - \"fix the typo\": WEAK-debug(1) + STRONG-simple-edit(10) -> simple-edit wins\n * (the Req 1.3 regression fix; v1 wrongly routed to debug).\n * - \"fix the race condition\": WEAK-debug(1) alone -> debug beats the zero\n * default (no len>=40 cascade to code-gen; v1 was already correct here, the\n * STRONG-only oracle had regressed it).\n * - \"simplify the nested if-else\": WEAK-refactor(1) alone -> refactor.\n * - \"lgtm, just double-check the tests\": WEAK-review(1) alone -> review.\n * - \"crash the typo\": STRONG-debug(100000) + STRONG-simple-edit(10) -> debug\n * (a crash-dominated prompt stays debug; precedence diagonal preserved).\n *\n * classifyTaskV2(input, DEFAULT_WEIGHTS) is STRICTLY BETTER than v1 on the gold\n * set (CWA(v2) > CWA(v1)); see .omk/goals/004-reasoning-router-v2-impl/\n * laneI5b-calibration.md. Thresholds stay 0 so hysteresis/consult/margin paths\n * remain inactive (Phase A).\n */\nexport const DEFAULT_WEIGHTS: RouterWeights = {\n\tclassWeights: {\n\t\ttrivial: { ...STRONG_DIAGONAL.trivial },\n\t\t\"simple-edit\": { ...STRONG_DIAGONAL[\"simple-edit\"] },\n\t\t\"code-gen\": { ...STRONG_DIAGONAL[\"code-gen\"], weakKeyword: { \"code-gen\": 1 } },\n\t\tdebug: { ...STRONG_DIAGONAL.debug, weakKeyword: { debug: 1 } },\n\t\trefactor: { ...STRONG_DIAGONAL.refactor, weakKeyword: { refactor: 1 } },\n\t\treview: { ...STRONG_DIAGONAL.review, weakKeyword: { review: 1 } },\n\t\tplan: { ...STRONG_DIAGONAL.plan, weakKeyword: { plan: 1 } },\n\t},\n\tstickyThreshold: 0,\n\tconsultThreshold: 0,\n\tmarginThreshold: 0,\n\tlaneStep: { planner: 1, security: 1, explorer: -1 },\n};\n"]}
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reasoning-router v2 — frozen types, feature vector, weight schema, the
|
|
3
|
-
* V1_COMPAT_WEIGHTS v1-exact oracle (Req 1.4), and the DEFAULT_WEIGHTS
|
|
4
|
-
* calibrated production preset (Goal 004 Lane I5b).
|
|
5
|
-
*
|
|
6
|
-
* Pure, side-effect-free, erasable TypeScript. No runtime imports.
|
|
7
|
-
*
|
|
8
|
-
* ============================================================================
|
|
9
|
-
* WEIGHT PRESETS — V1_COMPAT_WEIGHTS (v1 oracle) + DEFAULT_WEIGHTS (calibrated)
|
|
10
|
-
* ============================================================================
|
|
11
|
-
* The v1 classifier (reasoning-router.ts) is a first-match keyword cascade with
|
|
12
|
-
* FIXED precedence: debug > refactor > review > plan > simple-edit > code-gen,
|
|
13
|
-
* then fence/diff, then length, then lane fallback, then default code-gen.
|
|
14
|
-
*
|
|
15
|
-
* We reproduce that precedence as DIGIT-POSITION weights. The keyword family at
|
|
16
|
-
* precedence rank r contributes exactly 10^(6-r) to ITS OWN class score when its
|
|
17
|
-
* pattern matches (strongKeyword diagonal, 0/1 match indicator):
|
|
18
|
-
*
|
|
19
|
-
* debug r=1 -> 10^5 = 100000
|
|
20
|
-
* refactor r=2 -> 10^4 = 10000
|
|
21
|
-
* review r=3 -> 10^3 = 1000
|
|
22
|
-
* plan r=4 -> 10^2 = 100
|
|
23
|
-
* simple-edit r=5 -> 10^1 = 10
|
|
24
|
-
* code-gen r=6 -> 10^0 = 1
|
|
25
|
-
*
|
|
26
|
-
* Why this is exact for v1's keyword step: a higher-precedence family always
|
|
27
|
-
* outscores the SUM of every lower-precedence family (100000 > 10000+1000+100+
|
|
28
|
-
* 10+1 = 11111), so argmax over scoreClass recovers "highest-precedence match
|
|
29
|
-
* wins" with no possibility of ties between matched families.
|
|
30
|
-
*
|
|
31
|
-
* v1 steps 2-6 (fence/diff -> code-gen; len<40 -> trivial; len>=2400 -> plan;
|
|
32
|
-
* lane fallback; default code-gen) are NOT linear in the v2 feature vector
|
|
33
|
-
* (v1 uses hard 40/2400 thresholds; a single lenBucket weight is monotonic and
|
|
34
|
-
* cannot reproduce both). They are therefore reproduced by classifyTaskV2's
|
|
35
|
-
* zero-score cascade: when no keyword and no fence/diff signal fires (every
|
|
36
|
-
* scoreClass term is 0 under DEFAULT_WEIGHTS), classifyTaskV2 applies v1 steps
|
|
37
|
-
* 3-6 verbatim. The fence/diff flags live on the code-gen weight row (boolean
|
|
38
|
-
* weight flag AND feature -> +1) so a fence/diff-only turn still scores code-gen
|
|
39
|
-
* > 0 and is selected by argmax, matching v1 step 2. trivial carries no keyword
|
|
40
|
-
* weight (it is not a keyword family) and is reached only via the cascade.
|
|
41
|
-
*
|
|
42
|
-
* Two presets ship from this encoding (both with thresholds 0, so the
|
|
43
|
-
* sticky / consult / low-margin-judge paths stay INACTIVE):
|
|
44
|
-
* - V1_COMPAT_WEIGHTS: STRONG-only diagonal, every WEAK coefficient 0. This is
|
|
45
|
-
* the frozen Req 1.4 oracle — classifyTaskV2(input, V1_COMPAT_WEIGHTS) ===
|
|
46
|
-
* classifyTask(input) bit-for-bit on the v1 corpus. Do NOT recalibrate it.
|
|
47
|
-
* - DEFAULT_WEIGHTS: the calibrated production preset. Identical STRONG
|
|
48
|
-
* diagonal PLUS a constant WEAK diagonal of 1 per keyword class, so an
|
|
49
|
-
* ambiguous WEAK token (debug "fix", refactor "simplify", review "assess" /
|
|
50
|
-
* "lgtm", code-gen "add", plan "break down") contributes a small positive
|
|
51
|
-
* signal to its own class WITHOUT dominating a stronger sibling-class signal
|
|
52
|
-
* (every STRONG weight >= 1; higher-precedence STRONG weights strictly > 1).
|
|
53
|
-
* classifyTaskV2(input, DEFAULT_WEIGHTS) is strictly better than v1 on the
|
|
54
|
-
* gold set (CWA(v2) > CWA(v1)); see laneI5b-calibration.md.
|
|
55
|
-
* ============================================================================
|
|
56
|
-
*/
|
|
57
|
-
/** Fixed iteration order over TaskClassV2 (matches the type-declaration order). */
|
|
58
|
-
export const TASK_CLASSES_V2 = [
|
|
59
|
-
"trivial",
|
|
60
|
-
"simple-edit",
|
|
61
|
-
"code-gen",
|
|
62
|
-
"debug",
|
|
63
|
-
"refactor",
|
|
64
|
-
"review",
|
|
65
|
-
"plan",
|
|
66
|
-
];
|
|
67
|
-
/**
|
|
68
|
-
* Pure integer-arithmetic scorer. Returns S(c) = sum of weighted features for
|
|
69
|
-
* class c under weights w:
|
|
70
|
-
* - keyword terms: sum over classes c2 of strong/weak weight * feature count.
|
|
71
|
-
* - boolean signal terms (codeFence, diffHunk): +1 when both the weight flag
|
|
72
|
-
* AND the feature are set (magnitude is a unit; the flag gates the class).
|
|
73
|
-
* - numeric terms (lenBucket, pressureBucket): weight * feature product.
|
|
74
|
-
* - class-equality terms (multiTurnPrior, judgeVote): +1 on exact match with a
|
|
75
|
-
* non-null feature.
|
|
76
|
-
*
|
|
77
|
-
* Deterministic: same (c, f, w) -> same number. No clock, randomness, or I/O.
|
|
78
|
-
*/
|
|
79
|
-
export function scoreClass(c, f, w) {
|
|
80
|
-
const cw = w.classWeights[c];
|
|
81
|
-
let s = 0;
|
|
82
|
-
for (const c2 of TASK_CLASSES_V2) {
|
|
83
|
-
s += (cw.strongKeyword[c2] ?? 0) * (f.strongKeyword[c2] ?? 0);
|
|
84
|
-
s += (cw.weakKeyword[c2] ?? 0) * (f.weakKeyword[c2] ?? 0);
|
|
85
|
-
}
|
|
86
|
-
if (cw.codeFence && f.codeFence)
|
|
87
|
-
s += 1;
|
|
88
|
-
if (cw.diffHunk && f.diffHunk)
|
|
89
|
-
s += 1;
|
|
90
|
-
s += cw.lenBucket * f.lenBucket;
|
|
91
|
-
if (f.multiTurnPrior !== null && cw.multiTurnPrior === f.multiTurnPrior)
|
|
92
|
-
s += 1;
|
|
93
|
-
s += cw.pressureBucket * f.pressureBucket;
|
|
94
|
-
if (f.judgeVote !== null && cw.judgeVote === f.judgeVote)
|
|
95
|
-
s += 1;
|
|
96
|
-
return s;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* All-neutral feature row (every coefficient inert). Used to build weight rows.
|
|
100
|
-
*/
|
|
101
|
-
function neutralRow() {
|
|
102
|
-
return {
|
|
103
|
-
strongKeyword: {},
|
|
104
|
-
weakKeyword: {},
|
|
105
|
-
codeFence: false,
|
|
106
|
-
diffHunk: false,
|
|
107
|
-
lenBucket: 0,
|
|
108
|
-
multiTurnPrior: null,
|
|
109
|
-
pressureBucket: 0,
|
|
110
|
-
judgeVote: null,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* STRONG-keyword diagonal shared by both presets: family at precedence rank r
|
|
115
|
-
* contributes 10^(6-r) to its own class (see file header). code-gen additionally
|
|
116
|
-
* owns the codeFence/diffHunk flags so v1 step 2 is captured inside scoreClass.
|
|
117
|
-
* Every WEAK / length / prior / pressure / judge coefficient is zero here.
|
|
118
|
-
*/
|
|
119
|
-
const STRONG_DIAGONAL = {
|
|
120
|
-
trivial: neutralRow(),
|
|
121
|
-
"simple-edit": { ...neutralRow(), strongKeyword: { "simple-edit": 10 } },
|
|
122
|
-
"code-gen": {
|
|
123
|
-
...neutralRow(),
|
|
124
|
-
strongKeyword: { "code-gen": 1 },
|
|
125
|
-
codeFence: true,
|
|
126
|
-
diffHunk: true,
|
|
127
|
-
},
|
|
128
|
-
debug: { ...neutralRow(), strongKeyword: { debug: 100000 } },
|
|
129
|
-
refactor: { ...neutralRow(), strongKeyword: { refactor: 10000 } },
|
|
130
|
-
review: { ...neutralRow(), strongKeyword: { review: 1000 } },
|
|
131
|
-
plan: { ...neutralRow(), strongKeyword: { plan: 100 } },
|
|
132
|
-
};
|
|
133
|
-
/**
|
|
134
|
-
* v1-compat oracle weight preset (Goal 004 Req 1.4 regression oracle).
|
|
135
|
-
*
|
|
136
|
-
* STRONG-only diagonal (10^(6-r), see file header); every WEAK coefficient is 0
|
|
137
|
-
* so WEAK keywords are inert. code-gen owns the codeFence/diffHunk flags so v1
|
|
138
|
-
* step 2 is captured inside scoreClass. Every length / prior / pressure / judge
|
|
139
|
-
* coefficient is zero (v1 has no linear length signal, no history, no
|
|
140
|
-
* context-pressure input, no judge). Thresholds are 0 so classifyTaskV2 never
|
|
141
|
-
* enters the sticky / consult / low-margin-judge paths.
|
|
142
|
-
*
|
|
143
|
-
* classifyTaskV2(input, V1_COMPAT_WEIGHTS) === classifyTask(input) bit-for-bit
|
|
144
|
-
* on the v1 corpus. Kept frozen as the oracle; do NOT recalibrate this preset.
|
|
145
|
-
*/
|
|
146
|
-
export const V1_COMPAT_WEIGHTS = {
|
|
147
|
-
classWeights: {
|
|
148
|
-
trivial: { ...STRONG_DIAGONAL.trivial },
|
|
149
|
-
"simple-edit": { ...STRONG_DIAGONAL["simple-edit"] },
|
|
150
|
-
"code-gen": { ...STRONG_DIAGONAL["code-gen"] },
|
|
151
|
-
debug: { ...STRONG_DIAGONAL.debug },
|
|
152
|
-
refactor: { ...STRONG_DIAGONAL.refactor },
|
|
153
|
-
review: { ...STRONG_DIAGONAL.review },
|
|
154
|
-
plan: { ...STRONG_DIAGONAL.plan },
|
|
155
|
-
},
|
|
156
|
-
stickyThreshold: 0,
|
|
157
|
-
consultThreshold: 0,
|
|
158
|
-
marginThreshold: 0,
|
|
159
|
-
laneStep: { planner: 1, security: 1, explorer: -1 },
|
|
160
|
-
};
|
|
161
|
-
/**
|
|
162
|
-
* Calibrated production preset (Goal 004 Lane I5b). Extends V1_COMPAT_WEIGHTS
|
|
163
|
-
* with a constant WEAK-keyword diagonal of 1 per keyword class.
|
|
164
|
-
*
|
|
165
|
-
* Weighting invariant:
|
|
166
|
-
* - STRONG keyword in class C: 10^(6-rank(C)) (unchanged from V1_COMPAT).
|
|
167
|
-
* - WEAK keyword in class C: 1 (constant, identical across classes).
|
|
168
|
-
*
|
|
169
|
-
* Because every STRONG weight (min 1 for code-gen) is >= the constant WEAK
|
|
170
|
-
* weight 1, and higher-precedence STRONG weights (>=10) strictly exceed it, a
|
|
171
|
-
* single STRONG hit in ANY class always beats or ties a lone WEAK hit, while two
|
|
172
|
-
* STRONG hits resolve by the precedence diagonal (10^(6-r)). Concrete
|
|
173
|
-
* consequences on the gold set:
|
|
174
|
-
* - "fix the typo": WEAK-debug(1) + STRONG-simple-edit(10) -> simple-edit wins
|
|
175
|
-
* (the Req 1.3 regression fix; v1 wrongly routed to debug).
|
|
176
|
-
* - "fix the race condition": WEAK-debug(1) alone -> debug beats the zero
|
|
177
|
-
* default (no len>=40 cascade to code-gen; v1 was already correct here, the
|
|
178
|
-
* STRONG-only oracle had regressed it).
|
|
179
|
-
* - "simplify the nested if-else": WEAK-refactor(1) alone -> refactor.
|
|
180
|
-
* - "lgtm, just double-check the tests": WEAK-review(1) alone -> review.
|
|
181
|
-
* - "crash the typo": STRONG-debug(100000) + STRONG-simple-edit(10) -> debug
|
|
182
|
-
* (a crash-dominated prompt stays debug; precedence diagonal preserved).
|
|
183
|
-
*
|
|
184
|
-
* classifyTaskV2(input, DEFAULT_WEIGHTS) is STRICTLY BETTER than v1 on the gold
|
|
185
|
-
* set (CWA(v2) > CWA(v1)); see .omk/goals/004-reasoning-router-v2-impl/
|
|
186
|
-
* laneI5b-calibration.md. Thresholds stay 0 so hysteresis/consult/margin paths
|
|
187
|
-
* remain inactive (Phase A).
|
|
188
|
-
*/
|
|
189
|
-
export const DEFAULT_WEIGHTS = {
|
|
190
|
-
classWeights: {
|
|
191
|
-
trivial: { ...STRONG_DIAGONAL.trivial },
|
|
192
|
-
"simple-edit": { ...STRONG_DIAGONAL["simple-edit"] },
|
|
193
|
-
"code-gen": { ...STRONG_DIAGONAL["code-gen"], weakKeyword: { "code-gen": 1 } },
|
|
194
|
-
debug: { ...STRONG_DIAGONAL.debug, weakKeyword: { debug: 1 } },
|
|
195
|
-
refactor: { ...STRONG_DIAGONAL.refactor, weakKeyword: { refactor: 1 } },
|
|
196
|
-
review: { ...STRONG_DIAGONAL.review, weakKeyword: { review: 1 } },
|
|
197
|
-
plan: { ...STRONG_DIAGONAL.plan, weakKeyword: { plan: 1 } },
|
|
198
|
-
},
|
|
199
|
-
stickyThreshold: 0,
|
|
200
|
-
consultThreshold: 0,
|
|
201
|
-
marginThreshold: 0,
|
|
202
|
-
laneStep: { planner: 1, security: 1, explorer: -1 },
|
|
203
|
-
};
|
|
204
|
-
//# sourceMappingURL=reasoning-router-weights.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"reasoning-router-weights.js","sourceRoot":"","sources":["../../src/core/reasoning-router-weights.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reasoning-router v2 — frozen types, feature vector, weight schema, the\n * V1_COMPAT_WEIGHTS v1-exact oracle (Req 1.4), and the DEFAULT_WEIGHTS\n * calibrated production preset (Goal 004 Lane I5b).\n *\n * Pure, side-effect-free, erasable TypeScript. No runtime imports.\n *\n * ============================================================================\n * WEIGHT PRESETS — V1_COMPAT_WEIGHTS (v1 oracle) + DEFAULT_WEIGHTS (calibrated)\n * ============================================================================\n * The v1 classifier (reasoning-router.ts) is a first-match keyword cascade with\n * FIXED precedence: debug > refactor > review > plan > simple-edit > code-gen,\n * then fence/diff, then length, then lane fallback, then default code-gen.\n *\n * We reproduce that precedence as DIGIT-POSITION weights. The keyword family at\n * precedence rank r contributes exactly 10^(6-r) to ITS OWN class score when its\n * pattern matches (strongKeyword diagonal, 0/1 match indicator):\n *\n * debug r=1 -> 10^5 = 100000\n * refactor r=2 -> 10^4 = 10000\n * review r=3 -> 10^3 = 1000\n * plan r=4 -> 10^2 = 100\n * simple-edit r=5 -> 10^1 = 10\n * code-gen r=6 -> 10^0 = 1\n *\n * Why this is exact for v1's keyword step: a higher-precedence family always\n * outscores the SUM of every lower-precedence family (100000 > 10000+1000+100+\n * 10+1 = 11111), so argmax over scoreClass recovers \"highest-precedence match\n * wins\" with no possibility of ties between matched families.\n *\n * v1 steps 2-6 (fence/diff -> code-gen; len<40 -> trivial; len>=2400 -> plan;\n * lane fallback; default code-gen) are NOT linear in the v2 feature vector\n * (v1 uses hard 40/2400 thresholds; a single lenBucket weight is monotonic and\n * cannot reproduce both). They are therefore reproduced by classifyTaskV2's\n * zero-score cascade: when no keyword and no fence/diff signal fires (every\n * scoreClass term is 0 under DEFAULT_WEIGHTS), classifyTaskV2 applies v1 steps\n * 3-6 verbatim. The fence/diff flags live on the code-gen weight row (boolean\n * weight flag AND feature -> +1) so a fence/diff-only turn still scores code-gen\n * > 0 and is selected by argmax, matching v1 step 2. trivial carries no keyword\n * weight (it is not a keyword family) and is reached only via the cascade.\n *\n * Two presets ship from this encoding (both with thresholds 0, so the\n * sticky / consult / low-margin-judge paths stay INACTIVE):\n * - V1_COMPAT_WEIGHTS: STRONG-only diagonal, every WEAK coefficient 0. This is\n * the frozen Req 1.4 oracle — classifyTaskV2(input, V1_COMPAT_WEIGHTS) ===\n * classifyTask(input) bit-for-bit on the v1 corpus. Do NOT recalibrate it.\n * - DEFAULT_WEIGHTS: the calibrated production preset. Identical STRONG\n * diagonal PLUS a constant WEAK diagonal of 1 per keyword class, so an\n * ambiguous WEAK token (debug \"fix\", refactor \"simplify\", review \"assess\" /\n * \"lgtm\", code-gen \"add\", plan \"break down\") contributes a small positive\n * signal to its own class WITHOUT dominating a stronger sibling-class signal\n * (every STRONG weight >= 1; higher-precedence STRONG weights strictly > 1).\n * classifyTaskV2(input, DEFAULT_WEIGHTS) is strictly better than v1 on the\n * gold set (CWA(v2) > CWA(v1)); see laneI5b-calibration.md.\n * ============================================================================\n */\n\n/** Closed set of v2 task classes (same labels as v1 TaskClass). */\nexport type TaskClassV2 = \"trivial\" | \"simple-edit\" | \"code-gen\" | \"debug\" | \"refactor\" | \"review\" | \"plan\";\n\n/** Subagent lane types recognized by the v2 classifier and resolver. */\nexport type ReasoningLaneTypeV2 = \"planner\" | \"security\" | \"explorer\" | \"coder\" | \"reviewer\" | \"tester\";\n\n/** Fixed iteration order over TaskClassV2 (matches the type-declaration order). */\nexport const TASK_CLASSES_V2: readonly TaskClassV2[] = [\n\t\"trivial\",\n\t\"simple-edit\",\n\t\"code-gen\",\n\t\"debug\",\n\t\"refactor\",\n\t\"review\",\n\t\"plan\",\n];\n\n/**\n * Feature vector for one turn, extracted by classifyTaskV2's caller layer.\n *\n * - strongKeyword[c] / weakKeyword[c]: per-class match counts (0 when absent).\n * For v1-compat, strongKeyword[c] is the 0/1 result of v1's pattern.test.\n * - codeFence / diffHunk: prompt-embedded code-fence / diff-marker booleans.\n * - lenBucket: floor(log2(trimmedLen + 1)) clamped to [0, 7].\n * - multiTurnPrior: most-recent historical class (N=8 ring buffer) or null.\n * - pressureBucket: context-pressure band (0..<0.5, 1..<0.75, 2..<0.9, 3..>=0.9).\n * - judgeVote: optional tier-2 LLM-judge label, or null.\n */\nexport interface RouterFeatures {\n\tstrongKeyword: Partial<Record<TaskClassV2, number>>;\n\tweakKeyword: Partial<Record<TaskClassV2, number>>;\n\tcodeFence: boolean;\n\tdiffHunk: boolean;\n\tlenBucket: number;\n\tmultiTurnPrior: TaskClassV2 | null;\n\tpressureBucket: number;\n\tjudgeVote: TaskClassV2 | null;\n}\n\n/**\n * Tunable weight surface for the v2 router.\n *\n * classWeights[c] is a RouterFeatures-shaped row of per-feature coefficients for\n * class c. stickyThreshold / consultThreshold / marginThreshold steer\n * classifyTaskV2's hysteresis, AdaptOrch consultation, and low-confidence margin\n * gate respectively. laneStep mirrors v1's resolver ladder adjustment per lane.\n */\nexport interface RouterWeights {\n\tclassWeights: Readonly<Record<TaskClassV2, RouterFeatures>>;\n\tstickyThreshold: number;\n\tconsultThreshold: number;\n\tmarginThreshold: number;\n\tlaneStep: Readonly<Partial<Record<ReasoningLaneTypeV2, 1 | -1>>>;\n}\n\n/**\n * Pure integer-arithmetic scorer. Returns S(c) = sum of weighted features for\n * class c under weights w:\n * - keyword terms: sum over classes c2 of strong/weak weight * feature count.\n * - boolean signal terms (codeFence, diffHunk): +1 when both the weight flag\n * AND the feature are set (magnitude is a unit; the flag gates the class).\n * - numeric terms (lenBucket, pressureBucket): weight * feature product.\n * - class-equality terms (multiTurnPrior, judgeVote): +1 on exact match with a\n * non-null feature.\n *\n * Deterministic: same (c, f, w) -> same number. No clock, randomness, or I/O.\n */\nexport function scoreClass(c: TaskClassV2, f: RouterFeatures, w: RouterWeights): number {\n\tconst cw = w.classWeights[c];\n\tlet s = 0;\n\tfor (const c2 of TASK_CLASSES_V2) {\n\t\ts += (cw.strongKeyword[c2] ?? 0) * (f.strongKeyword[c2] ?? 0);\n\t\ts += (cw.weakKeyword[c2] ?? 0) * (f.weakKeyword[c2] ?? 0);\n\t}\n\tif (cw.codeFence && f.codeFence) s += 1;\n\tif (cw.diffHunk && f.diffHunk) s += 1;\n\ts += cw.lenBucket * f.lenBucket;\n\tif (f.multiTurnPrior !== null && cw.multiTurnPrior === f.multiTurnPrior) s += 1;\n\ts += cw.pressureBucket * f.pressureBucket;\n\tif (f.judgeVote !== null && cw.judgeVote === f.judgeVote) s += 1;\n\treturn s;\n}\n\n/**\n * All-neutral feature row (every coefficient inert). Used to build weight rows.\n */\nfunction neutralRow(): RouterFeatures {\n\treturn {\n\t\tstrongKeyword: {},\n\t\tweakKeyword: {},\n\t\tcodeFence: false,\n\t\tdiffHunk: false,\n\t\tlenBucket: 0,\n\t\tmultiTurnPrior: null,\n\t\tpressureBucket: 0,\n\t\tjudgeVote: null,\n\t};\n}\n\n/**\n * STRONG-keyword diagonal shared by both presets: family at precedence rank r\n * contributes 10^(6-r) to its own class (see file header). code-gen additionally\n * owns the codeFence/diffHunk flags so v1 step 2 is captured inside scoreClass.\n * Every WEAK / length / prior / pressure / judge coefficient is zero here.\n */\nconst STRONG_DIAGONAL: Readonly<Record<TaskClassV2, RouterFeatures>> = {\n\ttrivial: neutralRow(),\n\t\"simple-edit\": { ...neutralRow(), strongKeyword: { \"simple-edit\": 10 } },\n\t\"code-gen\": {\n\t\t...neutralRow(),\n\t\tstrongKeyword: { \"code-gen\": 1 },\n\t\tcodeFence: true,\n\t\tdiffHunk: true,\n\t},\n\tdebug: { ...neutralRow(), strongKeyword: { debug: 100000 } },\n\trefactor: { ...neutralRow(), strongKeyword: { refactor: 10000 } },\n\treview: { ...neutralRow(), strongKeyword: { review: 1000 } },\n\tplan: { ...neutralRow(), strongKeyword: { plan: 100 } },\n};\n\n/**\n * v1-compat oracle weight preset (Goal 004 Req 1.4 regression oracle).\n *\n * STRONG-only diagonal (10^(6-r), see file header); every WEAK coefficient is 0\n * so WEAK keywords are inert. code-gen owns the codeFence/diffHunk flags so v1\n * step 2 is captured inside scoreClass. Every length / prior / pressure / judge\n * coefficient is zero (v1 has no linear length signal, no history, no\n * context-pressure input, no judge). Thresholds are 0 so classifyTaskV2 never\n * enters the sticky / consult / low-margin-judge paths.\n *\n * classifyTaskV2(input, V1_COMPAT_WEIGHTS) === classifyTask(input) bit-for-bit\n * on the v1 corpus. Kept frozen as the oracle; do NOT recalibrate this preset.\n */\nexport const V1_COMPAT_WEIGHTS: RouterWeights = {\n\tclassWeights: {\n\t\ttrivial: { ...STRONG_DIAGONAL.trivial },\n\t\t\"simple-edit\": { ...STRONG_DIAGONAL[\"simple-edit\"] },\n\t\t\"code-gen\": { ...STRONG_DIAGONAL[\"code-gen\"] },\n\t\tdebug: { ...STRONG_DIAGONAL.debug },\n\t\trefactor: { ...STRONG_DIAGONAL.refactor },\n\t\treview: { ...STRONG_DIAGONAL.review },\n\t\tplan: { ...STRONG_DIAGONAL.plan },\n\t},\n\tstickyThreshold: 0,\n\tconsultThreshold: 0,\n\tmarginThreshold: 0,\n\tlaneStep: { planner: 1, security: 1, explorer: -1 },\n};\n\n/**\n * Calibrated production preset (Goal 004 Lane I5b). Extends V1_COMPAT_WEIGHTS\n * with a constant WEAK-keyword diagonal of 1 per keyword class.\n *\n * Weighting invariant:\n * - STRONG keyword in class C: 10^(6-rank(C)) (unchanged from V1_COMPAT).\n * - WEAK keyword in class C: 1 (constant, identical across classes).\n *\n * Because every STRONG weight (min 1 for code-gen) is >= the constant WEAK\n * weight 1, and higher-precedence STRONG weights (>=10) strictly exceed it, a\n * single STRONG hit in ANY class always beats or ties a lone WEAK hit, while two\n * STRONG hits resolve by the precedence diagonal (10^(6-r)). Concrete\n * consequences on the gold set:\n * - \"fix the typo\": WEAK-debug(1) + STRONG-simple-edit(10) -> simple-edit wins\n * (the Req 1.3 regression fix; v1 wrongly routed to debug).\n * - \"fix the race condition\": WEAK-debug(1) alone -> debug beats the zero\n * default (no len>=40 cascade to code-gen; v1 was already correct here, the\n * STRONG-only oracle had regressed it).\n * - \"simplify the nested if-else\": WEAK-refactor(1) alone -> refactor.\n * - \"lgtm, just double-check the tests\": WEAK-review(1) alone -> review.\n * - \"crash the typo\": STRONG-debug(100000) + STRONG-simple-edit(10) -> debug\n * (a crash-dominated prompt stays debug; precedence diagonal preserved).\n *\n * classifyTaskV2(input, DEFAULT_WEIGHTS) is STRICTLY BETTER than v1 on the gold\n * set (CWA(v2) > CWA(v1)); see .omk/goals/004-reasoning-router-v2-impl/\n * laneI5b-calibration.md. Thresholds stay 0 so hysteresis/consult/margin paths\n * remain inactive (Phase A).\n */\nexport const DEFAULT_WEIGHTS: RouterWeights = {\n\tclassWeights: {\n\t\ttrivial: { ...STRONG_DIAGONAL.trivial },\n\t\t\"simple-edit\": { ...STRONG_DIAGONAL[\"simple-edit\"] },\n\t\t\"code-gen\": { ...STRONG_DIAGONAL[\"code-gen\"], weakKeyword: { \"code-gen\": 1 } },\n\t\tdebug: { ...STRONG_DIAGONAL.debug, weakKeyword: { debug: 1 } },\n\t\trefactor: { ...STRONG_DIAGONAL.refactor, weakKeyword: { refactor: 1 } },\n\t\treview: { ...STRONG_DIAGONAL.review, weakKeyword: { review: 1 } },\n\t\tplan: { ...STRONG_DIAGONAL.plan, weakKeyword: { plan: 1 } },\n\t},\n\tstickyThreshold: 0,\n\tconsultThreshold: 0,\n\tmarginThreshold: 0,\n\tlaneStep: { planner: 1, security: 1, explorer: -1 },\n};\n"]}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reasoning-effort router: pure, side-effect-free task classification and
|
|
3
|
-
* ThinkingLevel resolution for the `auto` thinking mode.
|
|
4
|
-
*
|
|
5
|
-
* Deterministic by construction: consumes only the prompt text and an optional
|
|
6
|
-
* subagent lane type. No clock, randomness, I/O, or imports from session, TUI,
|
|
7
|
-
* or provider code (the ThinkingLevel import below is type-only and erased).
|
|
8
|
-
*/
|
|
9
|
-
import type { ThinkingLevel } from "omk-agent-core";
|
|
10
|
-
/** Closed set of task classes the router can assign to a turn. */
|
|
11
|
-
export type TaskClass = "trivial" | "simple-edit" | "code-gen" | "debug" | "refactor" | "review" | "plan";
|
|
12
|
-
/** Subagent lane types recognized by the classifier and resolver. */
|
|
13
|
-
export type ReasoningLaneType = "planner" | "security" | "explorer" | "coder" | "reviewer" | "tester";
|
|
14
|
-
export interface TaskClassifierInput {
|
|
15
|
-
prompt: string;
|
|
16
|
-
laneType?: ReasoningLaneType;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Classify a turn deterministically. Signal precedence (first match wins):
|
|
20
|
-
* 1. Keyword families (debug > refactor > review > plan > simple-edit > code-gen)
|
|
21
|
-
* 2. Code fence or diff markers -> code-gen
|
|
22
|
-
* 3. Trimmed length < TRIVIAL_MAX_CHARS -> trivial
|
|
23
|
-
* 4. Trimmed length >= COMPLEX_PROSE_MIN_CHARS -> plan (long prose brief)
|
|
24
|
-
* 5. Lane fallback (see LANE_FALLBACK_CLASS)
|
|
25
|
-
* 6. Default -> code-gen
|
|
26
|
-
*/
|
|
27
|
-
export declare function classifyTask(input: TaskClassifierInput): TaskClass;
|
|
28
|
-
/** Static rule table: task class -> recommended ThinkingLevel (before lane adjustment and clamping). */
|
|
29
|
-
export declare const TASK_CLASS_THINKING_LEVELS: Readonly<Record<TaskClass, ThinkingLevel>>;
|
|
30
|
-
/**
|
|
31
|
-
* Resolve the recommended ThinkingLevel for a task class:
|
|
32
|
-
* 1. Look up the static rule table.
|
|
33
|
-
* 2. Apply the lane adjustment (one ladder step, saturating at ladder ends).
|
|
34
|
-
* 3. Clamp to the highest level in `availableLevels` that is <= the target;
|
|
35
|
-
* if no available level is at/below the target, return the lowest available
|
|
36
|
-
* reasoning level. Never invents a level outside `availableLevels`.
|
|
37
|
-
*
|
|
38
|
-
* `"off"` is never a router output for reasoning models. If `availableLevels`
|
|
39
|
-
* contains no ladder level at all (e.g. a non-reasoning model exposing only
|
|
40
|
-
* "off"), the first available level is returned; callers are expected to
|
|
41
|
-
* bypass the router entirely for models with `reasoning: false`.
|
|
42
|
-
*/
|
|
43
|
-
export declare function resolveThinkingLevel(taskClass: TaskClass, availableLevels: readonly ThinkingLevel[], laneType?: ReasoningLaneType): ThinkingLevel;
|
|
44
|
-
//# sourceMappingURL=reasoning-router.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"reasoning-router.d.ts","sourceRoot":"","sources":["../../src/core/reasoning-router.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,kEAAkE;AAClE,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1G,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEtG,MAAM,WAAW,mBAAmB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC7B;AAqED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,mBAAmB,GAAG,SAAS,CAYlE;AAKD,wGAAwG;AACxG,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,CAQjF,CAAC;AASF;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CACnC,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,SAAS,aAAa,EAAE,EACzC,QAAQ,CAAC,EAAE,iBAAiB,GAC1B,aAAa,CAef","sourcesContent":["/**\n * Reasoning-effort router: pure, side-effect-free task classification and\n * ThinkingLevel resolution for the `auto` thinking mode.\n *\n * Deterministic by construction: consumes only the prompt text and an optional\n * subagent lane type. No clock, randomness, I/O, or imports from session, TUI,\n * or provider code (the ThinkingLevel import below is type-only and erased).\n */\n\nimport type { ThinkingLevel } from \"omk-agent-core\";\n\n/** Closed set of task classes the router can assign to a turn. */\nexport type TaskClass = \"trivial\" | \"simple-edit\" | \"code-gen\" | \"debug\" | \"refactor\" | \"review\" | \"plan\";\n\n/** Subagent lane types recognized by the classifier and resolver. */\nexport type ReasoningLaneType = \"planner\" | \"security\" | \"explorer\" | \"coder\" | \"reviewer\" | \"tester\";\n\nexport interface TaskClassifierInput {\n\tprompt: string;\n\tlaneType?: ReasoningLaneType;\n}\n\n/** Prompts shorter than this (trimmed) classify as trivial when no stronger signal matches. */\nconst TRIVIAL_MAX_CHARS = 40;\n/** Prompts at/above this length with no keyword/code/diff signal are treated as long prose briefs (plan). */\nconst COMPLEX_PROSE_MIN_CHARS = 2400;\n\n/**\n * Keyword families in FIXED precedence order:\n * debug > refactor > review > plan > simple-edit > code-gen.\n * The first matching family wins; ties are impossible by construction.\n * Patterns are case-insensitive and stateless (no `g` flag, so no lastIndex carry-over).\n */\nconst KEYWORD_FAMILIES: ReadonlyArray<{ taskClass: TaskClass; pattern: RegExp }> = [\n\t{\n\t\ttaskClass: \"debug\",\n\t\tpattern:\n\t\t\t/\\b(debug(ging)?|bugs?|fix(es|ing|ed)?|errors?|exceptions?|crash(es|ing|ed)?|stack\\s*trace|traceback|regressions?|broken|fail(s|ing|ed|ure|ures)?|flaky|reproduce)\\b/i,\n\t},\n\t{\n\t\ttaskClass: \"refactor\",\n\t\tpattern:\n\t\t\t/\\b(refactor(ing|ed)?|restructur(e|ing)|renam(e|ing)|extract(ing)?|clean\\s*up|cleanup|simplif(y|ying|ied)|deduplicat(e|ing)|reorganiz(e|ing)|rewrit(e|ing)|modulariz(e|ing))\\b/i,\n\t},\n\t{\n\t\ttaskClass: \"review\",\n\t\tpattern: /\\b(review(s|ing|ed)?|audit(s|ing|ed)?|critique|assess(ing|ment)?|inspect(ing)?|lgtm|approve)\\b/i,\n\t},\n\t{\n\t\ttaskClass: \"plan\",\n\t\tpattern:\n\t\t\t/\\b(plan(s|ning)?|design(s|ing)?|architect(s|ure|ing)?|roadmap|spec(s|ification|ifications)?|strateg(y|ies|ize)|decompos(e|ing|ition)|break\\s+(this\\s+|it\\s+)?down|milestones?)\\b/i,\n\t},\n\t{\n\t\ttaskClass: \"simple-edit\",\n\t\tpattern:\n\t\t\t/\\b(typos?|tweak(s|ing|ed)?|one-?liner?|single\\s+line|bump(s|ing|ed)?|whitespace|reword(s|ing|ed)?|indentation|punctuation)\\b/i,\n\t},\n\t{\n\t\ttaskClass: \"code-gen\",\n\t\tpattern:\n\t\t\t/\\b(implement(s|ing|ation)?|writ(e|ing)|creat(e|es|ing)|add(s|ing|ed)?|build(s|ing)?|generat(e|es|ing)|scaffold(ing)?|prototype)\\b/i,\n\t},\n];\n\n/** Fallback class per lane when no keyword, code/diff, or length signal decides. */\nconst LANE_FALLBACK_CLASS: Record<ReasoningLaneType, TaskClass> = {\n\tplanner: \"plan\",\n\tsecurity: \"review\",\n\texplorer: \"review\",\n\tcoder: \"code-gen\",\n\treviewer: \"review\",\n\ttester: \"code-gen\",\n};\n\nfunction hasCodeFence(text: string): boolean {\n\treturn text.includes(\"```\");\n}\n\n/**\n * Diff detection: explicit hunk (`@@ ... @@`) or `diff --git` headers count alone;\n * bare `+`/`-` line starts only count when both added AND removed lines are present\n * (avoids false positives on markdown bullet lists).\n */\nfunction hasDiffMarkers(text: string): boolean {\n\tif (/^@@[^\\n]*@@/m.test(text) || /^diff --git /m.test(text)) return true;\n\treturn /^\\+(?!\\+)/m.test(text) && /^-(?!-)/m.test(text);\n}\n\n/**\n * Classify a turn deterministically. Signal precedence (first match wins):\n * 1. Keyword families (debug > refactor > review > plan > simple-edit > code-gen)\n * 2. Code fence or diff markers -> code-gen\n * 3. Trimmed length < TRIVIAL_MAX_CHARS -> trivial\n * 4. Trimmed length >= COMPLEX_PROSE_MIN_CHARS -> plan (long prose brief)\n * 5. Lane fallback (see LANE_FALLBACK_CLASS)\n * 6. Default -> code-gen\n */\nexport function classifyTask(input: TaskClassifierInput): TaskClass {\n\tconst prompt = input.prompt.trim();\n\n\tfor (const family of KEYWORD_FAMILIES) {\n\t\tif (family.pattern.test(prompt)) return family.taskClass;\n\t}\n\n\tif (hasCodeFence(prompt) || hasDiffMarkers(prompt)) return \"code-gen\";\n\tif (prompt.length < TRIVIAL_MAX_CHARS) return \"trivial\";\n\tif (prompt.length >= COMPLEX_PROSE_MIN_CHARS) return \"plan\";\n\tif (input.laneType) return LANE_FALLBACK_CLASS[input.laneType];\n\treturn \"code-gen\";\n}\n\n/** Reasoning ladder used for targets and clamping. Intentionally excludes \"off\". */\nconst REASONING_LADDER: readonly ThinkingLevel[] = [\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n\n/** Static rule table: task class -> recommended ThinkingLevel (before lane adjustment and clamping). */\nexport const TASK_CLASS_THINKING_LEVELS: Readonly<Record<TaskClass, 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. */\nconst LANE_STEP: Readonly<Partial<Record<ReasoningLaneType, 1 | -1>>> = {\n\tplanner: 1,\n\tsecurity: 1,\n\texplorer: -1,\n};\n\n/**\n * Resolve the recommended ThinkingLevel for a task class:\n * 1. Look up the static rule table.\n * 2. Apply the lane adjustment (one ladder step, saturating at ladder ends).\n * 3. Clamp to the highest level in `availableLevels` that is <= the target;\n * if no available level is at/below the target, return the lowest available\n * reasoning level. Never invents a level outside `availableLevels`.\n *\n * `\"off\"` is never a router output for reasoning models. If `availableLevels`\n * contains no ladder level at all (e.g. a non-reasoning model exposing only\n * \"off\"), the first available level is returned; callers are expected to\n * bypass the router entirely for models with `reasoning: false`.\n */\nexport function resolveThinkingLevel(\n\ttaskClass: TaskClass,\n\tavailableLevels: readonly ThinkingLevel[],\n\tlaneType?: ReasoningLaneType,\n): ThinkingLevel {\n\tconst baseIndex = REASONING_LADDER.indexOf(TASK_CLASS_THINKING_LEVELS[taskClass]);\n\tconst step = laneType ? (LANE_STEP[laneType] ?? 0) : 0;\n\tconst targetIndex = Math.min(Math.max(baseIndex + step, 0), REASONING_LADDER.length - 1);\n\n\tconst availableOnLadder = REASONING_LADDER.filter((level) => availableLevels.includes(level));\n\tif (availableOnLadder.length === 0) {\n\t\treturn availableLevels[0] ?? \"off\";\n\t}\n\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"]}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reasoning-effort router: pure, side-effect-free task classification and
|
|
3
|
-
* ThinkingLevel resolution for the `auto` thinking mode.
|
|
4
|
-
*
|
|
5
|
-
* Deterministic by construction: consumes only the prompt text and an optional
|
|
6
|
-
* subagent lane type. No clock, randomness, I/O, or imports from session, TUI,
|
|
7
|
-
* or provider code (the ThinkingLevel import below is type-only and erased).
|
|
8
|
-
*/
|
|
9
|
-
/** Prompts shorter than this (trimmed) classify as trivial when no stronger signal matches. */
|
|
10
|
-
const TRIVIAL_MAX_CHARS = 40;
|
|
11
|
-
/** Prompts at/above this length with no keyword/code/diff signal are treated as long prose briefs (plan). */
|
|
12
|
-
const COMPLEX_PROSE_MIN_CHARS = 2400;
|
|
13
|
-
/**
|
|
14
|
-
* Keyword families in FIXED precedence order:
|
|
15
|
-
* debug > refactor > review > plan > simple-edit > code-gen.
|
|
16
|
-
* The first matching family wins; ties are impossible by construction.
|
|
17
|
-
* Patterns are case-insensitive and stateless (no `g` flag, so no lastIndex carry-over).
|
|
18
|
-
*/
|
|
19
|
-
const KEYWORD_FAMILIES = [
|
|
20
|
-
{
|
|
21
|
-
taskClass: "debug",
|
|
22
|
-
pattern: /\b(debug(ging)?|bugs?|fix(es|ing|ed)?|errors?|exceptions?|crash(es|ing|ed)?|stack\s*trace|traceback|regressions?|broken|fail(s|ing|ed|ure|ures)?|flaky|reproduce)\b/i,
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
taskClass: "refactor",
|
|
26
|
-
pattern: /\b(refactor(ing|ed)?|restructur(e|ing)|renam(e|ing)|extract(ing)?|clean\s*up|cleanup|simplif(y|ying|ied)|deduplicat(e|ing)|reorganiz(e|ing)|rewrit(e|ing)|modulariz(e|ing))\b/i,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
taskClass: "review",
|
|
30
|
-
pattern: /\b(review(s|ing|ed)?|audit(s|ing|ed)?|critique|assess(ing|ment)?|inspect(ing)?|lgtm|approve)\b/i,
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
taskClass: "plan",
|
|
34
|
-
pattern: /\b(plan(s|ning)?|design(s|ing)?|architect(s|ure|ing)?|roadmap|spec(s|ification|ifications)?|strateg(y|ies|ize)|decompos(e|ing|ition)|break\s+(this\s+|it\s+)?down|milestones?)\b/i,
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
taskClass: "simple-edit",
|
|
38
|
-
pattern: /\b(typos?|tweak(s|ing|ed)?|one-?liner?|single\s+line|bump(s|ing|ed)?|whitespace|reword(s|ing|ed)?|indentation|punctuation)\b/i,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
taskClass: "code-gen",
|
|
42
|
-
pattern: /\b(implement(s|ing|ation)?|writ(e|ing)|creat(e|es|ing)|add(s|ing|ed)?|build(s|ing)?|generat(e|es|ing)|scaffold(ing)?|prototype)\b/i,
|
|
43
|
-
},
|
|
44
|
-
];
|
|
45
|
-
/** Fallback class per lane when no keyword, code/diff, or length signal decides. */
|
|
46
|
-
const LANE_FALLBACK_CLASS = {
|
|
47
|
-
planner: "plan",
|
|
48
|
-
security: "review",
|
|
49
|
-
explorer: "review",
|
|
50
|
-
coder: "code-gen",
|
|
51
|
-
reviewer: "review",
|
|
52
|
-
tester: "code-gen",
|
|
53
|
-
};
|
|
54
|
-
function hasCodeFence(text) {
|
|
55
|
-
return text.includes("```");
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Diff detection: explicit hunk (`@@ ... @@`) or `diff --git` headers count alone;
|
|
59
|
-
* bare `+`/`-` line starts only count when both added AND removed lines are present
|
|
60
|
-
* (avoids false positives on markdown bullet lists).
|
|
61
|
-
*/
|
|
62
|
-
function hasDiffMarkers(text) {
|
|
63
|
-
if (/^@@[^\n]*@@/m.test(text) || /^diff --git /m.test(text))
|
|
64
|
-
return true;
|
|
65
|
-
return /^\+(?!\+)/m.test(text) && /^-(?!-)/m.test(text);
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Classify a turn deterministically. Signal precedence (first match wins):
|
|
69
|
-
* 1. Keyword families (debug > refactor > review > plan > simple-edit > code-gen)
|
|
70
|
-
* 2. Code fence or diff markers -> code-gen
|
|
71
|
-
* 3. Trimmed length < TRIVIAL_MAX_CHARS -> trivial
|
|
72
|
-
* 4. Trimmed length >= COMPLEX_PROSE_MIN_CHARS -> plan (long prose brief)
|
|
73
|
-
* 5. Lane fallback (see LANE_FALLBACK_CLASS)
|
|
74
|
-
* 6. Default -> code-gen
|
|
75
|
-
*/
|
|
76
|
-
export function classifyTask(input) {
|
|
77
|
-
const prompt = input.prompt.trim();
|
|
78
|
-
for (const family of KEYWORD_FAMILIES) {
|
|
79
|
-
if (family.pattern.test(prompt))
|
|
80
|
-
return family.taskClass;
|
|
81
|
-
}
|
|
82
|
-
if (hasCodeFence(prompt) || hasDiffMarkers(prompt))
|
|
83
|
-
return "code-gen";
|
|
84
|
-
if (prompt.length < TRIVIAL_MAX_CHARS)
|
|
85
|
-
return "trivial";
|
|
86
|
-
if (prompt.length >= COMPLEX_PROSE_MIN_CHARS)
|
|
87
|
-
return "plan";
|
|
88
|
-
if (input.laneType)
|
|
89
|
-
return LANE_FALLBACK_CLASS[input.laneType];
|
|
90
|
-
return "code-gen";
|
|
91
|
-
}
|
|
92
|
-
/** Reasoning ladder used for targets and clamping. Intentionally excludes "off". */
|
|
93
|
-
const REASONING_LADDER = ["minimal", "low", "medium", "high", "xhigh", "max"];
|
|
94
|
-
/** Static rule table: task class -> recommended ThinkingLevel (before lane adjustment and clamping). */
|
|
95
|
-
export const TASK_CLASS_THINKING_LEVELS = {
|
|
96
|
-
trivial: "minimal",
|
|
97
|
-
"simple-edit": "low",
|
|
98
|
-
"code-gen": "medium",
|
|
99
|
-
debug: "high",
|
|
100
|
-
refactor: "high",
|
|
101
|
-
review: "high",
|
|
102
|
-
plan: "xhigh",
|
|
103
|
-
};
|
|
104
|
-
/** Lane adjustment in ladder steps: planner/security escalate, explorer de-escalates. */
|
|
105
|
-
const LANE_STEP = {
|
|
106
|
-
planner: 1,
|
|
107
|
-
security: 1,
|
|
108
|
-
explorer: -1,
|
|
109
|
-
};
|
|
110
|
-
/**
|
|
111
|
-
* Resolve the recommended ThinkingLevel for a task class:
|
|
112
|
-
* 1. Look up the static rule table.
|
|
113
|
-
* 2. Apply the lane adjustment (one ladder step, saturating at ladder ends).
|
|
114
|
-
* 3. Clamp to the highest level in `availableLevels` that is <= the target;
|
|
115
|
-
* if no available level is at/below the target, return the lowest available
|
|
116
|
-
* reasoning level. Never invents a level outside `availableLevels`.
|
|
117
|
-
*
|
|
118
|
-
* `"off"` is never a router output for reasoning models. If `availableLevels`
|
|
119
|
-
* contains no ladder level at all (e.g. a non-reasoning model exposing only
|
|
120
|
-
* "off"), the first available level is returned; callers are expected to
|
|
121
|
-
* bypass the router entirely for models with `reasoning: false`.
|
|
122
|
-
*/
|
|
123
|
-
export function resolveThinkingLevel(taskClass, availableLevels, laneType) {
|
|
124
|
-
const baseIndex = REASONING_LADDER.indexOf(TASK_CLASS_THINKING_LEVELS[taskClass]);
|
|
125
|
-
const step = laneType ? (LANE_STEP[laneType] ?? 0) : 0;
|
|
126
|
-
const targetIndex = Math.min(Math.max(baseIndex + step, 0), REASONING_LADDER.length - 1);
|
|
127
|
-
const availableOnLadder = REASONING_LADDER.filter((level) => availableLevels.includes(level));
|
|
128
|
-
if (availableOnLadder.length === 0) {
|
|
129
|
-
return availableLevels[0] ?? "off";
|
|
130
|
-
}
|
|
131
|
-
for (let i = targetIndex; i >= 0; i--) {
|
|
132
|
-
const candidate = REASONING_LADDER[i];
|
|
133
|
-
if (availableOnLadder.includes(candidate))
|
|
134
|
-
return candidate;
|
|
135
|
-
}
|
|
136
|
-
return availableOnLadder[0];
|
|
137
|
-
}
|
|
138
|
-
//# sourceMappingURL=reasoning-router.js.map
|