@oeronteros-1/opencode-orchestra 0.5.2 → 1.0.1
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 +65 -4
- package/GITHUB_RELEASE_1.0.0.md +84 -0
- package/README.md +95 -7
- package/dashboard-dist/assets/index-CN3CG5oG.js +124 -0
- package/dashboard-dist/assets/{index-89Gs8jun.css → index-j0b0ZK2D.css} +1 -1
- package/dashboard-dist/index.html +2 -2
- package/dist/agents/workers.d.ts +14 -1
- package/dist/agents/workers.js +10 -0
- package/dist/agents/workers.js.map +1 -1
- package/dist/cli.js +93 -9
- package/dist/cli.js.map +1 -1
- package/dist/config/schema.d.ts +23 -0
- package/dist/config/schema.js +18 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/dashboard/server.d.ts +11 -0
- package/dist/dashboard/server.js +152 -33
- package/dist/dashboard/server.js.map +1 -1
- package/dist/diagnostics/completion.d.ts +5 -0
- package/dist/diagnostics/completion.js +145 -0
- package/dist/diagnostics/completion.js.map +1 -0
- package/dist/diagnostics/doctor.d.ts +58 -0
- package/dist/diagnostics/doctor.js +354 -0
- package/dist/diagnostics/doctor.js.map +1 -0
- package/dist/diagnostics/update.d.ts +17 -0
- package/dist/diagnostics/update.js +89 -0
- package/dist/diagnostics/update.js.map +1 -0
- package/dist/index.js +141 -2
- package/dist/index.js.map +1 -1
- package/dist/plugin-status.d.ts +34 -0
- package/dist/plugin-status.js +90 -0
- package/dist/plugin-status.js.map +1 -0
- package/dist/routing/budget-guard.d.ts +36 -0
- package/dist/routing/budget-guard.js +52 -0
- package/dist/routing/budget-guard.js.map +1 -0
- package/dist/routing/classifier-cache.d.ts +25 -0
- package/dist/routing/classifier-cache.js +46 -0
- package/dist/routing/classifier-cache.js.map +1 -0
- package/dist/routing/fallback.d.ts +41 -0
- package/dist/routing/fallback.js +81 -0
- package/dist/routing/fallback.js.map +1 -0
- package/dist/routing/model-discovery.js +3 -1
- package/dist/routing/model-discovery.js.map +1 -1
- package/dist/routing/model-resolver.d.ts +11 -0
- package/dist/routing/model-resolver.js +27 -7
- package/dist/routing/model-resolver.js.map +1 -1
- package/dist/routing/observer.d.ts +22 -0
- package/dist/routing/observer.js +64 -0
- package/dist/routing/observer.js.map +1 -0
- package/dist/routing/planner.d.ts +24 -0
- package/dist/routing/planner.js +106 -0
- package/dist/routing/planner.js.map +1 -0
- package/dist/routing/pricing/estimate.d.ts +60 -0
- package/dist/routing/pricing/estimate.js +81 -0
- package/dist/routing/pricing/estimate.js.map +1 -0
- package/dist/routing/pricing/prices.d.ts +19 -0
- package/dist/routing/pricing/prices.js +74 -0
- package/dist/routing/pricing/prices.js.map +1 -0
- package/dist/routing/pricing/refresh.d.ts +30 -0
- package/dist/routing/pricing/refresh.js +105 -0
- package/dist/routing/pricing/refresh.js.map +1 -0
- package/dist/telemetry/analytics.d.ts +45 -0
- package/dist/telemetry/analytics.js +78 -0
- package/dist/telemetry/analytics.js.map +1 -0
- package/dist/telemetry/ledger.d.ts +13 -1
- package/dist/telemetry/ledger.js +29 -1
- package/dist/telemetry/ledger.js.map +1 -1
- package/dist/tools.d.ts +6 -1
- package/dist/tools.js +77 -7
- package/dist/tools.js.map +1 -1
- package/package.json +17 -4
- package/schema/opencode-orchestra.schema.json +15 -2
- package/dashboard-dist/assets/index-Bhe8OPZD.js +0 -124
package/dist/index.js
CHANGED
|
@@ -6,6 +6,9 @@ import { applyDiscoveredModels, discoverConnectedModels } from "./routing/model-
|
|
|
6
6
|
import { primarySystemHint } from "./superpowers/compatibility.js";
|
|
7
7
|
import { Ledger } from "./telemetry/ledger.js";
|
|
8
8
|
import { createOrchestraTools } from "./tools.js";
|
|
9
|
+
import { createStreamObserver } from "./routing/observer.js";
|
|
10
|
+
import { createPriceRefresher } from "./routing/pricing/refresh.js";
|
|
11
|
+
import { detectMcpPresence, resolvePluginVersion, PACKAGE_NAME } from "./plugin-status.js";
|
|
9
12
|
function mergeAgent(base, override) {
|
|
10
13
|
if (!override)
|
|
11
14
|
return base;
|
|
@@ -18,6 +21,74 @@ function mergeAgent(base, override) {
|
|
|
18
21
|
},
|
|
19
22
|
};
|
|
20
23
|
}
|
|
24
|
+
// Stream observers keyed by message part id. These let us flag low-confidence
|
|
25
|
+
// or self-correcting output *while a worker is still generating*, before any
|
|
26
|
+
// finalized answer exists, so escalation can fire early instead of post-hoc.
|
|
27
|
+
const streamObservers = new Map();
|
|
28
|
+
const flaggedParts = new Set();
|
|
29
|
+
const STREAM_CONFIDENCE_THRESHOLD = 0.6;
|
|
30
|
+
// Opt-in reply text and prompt text accumulation, keyed by message id. Only
|
|
31
|
+
// populated when `telemetry.storeTexts` is enabled; otherwise dropped.
|
|
32
|
+
const replyBuffers = new Map();
|
|
33
|
+
// A chat.message prompt precedes its assistant message and has a different id,
|
|
34
|
+
// so prompts are correlated by session rather than by assistant message id.
|
|
35
|
+
const promptBuffers = new Map();
|
|
36
|
+
const MAX_TEXT_BUFFERS = 512;
|
|
37
|
+
let storeTextsFlag = false;
|
|
38
|
+
function pruneOldest(map) {
|
|
39
|
+
while (map.size > MAX_TEXT_BUFFERS) {
|
|
40
|
+
const oldest = map.keys().next().value;
|
|
41
|
+
if (oldest === undefined)
|
|
42
|
+
break;
|
|
43
|
+
map.delete(oldest);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function trackStreamDelta(sessionID, part, delta) {
|
|
47
|
+
if (!delta)
|
|
48
|
+
return;
|
|
49
|
+
const partID = part.id;
|
|
50
|
+
let observer = streamObservers.get(partID);
|
|
51
|
+
if (!observer) {
|
|
52
|
+
observer = createStreamObserver({ threshold: STREAM_CONFIDENCE_THRESHOLD });
|
|
53
|
+
streamObservers.set(partID, observer);
|
|
54
|
+
}
|
|
55
|
+
const observation = observer.push(delta);
|
|
56
|
+
if (observation.lowConfidence && !flaggedParts.has(partID)) {
|
|
57
|
+
flaggedParts.add(partID);
|
|
58
|
+
void logStreamFlag(sessionID, partID, observation).catch(() => undefined);
|
|
59
|
+
}
|
|
60
|
+
if (storeTextsFlag) {
|
|
61
|
+
replyBuffers.set(part.messageID, `${replyBuffers.get(part.messageID) ?? ""}${delta}`);
|
|
62
|
+
pruneOldest(replyBuffers);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function endStream(messageID) {
|
|
66
|
+
// A finalized message no longer needs an accumulated reply after its event
|
|
67
|
+
// handler has consumed it. Keep the id parameter explicit for lifecycle use.
|
|
68
|
+
if (!storeTextsFlag)
|
|
69
|
+
replyBuffers.delete(messageID);
|
|
70
|
+
// Observers are pruned lazily by size to bound memory over a long session.
|
|
71
|
+
if (streamObservers.size > 512) {
|
|
72
|
+
const overflow = streamObservers.size - 512;
|
|
73
|
+
let removed = 0;
|
|
74
|
+
for (const key of streamObservers.keys()) {
|
|
75
|
+
if (removed >= overflow)
|
|
76
|
+
break;
|
|
77
|
+
streamObservers.delete(key);
|
|
78
|
+
flaggedParts.delete(key);
|
|
79
|
+
removed += 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Stub logger: replaced when the plugin body captures `client.app.log`.
|
|
84
|
+
let streamLog = () => Promise.resolve();
|
|
85
|
+
function logStreamFlag(sessionID, partID, observation) {
|
|
86
|
+
return streamLog(sessionID, "orchestra stream observer flagged low-confidence output", {
|
|
87
|
+
partID,
|
|
88
|
+
confidence: observation.confidence,
|
|
89
|
+
flags: observation.flags,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
21
92
|
export const OrchestraPlugin = async ({ client, directory }, rawOptions = {}) => {
|
|
22
93
|
const loaded = await loadConfig(directory, rawOptions);
|
|
23
94
|
const discovered = await discoverConnectedModels(client);
|
|
@@ -29,8 +100,30 @@ export const OrchestraPlugin = async ({ client, directory }, rawOptions = {}) =>
|
|
|
29
100
|
...Object.values(orchestra.models.worker),
|
|
30
101
|
orchestra.models.judge,
|
|
31
102
|
];
|
|
32
|
-
const ledger = new Ledger(directory, orchestra.telemetry.directory, orchestra.telemetry.enabled, pools);
|
|
103
|
+
const ledger = new Ledger(directory, orchestra.telemetry.directory, orchestra.telemetry.enabled, pools, orchestra.telemetry.storeTexts);
|
|
104
|
+
storeTextsFlag = orchestra.telemetry.storeTexts;
|
|
105
|
+
const refreshSource = orchestra.pricing.endpoint
|
|
106
|
+
? { endpoint: orchestra.pricing.endpoint, refreshIntervalHours: orchestra.pricing.refreshIntervalHours }
|
|
107
|
+
: undefined;
|
|
108
|
+
const priceRefresher = createPriceRefresher(undefined, refreshSource);
|
|
109
|
+
priceRefresher.start();
|
|
33
110
|
const systemHint = primarySystemHint(orchestra);
|
|
111
|
+
const pluginStatus = {
|
|
112
|
+
name: PACKAGE_NAME,
|
|
113
|
+
version: await resolvePluginVersion(),
|
|
114
|
+
budget: orchestra.budget,
|
|
115
|
+
modelStrategy: orchestra.models.strategy,
|
|
116
|
+
configuredModels: pools.flat().length,
|
|
117
|
+
discoveredModels: discovered.length,
|
|
118
|
+
configSource: loaded.source ?? "plugin options/defaults",
|
|
119
|
+
mcp: await detectMcpPresence(),
|
|
120
|
+
};
|
|
121
|
+
streamLog = (sessionID, message, extra) => client.app
|
|
122
|
+
.log({
|
|
123
|
+
body: { service: "opencode-orchestra", level: "warn", message, extra: { sessionID, ...extra } },
|
|
124
|
+
})
|
|
125
|
+
.then(() => undefined)
|
|
126
|
+
.catch(() => undefined);
|
|
34
127
|
await client.app
|
|
35
128
|
.log({
|
|
36
129
|
body: {
|
|
@@ -59,19 +152,65 @@ export const OrchestraPlugin = async ({ client, directory }, rawOptions = {}) =>
|
|
|
59
152
|
description: "Show OpenCode Orchestra usage and escalation status",
|
|
60
153
|
template: "Call the orchestra_status tool and present its result verbatim.",
|
|
61
154
|
};
|
|
155
|
+
mutable.command["plugin-status"] ??= {
|
|
156
|
+
description: "Show the OpenCode Orchestra plugin's own runtime status",
|
|
157
|
+
template: "Call the orchestra_plugin_status tool and present its result verbatim.",
|
|
158
|
+
};
|
|
62
159
|
mutable.command.orchestra ??= {
|
|
63
160
|
description: "Classify a task and delegate it to orch-lead",
|
|
64
161
|
template: "Call orchestra_route for this task: $ARGUMENTS. Then delegate the full task once to orch-lead using the returned profile and constraints.",
|
|
65
162
|
};
|
|
66
163
|
},
|
|
67
|
-
tool: createOrchestraTools(orchestra, ledger
|
|
164
|
+
tool: createOrchestraTools(orchestra, ledger, pluginStatus, {
|
|
165
|
+
get snapshot() { return priceRefresher.snapshot; },
|
|
166
|
+
}),
|
|
167
|
+
dispose: async () => {
|
|
168
|
+
priceRefresher.stop();
|
|
169
|
+
promptBuffers.clear();
|
|
170
|
+
replyBuffers.clear();
|
|
171
|
+
streamObservers.clear();
|
|
172
|
+
flaggedParts.clear();
|
|
173
|
+
},
|
|
174
|
+
"chat.message": async ({ sessionID }, output) => {
|
|
175
|
+
if (!storeTextsFlag || !sessionID)
|
|
176
|
+
return;
|
|
177
|
+
const text = output.parts
|
|
178
|
+
.filter((part) => part.type === "text")
|
|
179
|
+
.map((part) => part.text)
|
|
180
|
+
.join("")
|
|
181
|
+
.trim();
|
|
182
|
+
if (text) {
|
|
183
|
+
promptBuffers.set(sessionID, text);
|
|
184
|
+
pruneOldest(promptBuffers);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
68
187
|
event: async ({ event }) => {
|
|
188
|
+
if (event.type === "message.part.updated") {
|
|
189
|
+
const part = event.properties.part;
|
|
190
|
+
trackStreamDelta(part.sessionID, part, event.properties.delta ?? "");
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
69
193
|
if (event.type !== "message.updated")
|
|
70
194
|
return;
|
|
71
195
|
const info = event.properties.info;
|
|
72
196
|
if (info.role !== "assistant")
|
|
73
197
|
return;
|
|
198
|
+
endStream(info.id);
|
|
74
199
|
await ledger.recordAssistant(info);
|
|
200
|
+
if (storeTextsFlag) {
|
|
201
|
+
const prompt = promptBuffers.get(info.sessionID);
|
|
202
|
+
const reply = replyBuffers.get(info.id);
|
|
203
|
+
promptBuffers.delete(info.sessionID);
|
|
204
|
+
replyBuffers.delete(info.id);
|
|
205
|
+
if (prompt !== undefined || reply !== undefined) {
|
|
206
|
+
const text = {};
|
|
207
|
+
if (prompt !== undefined)
|
|
208
|
+
text.prompt = prompt;
|
|
209
|
+
if (reply !== undefined)
|
|
210
|
+
text.reply = reply;
|
|
211
|
+
await ledger.recordText(info.sessionID, info.id, text);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
75
214
|
},
|
|
76
215
|
...(systemHint
|
|
77
216
|
? {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAExD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAC7F,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAExD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAC7F,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,EAAE,oBAAoB,EAAuB,MAAM,uBAAuB,CAAA;AACjF,OAAO,EAAE,oBAAoB,EAAsB,MAAM,8BAA8B,CAAA;AACvF,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,YAAY,EAAqB,MAAM,oBAAoB,CAAA;AAO7G,SAAS,UAAU,CAAC,IAAwB,EAAE,QAA6B;IACzE,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC1B,OAAO;QACL,GAAG,IAAI;QACP,GAAG,QAAQ;QACX,UAAU,EAAE;YACV,GAAG,IAAI,CAAC,UAAU;YAClB,GAAG,QAAQ,CAAC,UAAU;SACvB;KACF,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,MAAM,eAAe,GAAG,IAAI,GAAG,EAA0B,CAAA;AACzD,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAA;AACtC,MAAM,2BAA2B,GAAG,GAAG,CAAA;AAEvC,4EAA4E;AAC5E,uEAAuE;AACvE,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAA;AAC9C,+EAA+E;AAC/E,4EAA4E;AAC5E,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAA;AAC/C,MAAM,gBAAgB,GAAG,GAAG,CAAA;AAC5B,IAAI,cAAc,GAAG,KAAK,CAAA;AAE1B,SAAS,WAAW,CAAC,GAAwB;IAC3C,OAAO,GAAG,CAAC,IAAI,GAAG,gBAAgB,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAA;QACtC,IAAI,MAAM,KAAK,SAAS;YAAE,MAAK;QAC/B,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACpB,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,SAAiB,EAAE,IAAuC,EAAE,KAAa;IACjG,IAAI,CAAC,KAAK;QAAE,OAAM;IAClB,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;IACtB,IAAI,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,oBAAoB,CAAC,EAAE,SAAS,EAAE,2BAA2B,EAAE,CAAC,CAAA;QAC3E,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACvC,CAAC;IACD,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACxC,IAAI,WAAW,CAAC,aAAa,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3D,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACxB,KAAK,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAC3E,CAAC;IACD,IAAI,cAAc,EAAE,CAAC;QACnB,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC,CAAA;QACrF,WAAW,CAAC,YAAY,CAAC,CAAA;IAC3B,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,SAAiB;IAClC,2EAA2E;IAC3E,6EAA6E;IAC7E,IAAI,CAAC,cAAc;QAAE,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;IACnD,2EAA2E;IAC3E,IAAI,eAAe,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,GAAG,GAAG,CAAA;QAC3C,IAAI,OAAO,GAAG,CAAC,CAAA;QACf,KAAK,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC;YACzC,IAAI,OAAO,IAAI,QAAQ;gBAAE,MAAK;YAC9B,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAC3B,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACxB,OAAO,IAAI,CAAC,CAAA;QACd,CAAC;IACH,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,IAAI,SAAS,GAA0E,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;AAE9G,SAAS,aAAa,CAAC,SAAiB,EAAE,MAAc,EAAE,WAAoD;IAC5G,OAAO,SAAS,CAAC,SAAS,EAAE,yDAAyD,EAAE;QACrF,MAAM;QACN,UAAU,EAAE,WAAW,CAAC,UAAU;QAClC,KAAK,EAAE,WAAW,CAAC,KAAK;KACzB,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAW,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,EAAE,EAAE,EAAE;IACtF,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;IACtD,MAAM,UAAU,GAAG,MAAM,uBAAuB,CAAC,MAAM,CAAC,CAAA;IACxD,MAAM,SAAS,GAAG,qBAAqB,CAAC,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAA;IACrF,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAA;IACnC,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IACjD,MAAM,KAAK,GAA4B;QACrC,SAAS,CAAC,MAAM,CAAC,IAAI;QACrB,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC;QACzC,SAAS,CAAC,MAAM,CAAC,KAAK;KACvB,CAAA;IACD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,CAAA;IACvI,cAAc,GAAG,SAAS,CAAC,SAAS,CAAC,UAAU,CAAA;IAC/C,MAAM,aAAa,GAA8B,SAAS,CAAC,OAAO,CAAC,QAAQ;QACzE,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,oBAAoB,EAAE,SAAS,CAAC,OAAO,CAAC,oBAAoB,EAAE;QACxG,CAAC,CAAC,SAAS,CAAA;IACb,MAAM,cAAc,GAAG,oBAAoB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;IACrE,cAAc,CAAC,KAAK,EAAE,CAAA;IACtB,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAA;IAC/C,MAAM,YAAY,GAAiB;QACjC,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,MAAM,oBAAoB,EAAE;QACrC,MAAM,EAAE,SAAS,CAAC,MAAM;QACxB,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC,QAAQ;QACxC,gBAAgB,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM;QACrC,gBAAgB,EAAE,UAAU,CAAC,MAAM;QACnC,YAAY,EAAE,MAAM,CAAC,MAAM,IAAI,yBAAyB;QACxD,GAAG,EAAE,MAAM,iBAAiB,EAAE;KAC/B,CAAA;IAED,SAAS,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CACxC,MAAM,CAAC,GAAG;SACP,GAAG,CAAC;QACH,IAAI,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,GAAI,KAAgB,EAAE,EAAE;KAC5G,CAAC;SACD,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;SACrB,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAE3B,MAAM,MAAM,CAAC,GAAG;SACb,GAAG,CAAC;QACH,IAAI,EAAE;YACJ,OAAO,EAAE,oBAAoB;YAC7B,KAAK,EAAE,MAAM;YACb,OAAO,EAAE,gCAAgC;YACzC,KAAK,EAAE;gBACL,MAAM,EAAE,SAAS,CAAC,MAAM;gBACxB,YAAY,EAAE,MAAM,CAAC,MAAM,IAAI,yBAAyB;gBACxD,gBAAgB,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM;gBACrC,gBAAgB,EAAE,UAAU,CAAC,MAAM;gBACnC,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC,QAAQ;aACzC;SACF;KACF,CAAC;SACD,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAEzB,OAAO;QACL,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACtB,MAAM,OAAO,GAAG,KAAiC,CAAA;YACjD,OAAO,CAAC,KAAK,KAAK,EAAE,CAAA;YACpB,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACnD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;YAC9D,CAAC;YACD,OAAO,CAAC,OAAO,KAAK,EAAE,CAAA;YACtB,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK;gBACtC,WAAW,EAAE,qDAAqD;gBAClE,QAAQ,EAAE,iEAAiE;aAC5E,CAAA;YACD,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK;gBACnC,WAAW,EAAE,yDAAyD;gBACtE,QAAQ,EAAE,wEAAwE;aACnF,CAAA;YACD,OAAO,CAAC,OAAO,CAAC,SAAS,KAAK;gBAC5B,WAAW,EAAE,8CAA8C;gBAC3D,QAAQ,EAAE,2IAA2I;aACtJ,CAAA;QACH,CAAC;QACD,IAAI,EAAE,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE;YAC1D,IAAI,QAAQ,KAAK,OAAO,cAAc,CAAC,QAAQ,CAAA,CAAC,CAAC;SAClD,CAAC;QACF,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,cAAc,CAAC,IAAI,EAAE,CAAA;YACrB,aAAa,CAAC,KAAK,EAAE,CAAA;YACrB,YAAY,CAAC,KAAK,EAAE,CAAA;YACpB,eAAe,CAAC,KAAK,EAAE,CAAA;YACvB,YAAY,CAAC,KAAK,EAAE,CAAA;QACtB,CAAC;QACD,cAAc,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE;YAC9C,IAAI,CAAC,cAAc,IAAI,CAAC,SAAS;gBAAE,OAAM;YACzC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK;iBACtB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;iBACtC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;iBACxB,IAAI,CAAC,EAAE,CAAC;iBACR,IAAI,EAAE,CAAA;YACT,IAAI,IAAI,EAAE,CAAC;gBACT,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;gBAClC,WAAW,CAAC,aAAa,CAAC,CAAA;YAC5B,CAAC;QACH,CAAC;QACD,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YACzB,IAAI,KAAK,CAAC,IAAI,KAAK,sBAAsB,EAAE,CAAC;gBAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAA;gBAClC,gBAAgB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;gBACpE,OAAM;YACR,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB;gBAAE,OAAM;YAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAA;YAClC,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;gBAAE,OAAM;YACrC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAClB,MAAM,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;YAClC,IAAI,cAAc,EAAE,CAAC;gBACnB,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;gBAChD,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;gBACvC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;gBACpC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;gBAC5B,IAAI,MAAM,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBAChD,MAAM,IAAI,GAAwC,EAAE,CAAA;oBACpD,IAAI,MAAM,KAAK,SAAS;wBAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;oBAC9C,IAAI,KAAK,KAAK,SAAS;wBAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;oBAC3C,MAAM,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAA;gBACxD,CAAC;YACH,CAAC;QACH,CAAC;QACD,GAAG,CAAC,UAAU;YACZ,CAAC,CAAC;gBACE,oCAAoC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;oBAC7D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBAChC,CAAC;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAA;AACH,CAAC,CAAA;AAED,qEAAqE;AACrE,mEAAmE;AACnE,uEAAuE;AACvE,MAAM,CAAC,MAAM,MAAM,GAAW,eAAe,CAAA;AAC7C,eAAe,eAAe,CAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const PACKAGE_NAME = "@oeronteros-1/opencode-orchestra";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the installed package version from `package.json`. The compiled
|
|
4
|
+
* output lives in `dist/`, so the package root is one level up. Falls back to
|
|
5
|
+
* "unknown" when the manifest cannot be read (e.g. running from an unpacked
|
|
6
|
+
* source tree without a package.json next to the dist folder).
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolvePluginVersion(): Promise<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Snapshot of the plugin's own runtime state. Unlike `orchestra_status` (which
|
|
11
|
+
* reports per-session usage/escalation telemetry), this reports the plugin
|
|
12
|
+
* itself: what version is loaded, which budget/strategy are active, where the
|
|
13
|
+
* config came from, how many models were discovered, and which companion MCPs
|
|
14
|
+
* are present in the OpenCode configuration.
|
|
15
|
+
*/
|
|
16
|
+
export interface PluginStatus {
|
|
17
|
+
name: string;
|
|
18
|
+
version: string;
|
|
19
|
+
budget: string;
|
|
20
|
+
modelStrategy: string;
|
|
21
|
+
configuredModels: number;
|
|
22
|
+
discoveredModels: number;
|
|
23
|
+
configSource: string;
|
|
24
|
+
mcp: Record<string, boolean>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Detect which companion MCPs are configured in the OpenCode main config.
|
|
28
|
+
* Mirrors the dashboard's `mcpStatus` helper without importing the HTTP server.
|
|
29
|
+
*/
|
|
30
|
+
export declare function detectMcpPresence(configDirectory?: string): Promise<Record<string, boolean>>;
|
|
31
|
+
/**
|
|
32
|
+
* Format the plugin status snapshot as a stable, human-readable report.
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatPluginStatus(status: PluginStatus): Promise<string>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse } from "jsonc-parser";
|
|
4
|
+
import { openCodeConfigDirectory } from "./config/paths.js";
|
|
5
|
+
export const PACKAGE_NAME = "@oeronteros-1/opencode-orchestra";
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the installed package version from `package.json`. The compiled
|
|
8
|
+
* output lives in `dist/`, so the package root is one level up. Falls back to
|
|
9
|
+
* "unknown" when the manifest cannot be read (e.g. running from an unpacked
|
|
10
|
+
* source tree without a package.json next to the dist folder).
|
|
11
|
+
*/
|
|
12
|
+
export async function resolvePluginVersion() {
|
|
13
|
+
try {
|
|
14
|
+
const here = new URL(".", import.meta.url);
|
|
15
|
+
const inline = new URL("../package.json", here);
|
|
16
|
+
const pkg = JSON.parse(await readFile(inline, "utf8"));
|
|
17
|
+
if (pkg.version)
|
|
18
|
+
return pkg.version;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Fall through to the unknown marker.
|
|
22
|
+
}
|
|
23
|
+
return "unknown";
|
|
24
|
+
}
|
|
25
|
+
async function readTextOr(file, fallback) {
|
|
26
|
+
try {
|
|
27
|
+
return await readFile(file, "utf8");
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return fallback;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function findMainConfig(configDirectory) {
|
|
34
|
+
for (const name of ["opencode.json", "opencode.jsonc"]) {
|
|
35
|
+
const candidate = path.join(configDirectory, name);
|
|
36
|
+
try {
|
|
37
|
+
await readFile(candidate, "utf8");
|
|
38
|
+
return candidate;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Try the next name.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return path.join(configDirectory, "opencode.json");
|
|
45
|
+
}
|
|
46
|
+
function parseJsonc(text) {
|
|
47
|
+
const value = parse(text, [], { allowTrailingComma: true, disallowComments: false });
|
|
48
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
49
|
+
? value
|
|
50
|
+
: {};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Detect which companion MCPs are configured in the OpenCode main config.
|
|
54
|
+
* Mirrors the dashboard's `mcpStatus` helper without importing the HTTP server.
|
|
55
|
+
*/
|
|
56
|
+
export async function detectMcpPresence(configDirectory = openCodeConfigDirectory()) {
|
|
57
|
+
const root = parseJsonc(await readTextOr(await findMainConfig(configDirectory), "{}"));
|
|
58
|
+
const mcp = typeof root.mcp === "object" && root.mcp !== null && !Array.isArray(root.mcp)
|
|
59
|
+
? root.mcp
|
|
60
|
+
: {};
|
|
61
|
+
return {
|
|
62
|
+
context7: "context7" in mcp,
|
|
63
|
+
codebaseMemory: "codebase-memory" in mcp,
|
|
64
|
+
memoryGraph: "memorygraph" in mcp,
|
|
65
|
+
supermemory: "supermemory" in mcp,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Format the plugin status snapshot as a stable, human-readable report.
|
|
70
|
+
*/
|
|
71
|
+
export async function formatPluginStatus(status) {
|
|
72
|
+
const mcp = Object.entries({ ...status.mcp })
|
|
73
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
74
|
+
.map(([name, present]) => ` ${name.padEnd(24)} ${present ? "connected" : "not configured"}`)
|
|
75
|
+
.join("\n");
|
|
76
|
+
return [
|
|
77
|
+
"OpenCode Orchestra plugin status",
|
|
78
|
+
"",
|
|
79
|
+
`plugin: ${status.name}@${status.version}`,
|
|
80
|
+
`budget: ${status.budget}`,
|
|
81
|
+
`model strategy: ${status.modelStrategy}`,
|
|
82
|
+
`configured models: ${status.configuredModels}`,
|
|
83
|
+
`discovered models: ${status.discoveredModels}`,
|
|
84
|
+
`config source: ${status.configSource}`,
|
|
85
|
+
"",
|
|
86
|
+
"MCP servers:",
|
|
87
|
+
mcp || " none detected",
|
|
88
|
+
].join("\n");
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=plugin-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-status.js","sourceRoot":"","sources":["../src/plugin-status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { BudgetMode, ModelCost } from "../config/schema.js";
|
|
2
|
+
/** How much paid-model spend a single work branch is allowed before we cut it. */
|
|
3
|
+
export interface PaidBudgetLimit {
|
|
4
|
+
/** Maximum paid-model calls a branch may make before termination. */
|
|
5
|
+
maxPaidCalls: number;
|
|
6
|
+
/** Soft warning threshold (fraction of maxPaidCalls). */
|
|
7
|
+
warnAt: number;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Derive a paid-call budget for a branch of work from the global budget mode.
|
|
12
|
+
* eco/balanced keep it tight (no or few paid calls); quality/ebobo open it up.
|
|
13
|
+
*
|
|
14
|
+
* "Early termination" here is budget-aware: a branch is stopped as soon as it
|
|
15
|
+
* has burned its paid-call allowance, so paid models never silently overrun.
|
|
16
|
+
*/
|
|
17
|
+
export declare function paidBudgetFor(mode: BudgetMode, explicit?: Partial<PaidBudgetLimit>): PaidBudgetLimit;
|
|
18
|
+
export interface BudgetSnapshot {
|
|
19
|
+
paidCallsUsed: number;
|
|
20
|
+
remaining: number;
|
|
21
|
+
terminated: boolean;
|
|
22
|
+
warning: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface BudgetGuard {
|
|
25
|
+
/** Record a paid call and report whether the branch should stop. */
|
|
26
|
+
recordPaidCall(cost: ModelCost): BudgetSnapshot;
|
|
27
|
+
/** Current remaining paid-call allowance. */
|
|
28
|
+
remaining(): number;
|
|
29
|
+
reset(): void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Guard a single work branch. Only the costs of a *paid* model advance the
|
|
33
|
+
* counter; free/subscription calls are free passes (mode already prices them
|
|
34
|
+
* in elsewhere). Once paidCallsUsed reaches the cap the branch is terminated.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createBudgetGuard(limit: PaidBudgetLimit): BudgetGuard;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive a paid-call budget for a branch of work from the global budget mode.
|
|
3
|
+
* eco/balanced keep it tight (no or few paid calls); quality/ebobo open it up.
|
|
4
|
+
*
|
|
5
|
+
* "Early termination" here is budget-aware: a branch is stopped as soon as it
|
|
6
|
+
* has burned its paid-call allowance, so paid models never silently overrun.
|
|
7
|
+
*/
|
|
8
|
+
export function paidBudgetFor(mode, explicit) {
|
|
9
|
+
const base = {
|
|
10
|
+
eco: { maxPaidCalls: 0, warnAt: 0, enabled: false },
|
|
11
|
+
balanced: { maxPaidCalls: 1, warnAt: 1, enabled: true },
|
|
12
|
+
quality: { maxPaidCalls: 6, warnAt: 0.7, enabled: true },
|
|
13
|
+
ebobo: { maxPaidCalls: 12, warnAt: 0.75, enabled: true },
|
|
14
|
+
};
|
|
15
|
+
const defaults = base[mode];
|
|
16
|
+
return {
|
|
17
|
+
...defaults,
|
|
18
|
+
...explicit,
|
|
19
|
+
enabled: (explicit?.enabled ?? defaults.enabled) && (explicit?.maxPaidCalls ?? defaults.maxPaidCalls) >= 0,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Guard a single work branch. Only the costs of a *paid* model advance the
|
|
24
|
+
* counter; free/subscription calls are free passes (mode already prices them
|
|
25
|
+
* in elsewhere). Once paidCallsUsed reaches the cap the branch is terminated.
|
|
26
|
+
*/
|
|
27
|
+
export function createBudgetGuard(limit) {
|
|
28
|
+
let paidCallsUsed = 0;
|
|
29
|
+
const cap = limit.maxPaidCalls;
|
|
30
|
+
const warnAt = Math.round(limit.warnAt * cap);
|
|
31
|
+
const snapshot = () => {
|
|
32
|
+
const remaining = Math.max(0, cap - paidCallsUsed);
|
|
33
|
+
return {
|
|
34
|
+
paidCallsUsed,
|
|
35
|
+
remaining,
|
|
36
|
+
terminated: cap > 0 && paidCallsUsed >= cap,
|
|
37
|
+
warning: !limit.enabled ? false : cap > 0 && paidCallsUsed >= warnAt && paidCallsUsed < cap,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
recordPaidCall(cost) {
|
|
42
|
+
if (cost === "paid")
|
|
43
|
+
paidCallsUsed += 1;
|
|
44
|
+
return snapshot();
|
|
45
|
+
},
|
|
46
|
+
remaining: () => Math.max(0, cap - paidCallsUsed),
|
|
47
|
+
reset() {
|
|
48
|
+
paidCallsUsed = 0;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=budget-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget-guard.js","sourceRoot":"","sources":["../../src/routing/budget-guard.ts"],"names":[],"mappings":"AAWA;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAgB,EAAE,QAAmC;IACjF,MAAM,IAAI,GAAgF;QACxF,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE;QACnD,QAAQ,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE;QACvD,OAAO,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;QACxD,KAAK,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;KACzD,CAAA;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3B,OAAO;QACL,GAAG,QAAQ;QACX,GAAG,QAAQ;QACX,OAAO,EAAE,CAAC,QAAQ,EAAE,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,IAAI,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC;KAC3G,CAAA;AACH,CAAC;AAiBD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAsB;IACtD,IAAI,aAAa,GAAG,CAAC,CAAA;IACrB,MAAM,GAAG,GAAG,KAAK,CAAC,YAAY,CAAA;IAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAA;IAE7C,MAAM,QAAQ,GAAG,GAAmB,EAAE;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,aAAa,CAAC,CAAA;QAClD,OAAO;YACL,aAAa;YACb,SAAS;YACT,UAAU,EAAE,GAAG,GAAG,CAAC,IAAI,aAAa,IAAI,GAAG;YAC3C,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,aAAa,IAAI,MAAM,IAAI,aAAa,GAAG,GAAG;SAC5F,CAAA;IACH,CAAC,CAAA;IAED,OAAO;QACL,cAAc,CAAC,IAAI;YACjB,IAAI,IAAI,KAAK,MAAM;gBAAE,aAAa,IAAI,CAAC,CAAA;YACvC,OAAO,QAAQ,EAAE,CAAA;QACnB,CAAC;QACD,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,aAAa,CAAC;QACjD,KAAK;YACH,aAAa,GAAG,CAAC,CAAA;QACnB,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Classification } from "./classifier.js";
|
|
2
|
+
/**
|
|
3
|
+
* Memoized classification cache: near-duplicate tasks reuse a previous
|
|
4
|
+
* classification instead of re-running the (judge-token-burning) classifier.
|
|
5
|
+
* Content-addressed by a normalized fingerprint and LRU-bounded in process.
|
|
6
|
+
*/
|
|
7
|
+
export interface CacheEntry {
|
|
8
|
+
key: string;
|
|
9
|
+
classification: Classification;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
hits: number;
|
|
12
|
+
lastHit: number;
|
|
13
|
+
}
|
|
14
|
+
export interface ClassifierCacheOptions {
|
|
15
|
+
maxEntries?: number;
|
|
16
|
+
}
|
|
17
|
+
/** Normalize a task into a stable fingerprint (case/punct/whitespace folding). */
|
|
18
|
+
export declare function fingerprint(task: string): string;
|
|
19
|
+
export declare function createClassifierCache(options?: ClassifierCacheOptions): {
|
|
20
|
+
get(task: string): Classification | undefined;
|
|
21
|
+
set(task: string, classification: Classification): void;
|
|
22
|
+
clear(): void;
|
|
23
|
+
size: () => number;
|
|
24
|
+
};
|
|
25
|
+
export type ClassifierCache = ReturnType<typeof createClassifierCache>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Normalize a task into a stable fingerprint (case/punct/whitespace folding). */
|
|
2
|
+
export function fingerprint(task) {
|
|
3
|
+
return task
|
|
4
|
+
.toLowerCase()
|
|
5
|
+
.normalize("NFKC")
|
|
6
|
+
.replace(/[^\p{L}\p{N}\s]/gu, " ")
|
|
7
|
+
.replace(/\s+/g, " ")
|
|
8
|
+
.trim();
|
|
9
|
+
}
|
|
10
|
+
export function createClassifierCache(options = {}) {
|
|
11
|
+
const maxEntries = options.maxEntries ?? 256;
|
|
12
|
+
const map = new Map();
|
|
13
|
+
function touch(entry) {
|
|
14
|
+
map.delete(entry.key);
|
|
15
|
+
map.set(entry.key, entry);
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
get(task) {
|
|
19
|
+
const key = fingerprint(task);
|
|
20
|
+
const entry = map.get(key);
|
|
21
|
+
if (!entry)
|
|
22
|
+
return undefined;
|
|
23
|
+
entry.hits += 1;
|
|
24
|
+
entry.lastHit = Date.now();
|
|
25
|
+
touch(entry);
|
|
26
|
+
return entry.classification;
|
|
27
|
+
},
|
|
28
|
+
set(task, classification) {
|
|
29
|
+
const key = fingerprint(task);
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
const entry = { key, classification, createdAt: now, hits: 0, lastHit: now };
|
|
32
|
+
map.set(key, entry);
|
|
33
|
+
while (map.size > maxEntries) {
|
|
34
|
+
const oldest = map.keys().next().value;
|
|
35
|
+
if (oldest === undefined)
|
|
36
|
+
break;
|
|
37
|
+
map.delete(oldest);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
clear() {
|
|
41
|
+
map.clear();
|
|
42
|
+
},
|
|
43
|
+
size: () => map.size,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=classifier-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifier-cache.js","sourceRoot":"","sources":["../../src/routing/classifier-cache.ts"],"names":[],"mappings":"AAoBA,kFAAkF;AAClF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC;SACjC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAA;AACX,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,UAAkC,EAAE;IACxE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,GAAG,CAAA;IAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAsB,CAAA;IAEzC,SAAS,KAAK,CAAC,KAAiB;QAC9B,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACrB,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC3B,CAAC;IAED,OAAO;QACL,GAAG,CAAC,IAAY;YACd,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;YAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC1B,IAAI,CAAC,KAAK;gBAAE,OAAO,SAAS,CAAA;YAC5B,KAAK,CAAC,IAAI,IAAI,CAAC,CAAA;YACf,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YAC1B,KAAK,CAAC,KAAK,CAAC,CAAA;YACZ,OAAO,KAAK,CAAC,cAAc,CAAA;QAC7B,CAAC;QACD,GAAG,CAAC,IAAY,EAAE,cAA8B;YAC9C,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;YAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACtB,MAAM,KAAK,GAAe,EAAE,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAA;YACxF,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YACnB,OAAO,GAAG,CAAC,IAAI,GAAG,UAAU,EAAE,CAAC;gBAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAA;gBACtC,IAAI,MAAM,KAAK,SAAS;oBAAE,MAAK;gBAC/B,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YACpB,CAAC;QACH,CAAC;QACD,KAAK;YACH,GAAG,CAAC,KAAK,EAAE,CAAA;QACb,CAAC;QACD,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI;KACrB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { BudgetMode, ModelCandidateInput, ModelCost } from "../config/schema.js";
|
|
2
|
+
import { resolveModel } from "./model-resolver.js";
|
|
3
|
+
export interface FallbackEntry {
|
|
4
|
+
id: string;
|
|
5
|
+
cost: ModelCost;
|
|
6
|
+
/** Lower is cheaper; used to prefer cheaper fallbacks after a failure. */
|
|
7
|
+
costRank: number;
|
|
8
|
+
}
|
|
9
|
+
export interface FallbackChain {
|
|
10
|
+
/** The winning model id (first choice). */
|
|
11
|
+
primary: string;
|
|
12
|
+
/** Ordered cheaper alternatives after the primary. */
|
|
13
|
+
alternatives: FallbackEntry[];
|
|
14
|
+
/** All entries (primary + alternatives) in failover order. */
|
|
15
|
+
all: FallbackEntry[];
|
|
16
|
+
}
|
|
17
|
+
interface RetryableError {
|
|
18
|
+
/** HTTP status or "timeout". Rate-limit (429) and 5xx are retryable. */
|
|
19
|
+
kind: "rate-limit" | "server" | "timeout" | "other";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Classify an error into retryable categories. A retryable failure should
|
|
23
|
+
* trigger failover to a cheaper model in the chain.
|
|
24
|
+
*/
|
|
25
|
+
export declare function classifyError(error: unknown): RetryableError;
|
|
26
|
+
export declare function isRetryable(kind: RetryableError["kind"]): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Build a failover chain for a capability pool. The primary is the current
|
|
29
|
+
* winner; alternatives are the remaining candidates sorted by increasing cost
|
|
30
|
+
* (cheaper first), so a retry steps down in price instead of up.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildFallbackChain(pool: ModelCandidateInput[], capability: Parameters<typeof resolveModel>[0]["capability"], budget: BudgetMode, allowPaid: boolean, options?: {
|
|
33
|
+
preferredCosts?: ModelCost[];
|
|
34
|
+
preferredTiers?: Parameters<typeof resolveModel>[0]["preferredTiers"];
|
|
35
|
+
}): FallbackChain | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Given a chain and the id of a model that just failed, return the next
|
|
38
|
+
* cheaper alternative to try, or undefined when the chain is exhausted.
|
|
39
|
+
*/
|
|
40
|
+
export declare function nextAfterFailure(chain: FallbackChain, failedId: string): string | undefined;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { normalizeCandidate, resolveModel } from "./model-resolver.js";
|
|
2
|
+
const COST_RANK = { paid: 2, subscription: 1, free: 0 };
|
|
3
|
+
/**
|
|
4
|
+
* Classify an error into retryable categories. A retryable failure should
|
|
5
|
+
* trigger failover to a cheaper model in the chain.
|
|
6
|
+
*/
|
|
7
|
+
export function classifyError(error) {
|
|
8
|
+
const candidate = typeof error === "object" && error !== null ? error : undefined;
|
|
9
|
+
const message = typeof error === "string"
|
|
10
|
+
? error
|
|
11
|
+
: candidate?.message
|
|
12
|
+
? String(candidate.message)
|
|
13
|
+
: error instanceof Error
|
|
14
|
+
? error.message
|
|
15
|
+
: String(error);
|
|
16
|
+
const status = candidate?.status ?? candidate?.statusCode;
|
|
17
|
+
const text = (status !== undefined ? String(status) + " " : "") + message.toLowerCase();
|
|
18
|
+
if (status === 429 || text.includes("429") || text.includes("rate limit") || text.includes("rate-limit") || text.includes("too many requests")) {
|
|
19
|
+
return { kind: "rate-limit" };
|
|
20
|
+
}
|
|
21
|
+
if (status === 408 || text.includes("timeout") || text.includes("timed out")) {
|
|
22
|
+
return { kind: "timeout" };
|
|
23
|
+
}
|
|
24
|
+
if ((status !== undefined && status >= 500) || text.includes("internal server error") || text.includes("service unavailable") || text.includes("overloaded")) {
|
|
25
|
+
return { kind: "server" };
|
|
26
|
+
}
|
|
27
|
+
return { kind: "other" };
|
|
28
|
+
}
|
|
29
|
+
export function isRetryable(kind) {
|
|
30
|
+
return kind === "rate-limit" || kind === "server" || kind === "timeout";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build a failover chain for a capability pool. The primary is the current
|
|
34
|
+
* winner; alternatives are the remaining candidates sorted by increasing cost
|
|
35
|
+
* (cheaper first), so a retry steps down in price instead of up.
|
|
36
|
+
*/
|
|
37
|
+
export function buildFallbackChain(pool, capability, budget, allowPaid, options = {}) {
|
|
38
|
+
const normalized = pool.map(normalizeCandidate);
|
|
39
|
+
if (normalized.length === 0)
|
|
40
|
+
return undefined;
|
|
41
|
+
const winner = resolveModel({
|
|
42
|
+
pool,
|
|
43
|
+
capability,
|
|
44
|
+
budget,
|
|
45
|
+
allowPaid,
|
|
46
|
+
...(options.preferredCosts ? { preferredCosts: options.preferredCosts } : {}),
|
|
47
|
+
...(options.preferredTiers ? { preferredTiers: options.preferredTiers } : {}),
|
|
48
|
+
});
|
|
49
|
+
const primary = winner?.id ?? normalized[0]?.id;
|
|
50
|
+
if (!primary)
|
|
51
|
+
return undefined;
|
|
52
|
+
const byId = new Map(normalized.map((candidate) => [candidate.id, candidate]));
|
|
53
|
+
const primaryEntry = toEntry(byId.get(primary));
|
|
54
|
+
const alternatives = normalized
|
|
55
|
+
.filter((candidate) => candidate.id !== primary)
|
|
56
|
+
.sort((a, b) => COST_RANK[a.cost] - COST_RANK[b.cost] || a.id.localeCompare(b.id))
|
|
57
|
+
.map((candidate) => toEntry(candidate));
|
|
58
|
+
return {
|
|
59
|
+
primary,
|
|
60
|
+
alternatives,
|
|
61
|
+
all: primaryEntry ? [primaryEntry, ...alternatives] : alternatives,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function toEntry(candidate) {
|
|
65
|
+
return {
|
|
66
|
+
id: candidate?.id ?? "",
|
|
67
|
+
cost: candidate?.cost ?? "free",
|
|
68
|
+
costRank: COST_RANK[candidate?.cost ?? "free"],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Given a chain and the id of a model that just failed, return the next
|
|
73
|
+
* cheaper alternative to try, or undefined when the chain is exhausted.
|
|
74
|
+
*/
|
|
75
|
+
export function nextAfterFailure(chain, failedId) {
|
|
76
|
+
const index = chain.all.findIndex((entry) => entry.id === failedId);
|
|
77
|
+
if (index < 0)
|
|
78
|
+
return undefined;
|
|
79
|
+
return chain.all[index + 1]?.id;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../src/routing/fallback.ts"],"names":[],"mappings":"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"}
|