@wrongstack/core 0.109.1 → 0.141.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-mOxbpFcg.d.ts → agent-bridge-r9y6gdn4.d.ts} +1 -1
- package/dist/{agent-subagent-runner-DukQLUcS.d.ts → agent-subagent-runner-1GeQE_L0.d.ts} +7 -8
- package/dist/{brain-Dfv4Y82E.d.ts → brain-Cp_3GIS2.d.ts} +14 -3
- package/dist/{compactor-DXLxLcmU.d.ts → compactor-BueGt7LG.d.ts} +9 -1
- package/dist/{config-BSU-6vah.d.ts → config-BaVThgnT.d.ts} +104 -3
- package/dist/{context-CNRYfhUv.d.ts → context-C7G_MtLV.d.ts} +38 -1
- package/dist/coordination/index.d.ts +12 -13
- package/dist/coordination/index.js +229 -17
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +26 -27
- package/dist/defaults/index.js +886 -721
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +15 -16
- package/dist/execution/index.js +535 -322
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +6 -7
- package/dist/{goal-preamble-CI8lxeY1.d.ts → goal-preamble-iuIUTQwk.d.ts} +48 -18
- package/dist/{index-BWRN6wOb.d.ts → index-BZdezm3g.d.ts} +9 -10
- package/dist/{index-DIKEcfgC.d.ts → index-CPweVoFM.d.ts} +7 -6
- package/dist/index.d.ts +51 -52
- package/dist/index.js +752 -545
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -7
- package/dist/infrastructure/index.js +64 -19
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +10 -11
- package/dist/kernel/index.js.map +1 -1
- package/dist/llm-selector-CP72f1lC.d.ts +58 -0
- package/dist/{mcp-servers-CXCsANdY.d.ts → mcp-servers-Bl5LTvQg.d.ts} +3 -3
- package/dist/{mode-ARA3HrkY.d.ts → mode-CZlO9iU1.d.ts} +1 -1
- package/dist/models/index.d.ts +157 -53
- package/dist/models/index.js +475 -20
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-DU64QxQa.d.ts → models-registry-D90K9UnM.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-51LvnXkD.d.ts → multi-agent-coordinator-QWEzJDlm.d.ts} +1 -1
- package/dist/{null-fleet-bus-D09hMzFQ.d.ts → null-fleet-bus-BUyfqh23.d.ts} +13 -10
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-B2CbsKpc.d.ts → parallel-eternal-engine-Dj2SYzha.d.ts} +24 -11
- package/dist/{path-resolver-DDJiMAtX.d.ts → path-resolver-DRjQBkoO.d.ts} +3 -3
- package/dist/{permission-BDv7z0mk.d.ts → permission-B7nKnEvQ.d.ts} +1 -1
- package/dist/{permission-policy-dF74EpDp.d.ts → permission-policy-8-6zBmfA.d.ts} +2 -13
- package/dist/{pipeline-BqiA_UMr.d.ts → pipeline-BG7UgbDc.d.ts} +2 -2
- package/dist/{plan-templates-BdDxl9cI.d.ts → plan-templates-CkKNPU3I.d.ts} +6 -6
- package/dist/{provider-runner-BUunikwY.d.ts → provider-runner-BNpuIyOL.d.ts} +3 -3
- package/dist/{retry-policy-BcmuT_V0.d.ts → retry-policy-rutAfVeR.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -9
- package/dist/sdd/index.js +208 -1
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-DrOhc2i5.d.ts → secret-vault-BTcC_T5v.d.ts} +3 -2
- package/dist/security/index.d.ts +5 -7
- package/dist/security/index.js +4 -35
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-C7wcdqMA.d.ts → selector-4vDFZKt3.d.ts} +1 -1
- package/dist/{session-event-bridge-BpJ5trO9.d.ts → session-event-bridge-DWlvglC2.d.ts} +2 -2
- package/dist/{session-reader-DDz1Ek4V.d.ts → session-reader-BAtCxdaw.d.ts} +1 -1
- package/dist/storage/index.d.ts +14 -15
- package/dist/storage/index.js +140 -134
- package/dist/storage/index.js.map +1 -1
- package/dist/types/index.d.ts +19 -20
- package/dist/types/index.js +186 -102
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +38 -15
- package/dist/utils/index.js +62 -33
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-_lqjzErq.d.ts → wstack-paths-DD50Omgn.d.ts} +3 -0
- package/package.json +2 -2
- package/dist/models-registry-B6_KfS65.d.ts +0 -95
package/dist/models/index.js
CHANGED
|
@@ -756,6 +756,55 @@ function expectDefined(value, label) {
|
|
|
756
756
|
return value;
|
|
757
757
|
}
|
|
758
758
|
|
|
759
|
+
// src/utils/token-estimate.ts
|
|
760
|
+
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
761
|
+
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
762
|
+
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
763
|
+
function getCachedEstimate(key, compute) {
|
|
764
|
+
const existing = ESTIMATE_CACHE.get(key);
|
|
765
|
+
if (existing !== void 0) return existing;
|
|
766
|
+
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
767
|
+
const keys = [...ESTIMATE_CACHE.keys()];
|
|
768
|
+
for (let i = 0; i < Math.floor(ESTIMATE_CACHE_MAX_SIZE / 4); i++) {
|
|
769
|
+
ESTIMATE_CACHE.delete(expectDefined(keys[i]));
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
const estimate = compute();
|
|
773
|
+
ESTIMATE_CACHE.set(key, estimate);
|
|
774
|
+
return estimate;
|
|
775
|
+
}
|
|
776
|
+
function estimateToolInputTokens(input) {
|
|
777
|
+
if (typeof input === "string") return RoughTokenEstimate(input);
|
|
778
|
+
if (input === null || typeof input !== "object") {
|
|
779
|
+
return RoughTokenEstimate(String(input));
|
|
780
|
+
}
|
|
781
|
+
const key = JSON.stringify(input);
|
|
782
|
+
return getCachedEstimate(key, () => RoughTokenEstimate(key));
|
|
783
|
+
}
|
|
784
|
+
function estimateToolResultTokens(content) {
|
|
785
|
+
if (typeof content === "string") return RoughTokenEstimate(content);
|
|
786
|
+
const key = JSON.stringify(content);
|
|
787
|
+
return getCachedEstimate(key, () => RoughTokenEstimate(key));
|
|
788
|
+
}
|
|
789
|
+
function estimateTextTokens(text) {
|
|
790
|
+
return RoughTokenEstimate(text);
|
|
791
|
+
}
|
|
792
|
+
function estimateMessageTokens(messages) {
|
|
793
|
+
let total = 0;
|
|
794
|
+
for (const m of messages) {
|
|
795
|
+
if (typeof m.content === "string") {
|
|
796
|
+
total += estimateTextTokens(m.content);
|
|
797
|
+
} else {
|
|
798
|
+
for (const b of m.content) {
|
|
799
|
+
if (b.type === "text") total += estimateTextTokens(b.text);
|
|
800
|
+
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
801
|
+
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return total;
|
|
806
|
+
}
|
|
807
|
+
|
|
759
808
|
// src/types/blocks.ts
|
|
760
809
|
function isTextBlock(b) {
|
|
761
810
|
return b.type === "text";
|
|
@@ -784,20 +833,6 @@ Rules:
|
|
|
784
833
|
- If unsure, keep rather than collapse (errors are more costly than waste)
|
|
785
834
|
|
|
786
835
|
Return ONLY the JSON object, no markdown, no explanation outside the JSON.`;
|
|
787
|
-
function estimateTokens(messages) {
|
|
788
|
-
let total = 0;
|
|
789
|
-
for (const m of messages) {
|
|
790
|
-
if (typeof m.content === "string") {
|
|
791
|
-
total += Math.ceil(m.content.length / 4);
|
|
792
|
-
} else if (Array.isArray(m.content)) {
|
|
793
|
-
for (const b of m.content) {
|
|
794
|
-
if (b.type === "text") total += Math.ceil(b.text.length / 4);
|
|
795
|
-
else total += Math.ceil(JSON.stringify(b).length / 4);
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
return total;
|
|
800
|
-
}
|
|
801
836
|
function formatMessages(messages, maxChars = 8e3) {
|
|
802
837
|
const lines = [];
|
|
803
838
|
let used = 0;
|
|
@@ -836,7 +871,7 @@ var LLMSelector = class {
|
|
|
836
871
|
async select(messages, maxToKeep) {
|
|
837
872
|
const effectiveBudget = Math.min(maxToKeep, this.maxContextTokens);
|
|
838
873
|
const historyText = formatMessages(messages);
|
|
839
|
-
const totalTokens =
|
|
874
|
+
const totalTokens = estimateMessageTokens(messages);
|
|
840
875
|
const systemText = `${this.systemPrompt}
|
|
841
876
|
|
|
842
877
|
Conversation (${messages.length} messages, ~${totalTokens} tokens, budget: ${effectiveBudget}):
|
|
@@ -868,10 +903,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
|
|
|
868
903
|
let startIdx = 0;
|
|
869
904
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
870
905
|
const m = expectDefined(messages[i]);
|
|
871
|
-
const cost =
|
|
872
|
-
(acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
|
|
873
|
-
0
|
|
874
|
-
);
|
|
906
|
+
const cost = estimateMessageTokens([m]);
|
|
875
907
|
if (tokenCount + cost <= budget) {
|
|
876
908
|
tokenCount += cost;
|
|
877
909
|
} else {
|
|
@@ -922,6 +954,429 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
|
|
|
922
954
|
}
|
|
923
955
|
};
|
|
924
956
|
|
|
925
|
-
|
|
957
|
+
// src/models/model-intelligence.ts
|
|
958
|
+
var MODEL_PROFILES = [
|
|
959
|
+
// ── Anthropic ──
|
|
960
|
+
{
|
|
961
|
+
provider: "anthropic",
|
|
962
|
+
pattern: /claude-opus/i,
|
|
963
|
+
family: "Claude Opus",
|
|
964
|
+
strengths: ["complex reasoning", "multi-step planning", "nuanced judgment", "long-form analysis"],
|
|
965
|
+
weaknesses: ["latency", "cost"],
|
|
966
|
+
bestFor: ["planning", "security", "debugging", "review"],
|
|
967
|
+
costTier: "premium",
|
|
968
|
+
speedTier: "slow"
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
provider: "anthropic",
|
|
972
|
+
pattern: /claude-sonnet/i,
|
|
973
|
+
family: "Claude Sonnet",
|
|
974
|
+
strengths: ["coding", "balanced reasoning", "tool use", "fast iteration"],
|
|
975
|
+
bestFor: ["coding", "refactoring", "backend", "general"],
|
|
976
|
+
costTier: "standard",
|
|
977
|
+
speedTier: "fast"
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
provider: "anthropic",
|
|
981
|
+
pattern: /claude-haiku/i,
|
|
982
|
+
family: "Claude Haiku",
|
|
983
|
+
strengths: ["speed", "low cost", "simple tasks"],
|
|
984
|
+
weaknesses: ["complex reasoning", "long context"],
|
|
985
|
+
bestFor: ["lightweight", "docs", "frontend"],
|
|
986
|
+
avoidFor: ["planning", "security"],
|
|
987
|
+
costTier: "budget",
|
|
988
|
+
speedTier: "fast"
|
|
989
|
+
},
|
|
990
|
+
// ── OpenAI ──
|
|
991
|
+
{
|
|
992
|
+
provider: "openai",
|
|
993
|
+
pattern: /gpt-5|o3|o4/i,
|
|
994
|
+
family: "GPT-5 / o3 / o4",
|
|
995
|
+
strengths: ["complex reasoning", "coding", "multi-modal"],
|
|
996
|
+
bestFor: ["planning", "coding", "debugging", "general"],
|
|
997
|
+
costTier: "premium",
|
|
998
|
+
speedTier: "normal"
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
provider: "openai",
|
|
1002
|
+
pattern: /gpt-4\.1|gpt-4o/i,
|
|
1003
|
+
family: "GPT-4.1 / 4o",
|
|
1004
|
+
strengths: ["coding", "balanced reasoning", "fast"],
|
|
1005
|
+
bestFor: ["coding", "refactoring", "backend", "docs"],
|
|
1006
|
+
costTier: "standard",
|
|
1007
|
+
speedTier: "fast"
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
provider: "openai",
|
|
1011
|
+
pattern: /gpt-4o-mini/i,
|
|
1012
|
+
family: "GPT-4o Mini",
|
|
1013
|
+
strengths: ["speed", "low cost", "simple tasks"],
|
|
1014
|
+
weaknesses: ["complex reasoning"],
|
|
1015
|
+
bestFor: ["lightweight", "frontend", "docs"],
|
|
1016
|
+
avoidFor: ["planning", "security"],
|
|
1017
|
+
costTier: "budget",
|
|
1018
|
+
speedTier: "fast"
|
|
1019
|
+
},
|
|
1020
|
+
// ── Google ──
|
|
1021
|
+
{
|
|
1022
|
+
provider: "google",
|
|
1023
|
+
pattern: /gemini-(?:2\.5|3)/i,
|
|
1024
|
+
family: "Gemini 2.5 / 3",
|
|
1025
|
+
strengths: ["long context", "multi-modal", "coding", "reasoning"],
|
|
1026
|
+
bestFor: ["coding", "planning", "data", "general"],
|
|
1027
|
+
costTier: "standard",
|
|
1028
|
+
speedTier: "normal"
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
provider: "google",
|
|
1032
|
+
pattern: /gemini-2\.0-flash|gemini-flash/i,
|
|
1033
|
+
family: "Gemini Flash",
|
|
1034
|
+
strengths: ["speed", "low cost", "long context"],
|
|
1035
|
+
weaknesses: ["deep reasoning"],
|
|
1036
|
+
bestFor: ["lightweight", "docs", "frontend", "data"],
|
|
1037
|
+
avoidFor: ["planning", "security"],
|
|
1038
|
+
costTier: "budget",
|
|
1039
|
+
speedTier: "fast"
|
|
1040
|
+
},
|
|
1041
|
+
// ── DeepSeek ──
|
|
1042
|
+
{
|
|
1043
|
+
provider: "deepseek",
|
|
1044
|
+
pattern: /deepseek-v3|deepseek-r1/i,
|
|
1045
|
+
family: "DeepSeek V3 / R1",
|
|
1046
|
+
strengths: ["coding", "math", "reasoning", "cost-effective"],
|
|
1047
|
+
bestFor: ["coding", "refactoring", "debugging", "general"],
|
|
1048
|
+
costTier: "standard",
|
|
1049
|
+
speedTier: "normal"
|
|
1050
|
+
},
|
|
1051
|
+
// ── OpenRouter / catch-all ──
|
|
1052
|
+
{
|
|
1053
|
+
provider: "openrouter",
|
|
1054
|
+
pattern: /.*/,
|
|
1055
|
+
family: "OpenRouter (routed)",
|
|
1056
|
+
strengths: ["model variety", "fallback"],
|
|
1057
|
+
bestFor: ["general"],
|
|
1058
|
+
costTier: "standard",
|
|
1059
|
+
speedTier: "normal"
|
|
1060
|
+
}
|
|
1061
|
+
];
|
|
1062
|
+
var TASK_TO_ROLE = {
|
|
1063
|
+
coding: ["executor", "architect", "bug-hunter"],
|
|
1064
|
+
planning: ["planner", "architect", "refactor-planner"],
|
|
1065
|
+
security: ["security-scanner", "security-reviewer"],
|
|
1066
|
+
docs: ["document", "simplifier"],
|
|
1067
|
+
testing: ["test", "e2e"],
|
|
1068
|
+
refactoring: ["refactor-planner", "refactor", "simplifier"],
|
|
1069
|
+
debugging: ["debugger", "bug-hunter", "tracer"],
|
|
1070
|
+
data: ["analyst", "data", "database"],
|
|
1071
|
+
frontend: ["frontend", "designer"],
|
|
1072
|
+
backend: ["backend", "api", "auth"],
|
|
1073
|
+
review: ["code-reviewer", "critic"],
|
|
1074
|
+
lightweight: ["executor", "simplifier"],
|
|
1075
|
+
general: ["executor", "analyst"]
|
|
1076
|
+
};
|
|
1077
|
+
function inferTaskType(description, role) {
|
|
1078
|
+
const d = description.toLowerCase();
|
|
1079
|
+
const keywords = [
|
|
1080
|
+
[/plan|architect|design|strategy|blueprint|system\s*design/i, "planning"],
|
|
1081
|
+
[/security|vuln|exploit|injection|auth/i, "security"],
|
|
1082
|
+
[/doc|readme|explain|write\s*(a|the)\s*doc|tutorial/i, "docs"],
|
|
1083
|
+
[/test|spec|assert|mock|coverage/i, "testing"],
|
|
1084
|
+
[/refactor|clean\s*up|restructure|simplify/i, "refactoring"],
|
|
1085
|
+
[/bug|fix|debug|trace|crash|error/i, "debugging"],
|
|
1086
|
+
[/data|json|sql|query|analyze|parse/i, "data"],
|
|
1087
|
+
[/frontend|react|ui|css|component|jsx/i, "frontend"],
|
|
1088
|
+
[/backend|api|server|endpoint|route|middleware/i, "backend"],
|
|
1089
|
+
[/review|audit|inspect|check/i, "review"],
|
|
1090
|
+
[/simple|quick|one-liner|trivial/i, "lightweight"]
|
|
1091
|
+
];
|
|
1092
|
+
for (const [re, taskType] of keywords) {
|
|
1093
|
+
if (re.test(d)) return taskType;
|
|
1094
|
+
}
|
|
1095
|
+
if (role) {
|
|
1096
|
+
for (const [taskType, roles] of Object.entries(TASK_TO_ROLE)) {
|
|
1097
|
+
if (roles.includes(role)) return taskType;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
return "general";
|
|
1101
|
+
}
|
|
1102
|
+
function findModelProfile(provider, modelId) {
|
|
1103
|
+
const candidates = MODEL_PROFILES.filter((p) => p.provider === provider);
|
|
1104
|
+
for (const p of candidates) {
|
|
1105
|
+
if (p.pattern.test(modelId)) return p;
|
|
1106
|
+
}
|
|
1107
|
+
return void 0;
|
|
1108
|
+
}
|
|
1109
|
+
function scoreModelForTask(profile, taskType) {
|
|
1110
|
+
if (!profile) return 50;
|
|
1111
|
+
if (profile.avoidFor?.includes(taskType)) return 10;
|
|
1112
|
+
const bestIdx = profile.bestFor.indexOf(taskType);
|
|
1113
|
+
if (bestIdx >= 0) {
|
|
1114
|
+
return 90 - bestIdx * 10;
|
|
1115
|
+
}
|
|
1116
|
+
let score = 50;
|
|
1117
|
+
if (taskType === "lightweight") {
|
|
1118
|
+
score += profile.costTier === "budget" ? 30 : profile.costTier === "standard" ? 15 : 0;
|
|
1119
|
+
score += profile.speedTier === "fast" ? 20 : 0;
|
|
1120
|
+
}
|
|
1121
|
+
if (taskType === "planning" || taskType === "security") {
|
|
1122
|
+
score += profile.costTier === "premium" ? 20 : 0;
|
|
1123
|
+
score += profile.speedTier === "slow" ? 10 : 0;
|
|
1124
|
+
}
|
|
1125
|
+
return score;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// src/models/model-router.ts
|
|
1129
|
+
var DEFAULT_PROFILES = [
|
|
1130
|
+
{ provider: "anthropic", pattern: /claude-opus/i, family: "Claude Opus", strengths: ["reasoning", "planning"], bestFor: ["planning", "security", "debugging"], costTier: "premium", speedTier: "slow" },
|
|
1131
|
+
{ provider: "anthropic", pattern: /claude-sonnet/i, family: "Claude Sonnet", strengths: ["coding", "balanced"], bestFor: ["coding", "general"], costTier: "standard", speedTier: "fast" },
|
|
1132
|
+
{ provider: "anthropic", pattern: /claude-haiku/i, family: "Claude Haiku", strengths: ["speed"], bestFor: ["lightweight", "docs"], avoidFor: ["planning"], costTier: "budget", speedTier: "fast" },
|
|
1133
|
+
{ provider: "openai", pattern: /gpt-5|o3|o4/i, family: "GPT-5/o3/o4", strengths: ["reasoning", "coding"], bestFor: ["planning", "coding", "debugging"], costTier: "premium", speedTier: "normal" },
|
|
1134
|
+
{ provider: "openai", pattern: /gpt-4/i, family: "GPT-4", strengths: ["coding"], bestFor: ["coding", "docs"], costTier: "standard", speedTier: "fast" },
|
|
1135
|
+
{ provider: "openai", pattern: /gpt-4o-mini/i, family: "GPT-4o Mini", strengths: ["speed"], bestFor: ["lightweight", "docs"], avoidFor: ["planning"], costTier: "budget", speedTier: "fast" },
|
|
1136
|
+
{ provider: "google", pattern: /gemini-(?:2\.5|3)/i, family: "Gemini 2.5/3", strengths: ["context", "coding"], bestFor: ["coding", "data"], costTier: "standard", speedTier: "normal" },
|
|
1137
|
+
{ provider: "google", pattern: /gemini.*flash/i, family: "Gemini Flash", strengths: ["speed"], bestFor: ["lightweight", "docs"], avoidFor: ["planning"], costTier: "budget", speedTier: "fast" },
|
|
1138
|
+
{ provider: "deepseek", pattern: /deepseek/i, family: "DeepSeek", strengths: ["coding", "cost-effective"], bestFor: ["coding", "general"], costTier: "standard", speedTier: "normal" }
|
|
1139
|
+
];
|
|
1140
|
+
var TASK_CATEGORIES = {
|
|
1141
|
+
plan: "planning",
|
|
1142
|
+
architect: "planning",
|
|
1143
|
+
design: "planning",
|
|
1144
|
+
strategy: "planning",
|
|
1145
|
+
security: "security",
|
|
1146
|
+
vuln: "security",
|
|
1147
|
+
exploit: "security",
|
|
1148
|
+
auth: "security",
|
|
1149
|
+
doc: "docs",
|
|
1150
|
+
readme: "docs",
|
|
1151
|
+
explain: "docs",
|
|
1152
|
+
write: "docs",
|
|
1153
|
+
test: "testing",
|
|
1154
|
+
spec: "testing",
|
|
1155
|
+
assert: "testing",
|
|
1156
|
+
coverage: "testing",
|
|
1157
|
+
refactor: "refactoring",
|
|
1158
|
+
cleanup: "refactoring",
|
|
1159
|
+
restructure: "refactoring",
|
|
1160
|
+
bug: "debugging",
|
|
1161
|
+
fix: "debugging",
|
|
1162
|
+
debug: "debugging",
|
|
1163
|
+
trace: "debugging",
|
|
1164
|
+
crash: "debugging",
|
|
1165
|
+
data: "data",
|
|
1166
|
+
json: "data",
|
|
1167
|
+
sql: "data",
|
|
1168
|
+
query: "data",
|
|
1169
|
+
analyze: "data",
|
|
1170
|
+
parse: "data",
|
|
1171
|
+
frontend: "frontend",
|
|
1172
|
+
react: "frontend",
|
|
1173
|
+
ui: "frontend",
|
|
1174
|
+
css: "frontend",
|
|
1175
|
+
component: "frontend",
|
|
1176
|
+
backend: "backend",
|
|
1177
|
+
api: "backend",
|
|
1178
|
+
server: "backend",
|
|
1179
|
+
endpoint: "backend",
|
|
1180
|
+
review: "review",
|
|
1181
|
+
audit: "review",
|
|
1182
|
+
inspect: "review",
|
|
1183
|
+
simple: "lightweight",
|
|
1184
|
+
quick: "lightweight",
|
|
1185
|
+
trivial: "lightweight"
|
|
1186
|
+
};
|
|
1187
|
+
var ModelRouter = class {
|
|
1188
|
+
profiles;
|
|
1189
|
+
matrix;
|
|
1190
|
+
config;
|
|
1191
|
+
costs = { byModel: {}, totalCost: 0 };
|
|
1192
|
+
constructor(opts) {
|
|
1193
|
+
this.profiles = opts.profiles ?? DEFAULT_PROFILES;
|
|
1194
|
+
this.matrix = opts.matrix ?? {};
|
|
1195
|
+
this.config = opts.config;
|
|
1196
|
+
}
|
|
1197
|
+
/** Look up the model matrix resolution for a role (role → phase → * → leader). */
|
|
1198
|
+
resolveMatrix(role) {
|
|
1199
|
+
if (this.matrix[role]) return this.matrix[role];
|
|
1200
|
+
const phase = this.roleToPhase(role);
|
|
1201
|
+
if (phase && this.matrix[phase]) return this.matrix[phase];
|
|
1202
|
+
if (this.matrix["*"]) return this.matrix["*"];
|
|
1203
|
+
return void 0;
|
|
1204
|
+
}
|
|
1205
|
+
/** Simplified phase lookup for common roles. */
|
|
1206
|
+
roleToPhase(role) {
|
|
1207
|
+
const phaseMap = {
|
|
1208
|
+
planner: "plan",
|
|
1209
|
+
architect: "plan",
|
|
1210
|
+
"refactor-planner": "plan",
|
|
1211
|
+
executor: "code",
|
|
1212
|
+
refactor: "code",
|
|
1213
|
+
simplifier: "code",
|
|
1214
|
+
migration: "code",
|
|
1215
|
+
"bug-hunter": "code",
|
|
1216
|
+
debugger: "code",
|
|
1217
|
+
tracer: "code",
|
|
1218
|
+
test: "code",
|
|
1219
|
+
e2e: "code",
|
|
1220
|
+
performance: "code",
|
|
1221
|
+
chaos: "code",
|
|
1222
|
+
"security-scanner": "review",
|
|
1223
|
+
"security-reviewer": "review",
|
|
1224
|
+
"code-reviewer": "review",
|
|
1225
|
+
critic: "review",
|
|
1226
|
+
accessibility: "review",
|
|
1227
|
+
compliance: "review",
|
|
1228
|
+
analyst: "code",
|
|
1229
|
+
data: "code",
|
|
1230
|
+
database: "code",
|
|
1231
|
+
frontend: "code",
|
|
1232
|
+
backend: "code",
|
|
1233
|
+
api: "code",
|
|
1234
|
+
auth: "code",
|
|
1235
|
+
designer: "code",
|
|
1236
|
+
document: "code",
|
|
1237
|
+
researcher: "plan",
|
|
1238
|
+
explore: "plan",
|
|
1239
|
+
search: "plan"
|
|
1240
|
+
};
|
|
1241
|
+
return phaseMap[role];
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Pick the best model for a task.
|
|
1245
|
+
*
|
|
1246
|
+
* Priority:
|
|
1247
|
+
* 1. User's /setmodel matrix entry for this role (explicit override)
|
|
1248
|
+
* 2. Best-matching model from intelligence profiles
|
|
1249
|
+
* 3. Leader model (fallback)
|
|
1250
|
+
*/
|
|
1251
|
+
pickForTask(role, description) {
|
|
1252
|
+
const matrixEntry = this.resolveMatrix(role);
|
|
1253
|
+
if (matrixEntry) {
|
|
1254
|
+
const provider = matrixEntry.provider ?? this.config.provider;
|
|
1255
|
+
return {
|
|
1256
|
+
provider,
|
|
1257
|
+
model: matrixEntry.model,
|
|
1258
|
+
reason: `matrix override for role ${role}`,
|
|
1259
|
+
fromMatrix: true
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
const category = this.inferCategory(description, role);
|
|
1263
|
+
const best = this.findBestModel(category);
|
|
1264
|
+
if (best) {
|
|
1265
|
+
return { ...best, reason: `best-for ${category} (auto-detected)`, fromMatrix: false };
|
|
1266
|
+
}
|
|
1267
|
+
return {
|
|
1268
|
+
provider: this.config.provider,
|
|
1269
|
+
model: this.config.model,
|
|
1270
|
+
reason: "leader fallback",
|
|
1271
|
+
fromMatrix: false
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
/** Infer task category from description keywords + role. */
|
|
1275
|
+
inferCategory(description, role) {
|
|
1276
|
+
const d = description.toLowerCase();
|
|
1277
|
+
for (const [keyword, category] of Object.entries(TASK_CATEGORIES)) {
|
|
1278
|
+
if (d.includes(keyword)) return category;
|
|
1279
|
+
}
|
|
1280
|
+
const roleMap = {
|
|
1281
|
+
"security-scanner": "security",
|
|
1282
|
+
"security-reviewer": "security",
|
|
1283
|
+
"bug-hunter": "debugging",
|
|
1284
|
+
debugger: "debugging",
|
|
1285
|
+
planner: "planning",
|
|
1286
|
+
architect: "planning",
|
|
1287
|
+
"refactor-planner": "refactoring",
|
|
1288
|
+
refactor: "refactoring",
|
|
1289
|
+
test: "testing",
|
|
1290
|
+
e2e: "testing",
|
|
1291
|
+
document: "docs",
|
|
1292
|
+
simplifier: "docs",
|
|
1293
|
+
"code-reviewer": "review",
|
|
1294
|
+
critic: "review",
|
|
1295
|
+
"frontend": "frontend",
|
|
1296
|
+
"backend": "backend"
|
|
1297
|
+
};
|
|
1298
|
+
return roleMap[role] ?? "general";
|
|
1299
|
+
}
|
|
1300
|
+
/** Find the best available model for a task category. */
|
|
1301
|
+
findBestModel(category) {
|
|
1302
|
+
const scored = this.profiles.filter((p) => this.hasKey(p.provider)).map((p) => {
|
|
1303
|
+
let score = 50;
|
|
1304
|
+
if (p.bestFor.includes(category)) score += 40;
|
|
1305
|
+
if (p.avoidFor?.includes(category)) score -= 50;
|
|
1306
|
+
if (category === "lightweight" && p.costTier === "budget") score += 20;
|
|
1307
|
+
if (category === "planning" && p.costTier === "premium") score += 15;
|
|
1308
|
+
if (category === "planning" && p.speedTier === "slow") score += 10;
|
|
1309
|
+
return { profile: p, score };
|
|
1310
|
+
}).filter((s) => s.score > 0).sort((a, b) => b.score - a.score);
|
|
1311
|
+
if (scored.length === 0) return void 0;
|
|
1312
|
+
const best = scored[0]?.profile;
|
|
1313
|
+
if (!best) return void 0;
|
|
1314
|
+
const models = this.getProviderModels(best.provider);
|
|
1315
|
+
const match = models.find((m) => best.pattern.test(m)) ?? models[0];
|
|
1316
|
+
if (!match) return void 0;
|
|
1317
|
+
return { provider: best.provider, model: match };
|
|
1318
|
+
}
|
|
1319
|
+
/** Check if a provider has an API key configured. */
|
|
1320
|
+
hasKey(providerId) {
|
|
1321
|
+
const pc = this.config.providers?.[providerId];
|
|
1322
|
+
if (!pc) return providerId === this.config.provider;
|
|
1323
|
+
if (typeof pc.apiKey === "string" && pc.apiKey.length > 0) return true;
|
|
1324
|
+
if (Array.isArray(pc.apiKeys) && pc.apiKeys.some((k) => k?.apiKey)) return true;
|
|
1325
|
+
return false;
|
|
1326
|
+
}
|
|
1327
|
+
/** Get the configured model list for a provider. */
|
|
1328
|
+
getProviderModels(providerId) {
|
|
1329
|
+
const pc = this.config.providers?.[providerId];
|
|
1330
|
+
return pc?.models ?? [];
|
|
1331
|
+
}
|
|
1332
|
+
/** Record cost for a model. */
|
|
1333
|
+
recordCost(provider, model, cost, tokens) {
|
|
1334
|
+
const key = `${provider}/${model}`;
|
|
1335
|
+
const entry = this.costs.byModel[key] ?? { cost: 0, tokens: { input: 0, output: 0 }, calls: 0 };
|
|
1336
|
+
entry.cost += cost;
|
|
1337
|
+
if (tokens) {
|
|
1338
|
+
entry.tokens.input += tokens.input;
|
|
1339
|
+
entry.tokens.output += tokens.output;
|
|
1340
|
+
}
|
|
1341
|
+
entry.calls += 1;
|
|
1342
|
+
this.costs.byModel[key] = entry;
|
|
1343
|
+
this.costs.totalCost += cost;
|
|
1344
|
+
}
|
|
1345
|
+
/** Get cumulative costs. */
|
|
1346
|
+
getCosts() {
|
|
1347
|
+
return { ...this.costs, byModel: { ...this.costs.byModel } };
|
|
1348
|
+
}
|
|
1349
|
+
/** Reset cost tracking. */
|
|
1350
|
+
resetCosts() {
|
|
1351
|
+
this.costs = { byModel: {}, totalCost: 0 };
|
|
1352
|
+
}
|
|
1353
|
+
/** List all available providers with their best model for each task category. */
|
|
1354
|
+
suggestMatrix() {
|
|
1355
|
+
const matrix = {};
|
|
1356
|
+
const roles = [
|
|
1357
|
+
"security-scanner",
|
|
1358
|
+
"bug-hunter",
|
|
1359
|
+
"planner",
|
|
1360
|
+
"architect",
|
|
1361
|
+
"refactor-planner",
|
|
1362
|
+
"test",
|
|
1363
|
+
"document",
|
|
1364
|
+
"code-reviewer",
|
|
1365
|
+
"executor",
|
|
1366
|
+
"debugger",
|
|
1367
|
+
"analyst"
|
|
1368
|
+
];
|
|
1369
|
+
for (const role of roles) {
|
|
1370
|
+
if (this.matrix[role]) continue;
|
|
1371
|
+
const pick = this.pickForTask(role, role);
|
|
1372
|
+
if (!pick.fromMatrix) {
|
|
1373
|
+
matrix[role] = pick;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
return matrix;
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
export { DefaultModeStore, DefaultModelsRegistry, LLMSelector, MODEL_PROFILES, ModelRouter, TASK_TO_ROLE, classifyFamily, findModelProfile, inferTaskType, loadProjectModes, loadUserModes, scoreModelForTask };
|
|
926
1381
|
//# sourceMappingURL=index.js.map
|
|
927
1382
|
//# sourceMappingURL=index.js.map
|