heyio 0.42.0 → 1.0.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/README.md +40 -52
- package/dist/api/auth.js +35 -38
- package/dist/api/server.js +157 -1139
- package/dist/config.js +49 -32
- package/dist/copilot/agents.js +72 -1055
- package/dist/copilot/client.js +6 -17
- package/dist/copilot/io-scheduler.js +55 -139
- package/dist/copilot/model-router.js +100 -72
- package/dist/copilot/orchestrator.js +91 -515
- package/dist/copilot/scheduler.js +67 -189
- package/dist/copilot/skills.js +41 -366
- package/dist/copilot/system-message.js +40 -200
- package/dist/copilot/tools.js +191 -2042
- package/dist/daemon.js +54 -201
- package/dist/index.js +15 -133
- package/dist/mcp/config.js +23 -31
- package/dist/mcp/index.js +2 -3
- package/dist/mcp/registry.js +33 -88
- package/dist/notify.js +18 -100
- package/dist/paths.js +13 -24
- package/dist/setup.js +35 -0
- package/dist/store/db.js +111 -297
- package/dist/store/feed.js +29 -97
- package/dist/store/instances.js +56 -121
- package/dist/store/schedules.js +21 -73
- package/dist/store/squads.js +35 -186
- package/dist/store/tasks.js +25 -168
- package/dist/telegram/bot.js +20 -312
- package/dist/telegram/handlers.js +39 -3
- package/dist/watchdog.js +31 -45
- package/dist/wiki/fs.js +38 -155
- package/dist/wiki/search.js +31 -44
- package/package.json +5 -8
- package/web-dist/assets/ChatView-EFFiln1H.js +11 -0
- package/web-dist/assets/FeedView-bN4NMOL7.js +6 -0
- package/web-dist/assets/LoginView-CNtasq3n.js +1 -0
- package/web-dist/assets/McpView-C2CHiwsi.js +1 -0
- package/web-dist/assets/SchedulesView-CyilLban.js +1 -0
- package/web-dist/assets/SettingsView-1wLXKEF4.js +1 -0
- package/web-dist/assets/SkillsView-BLsD-0u0.js +1 -0
- package/web-dist/assets/SquadDetailView-CsCw2ZLp.js +21 -0
- package/web-dist/assets/SquadsView-DQ3vFlyO.js +6 -0
- package/web-dist/assets/WikiView-19M3oqnq.js +21 -0
- package/web-dist/assets/api-WGvTsXaE.js +1 -0
- package/web-dist/assets/index-D7M5O-_l.css +1 -0
- package/web-dist/assets/index-DZOS9syn.js +95 -0
- package/web-dist/assets/plus-BOvyX1BC.js +6 -0
- package/web-dist/assets/trash-2-DHoetkC4.js +6 -0
- package/web-dist/favicon.svg +4 -1
- package/web-dist/index.html +7 -10
- package/dist/api/logout.test.js +0 -129
- package/dist/api/mcp.test.js +0 -285
- package/dist/api/wiki.test.js +0 -283
- package/dist/auth/session-logic.js +0 -79
- package/dist/auth/session-logic.test.js +0 -201
- package/dist/copilot/auto-complete-instance.test.js +0 -104
- package/dist/copilot/cron.js +0 -136
- package/dist/copilot/event-summary.js +0 -286
- package/dist/copilot/instance-deactivate.test.js +0 -119
- package/dist/copilot/model-router.test.js +0 -71
- package/dist/copilot/review-backfill.js +0 -57
- package/dist/copilot/session-timeout.js +0 -112
- package/dist/copilot/session-timeout.test.js +0 -372
- package/dist/copilot/skills.test.js +0 -55
- package/dist/copilot/universes.js +0 -469
- package/dist/instance-watchdog.js +0 -104
- package/dist/instance-watchdog.test.js +0 -183
- package/dist/mcp/client.js +0 -109
- package/dist/mcp/client.test.js +0 -99
- package/dist/mcp/config.test.js +0 -49
- package/dist/mcp/registry.test.js +0 -79
- package/dist/notify.test.js +0 -232
- package/dist/store/feed.test.js +0 -279
- package/dist/store/instances.test.js +0 -310
- package/dist/store/io-schedules.js +0 -63
- package/dist/store/notifications.js +0 -79
- package/dist/store/notifications.test.js +0 -197
- package/dist/store/schedule-runs.js +0 -46
- package/dist/store/squads.test.js +0 -405
- package/dist/store/tasks.test.js +0 -150
- package/dist/store/worktrees.js +0 -83
- package/dist/tui/index.js +0 -286
- package/dist/update.js +0 -81
- package/dist/watchdog.test.js +0 -83
- package/dist/wiki/wiki-squad.test.js +0 -54
- package/web-dist/assets/AgentActivityView-CedxxE6K.js +0 -1
- package/web-dist/assets/ChatView-DMkYQo_V.js +0 -4
- package/web-dist/assets/FeedView-BH4q-31V.js +0 -1
- package/web-dist/assets/InboxView-BVwVP4EW.js +0 -1
- package/web-dist/assets/LoginView-DRPDhnwu.js +0 -1
- package/web-dist/assets/McpView-D8yWz-lq.js +0 -1
- package/web-dist/assets/SchedulesView-BzzyncGF.js +0 -1
- package/web-dist/assets/SettingsTabs.vue_vue_type_script_setup_true_lang-oW3ySu7Y.js +0 -1
- package/web-dist/assets/SkillsView-oxpYuhx7.js +0 -1
- package/web-dist/assets/SquadsView-CaKUIKlq.js +0 -1
- package/web-dist/assets/StatusIndicator.vue_vue_type_script_setup_true_lang-8U15Qp_Q.js +0 -1
- package/web-dist/assets/WikiView-C5jXUlfW.js +0 -1
- package/web-dist/assets/index-BrWzNw-N.css +0 -10
- package/web-dist/assets/index-f67odrrt.js +0 -81
- package/web-dist/icons.svg +0 -24
package/dist/copilot/client.js
CHANGED
|
@@ -2,17 +2,7 @@ import { CopilotClient } from "@github/copilot-sdk";
|
|
|
2
2
|
let client;
|
|
3
3
|
export async function getClient() {
|
|
4
4
|
if (!client) {
|
|
5
|
-
|
|
6
|
-
autoStart: true,
|
|
7
|
-
};
|
|
8
|
-
// Pass explicit token if available (env vars are also auto-detected by the SDK)
|
|
9
|
-
const token = process.env.COPILOT_GITHUB_TOKEN
|
|
10
|
-
|| process.env.GH_TOKEN
|
|
11
|
-
|| process.env.GITHUB_TOKEN;
|
|
12
|
-
if (token) {
|
|
13
|
-
opts.githubToken = token;
|
|
14
|
-
}
|
|
15
|
-
client = new CopilotClient(opts);
|
|
5
|
+
client = new CopilotClient();
|
|
16
6
|
await client.start();
|
|
17
7
|
}
|
|
18
8
|
return client;
|
|
@@ -22,15 +12,14 @@ export async function resetClient() {
|
|
|
22
12
|
try {
|
|
23
13
|
await client.stop();
|
|
24
14
|
}
|
|
25
|
-
catch {
|
|
15
|
+
catch {
|
|
16
|
+
// ignore stop errors during reset
|
|
17
|
+
}
|
|
26
18
|
client = undefined;
|
|
27
19
|
}
|
|
28
20
|
return getClient();
|
|
29
21
|
}
|
|
30
|
-
export
|
|
31
|
-
|
|
32
|
-
await client.stop();
|
|
33
|
-
client = undefined;
|
|
34
|
-
}
|
|
22
|
+
export function getClientInstance() {
|
|
23
|
+
return client;
|
|
35
24
|
}
|
|
36
25
|
//# sourceMappingURL=client.js.map
|
|
@@ -1,155 +1,71 @@
|
|
|
1
|
-
|
|
2
|
-
// any squad. Mirrors the squad scheduler in shape (TICK_MS loop, in-flight
|
|
3
|
-
// guard, reconcile on startup) but dispatches into the orchestrator via
|
|
4
|
-
// sendToOrchestrator with a `background` source so IO can handle the prompt
|
|
5
|
-
// the same way it handles any other user message.
|
|
6
|
-
import { listIoSchedules, listDueIoSchedules, recordIoScheduleRun, setIoScheduleTimestamps, updateIoScheduleNextRun, } from "../store/io-schedules.js";
|
|
1
|
+
import { listSchedules, updateScheduleLastRun } from "../store/schedules.js";
|
|
7
2
|
import { sendToOrchestrator } from "./orchestrator.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
function buildPrompt(schedule) {
|
|
15
|
-
const header = `# Scheduled task: ${schedule.name}\n\n_This prompt was fired automatically by the IO scheduler. Cron expression: \`${schedule.cron_expr}\`._`;
|
|
16
|
-
const notes = schedule.notes
|
|
17
|
-
? `\n\n**Operator notes:** ${schedule.notes}`
|
|
18
|
-
: "";
|
|
19
|
-
return `${header}\n\n${schedule.prompt}${notes}`;
|
|
3
|
+
let ioSchedulerInterval;
|
|
4
|
+
export function startIoScheduler() {
|
|
5
|
+
ioSchedulerInterval = setInterval(() => {
|
|
6
|
+
checkIoSchedules();
|
|
7
|
+
}, 60_000);
|
|
8
|
+
ioSchedulerInterval.unref();
|
|
20
9
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
console.error(`[io] io-scheduler: cron parse error for schedule ${schedule.id}:`, err instanceof Error ? err.message : err);
|
|
32
|
-
}
|
|
33
|
-
recordIoScheduleRun(schedule.id, ranAt, nextIso);
|
|
34
|
-
console.log(`[io] io-scheduler: firing schedule "${schedule.name}" (next run: ${nextIso ?? "never"})`);
|
|
35
|
-
const run = startScheduleRun({
|
|
36
|
-
schedule_type: "io",
|
|
37
|
-
schedule_id: schedule.id,
|
|
38
|
-
schedule_name: schedule.name,
|
|
39
|
-
});
|
|
40
|
-
try {
|
|
41
|
-
let buffer = "";
|
|
42
|
-
await sendToOrchestrator(buildPrompt(schedule), { type: "background" }, (text, done) => {
|
|
43
|
-
buffer += text;
|
|
10
|
+
function checkIoSchedules() {
|
|
11
|
+
const schedules = listSchedules("io");
|
|
12
|
+
const now = new Date();
|
|
13
|
+
for (const schedule of schedules) {
|
|
14
|
+
if (!schedule.enabled)
|
|
15
|
+
continue;
|
|
16
|
+
if (!isDue(schedule.cron, schedule.last_run, now))
|
|
17
|
+
continue;
|
|
18
|
+
updateScheduleLastRun(schedule.id);
|
|
19
|
+
sendToOrchestrator(schedule.prompt, "io-scheduler", (_text, done) => {
|
|
44
20
|
if (done) {
|
|
45
|
-
|
|
46
|
-
source: {
|
|
47
|
-
type: "io-schedule",
|
|
48
|
-
scheduleId: schedule.id,
|
|
49
|
-
scheduleName: schedule.name,
|
|
50
|
-
},
|
|
51
|
-
title: `IO schedule: ${schedule.name}`,
|
|
52
|
-
text: buffer.trim(),
|
|
53
|
-
}).then((notifyResult) => {
|
|
54
|
-
completeScheduleRun(run.id, notifyResult.id);
|
|
55
|
-
}).catch((err) => {
|
|
56
|
-
failScheduleRun(run.id, err instanceof Error ? err.message : String(err));
|
|
57
|
-
});
|
|
21
|
+
console.log(`[io-scheduler] Schedule ${schedule.id} completed.`);
|
|
58
22
|
}
|
|
59
23
|
});
|
|
60
24
|
}
|
|
61
|
-
catch (err) {
|
|
62
|
-
failScheduleRun(run.id, err instanceof Error ? err.message : String(err));
|
|
63
|
-
console.error(`[io] io-scheduler: failed to dispatch schedule ${schedule.id}:`, err instanceof Error ? err.message : err);
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
66
|
-
inFlight.delete(schedule.id);
|
|
67
|
-
}
|
|
68
25
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
26
|
+
function isDue(cron, lastRun, now) {
|
|
27
|
+
const parts = cron.split(" ");
|
|
28
|
+
if (parts.length !== 5)
|
|
29
|
+
return false;
|
|
30
|
+
const [minSpec, hourSpec, daySpec, monthSpec, weekdaySpec] = parts;
|
|
31
|
+
if (!matchesCronField(minSpec, now.getMinutes()))
|
|
32
|
+
return false;
|
|
33
|
+
if (!matchesCronField(hourSpec, now.getHours()))
|
|
34
|
+
return false;
|
|
35
|
+
if (!matchesCronField(daySpec, now.getDate()))
|
|
36
|
+
return false;
|
|
37
|
+
if (!matchesCronField(monthSpec, now.getMonth() + 1))
|
|
38
|
+
return false;
|
|
39
|
+
if (!matchesCronField(weekdaySpec, now.getDay()))
|
|
40
|
+
return false;
|
|
41
|
+
if (lastRun) {
|
|
42
|
+
const lastDate = new Date(lastRun);
|
|
43
|
+
const diffMs = now.getTime() - lastDate.getTime();
|
|
44
|
+
if (diffMs < 60_000)
|
|
45
|
+
return false;
|
|
73
46
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
function matchesCronField(spec, value) {
|
|
50
|
+
if (spec === "*")
|
|
51
|
+
return true;
|
|
52
|
+
if (spec.includes("-")) {
|
|
53
|
+
const [start, end] = spec.split("-").map(Number);
|
|
54
|
+
return value >= start && value <= end;
|
|
77
55
|
}
|
|
78
|
-
|
|
79
|
-
|
|
56
|
+
if (spec.includes(",")) {
|
|
57
|
+
return spec.split(",").map(Number).includes(value);
|
|
80
58
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
* advance to the next future occurrence rather than replaying missed runs
|
|
85
|
-
* — same semantics as the squad scheduler.
|
|
86
|
-
*/
|
|
87
|
-
export function reconcileIoSchedules(now = new Date()) {
|
|
88
|
-
for (const s of listIoSchedules()) {
|
|
89
|
-
if (!s.enabled)
|
|
90
|
-
continue;
|
|
91
|
-
let needsUpdate = false;
|
|
92
|
-
if (!s.next_run_at) {
|
|
93
|
-
needsUpdate = true;
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
const next = new Date(s.next_run_at);
|
|
97
|
-
if (Number.isNaN(next.getTime()) || next <= now)
|
|
98
|
-
needsUpdate = true;
|
|
99
|
-
}
|
|
100
|
-
if (!needsUpdate)
|
|
101
|
-
continue;
|
|
102
|
-
try {
|
|
103
|
-
const next = nextRun(s.cron_expr, now);
|
|
104
|
-
updateIoScheduleNextRun(s.id, next.toISOString());
|
|
105
|
-
}
|
|
106
|
-
catch (err) {
|
|
107
|
-
console.error(`[io] io-scheduler: invalid cron "${s.cron_expr}" on schedule ${s.id}; clearing next_run_at:`, err instanceof Error ? err.message : err);
|
|
108
|
-
updateIoScheduleNextRun(s.id, null);
|
|
109
|
-
}
|
|
59
|
+
if (spec.startsWith("*/")) {
|
|
60
|
+
const step = parseInt(spec.slice(2), 10);
|
|
61
|
+
return value % step === 0;
|
|
110
62
|
}
|
|
111
|
-
|
|
112
|
-
export function startIoScheduler() {
|
|
113
|
-
if (timer)
|
|
114
|
-
return;
|
|
115
|
-
reconcileIoSchedules();
|
|
116
|
-
timer = setInterval(() => {
|
|
117
|
-
void tick();
|
|
118
|
-
}, TICK_MS);
|
|
119
|
-
// Don't keep the event loop alive on shutdown
|
|
120
|
-
timer.unref?.();
|
|
63
|
+
return parseInt(spec, 10) === value;
|
|
121
64
|
}
|
|
122
65
|
export function stopIoScheduler() {
|
|
123
|
-
if (
|
|
124
|
-
clearInterval(
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Force a schedule to run immediately. Used by the `schedule_run_now` tool.
|
|
130
|
-
*
|
|
131
|
-
* The regular tick path (`fireSchedule`) advances `last_run_at` and
|
|
132
|
-
* `next_run_at` as a side effect, which is correct for an automatic firing
|
|
133
|
-
* but is the wrong behaviour for a manual one — a user testing a schedule at
|
|
134
|
-
* 04:30 should not have the 05:00 occurrence skipped or the schedule shifted.
|
|
135
|
-
* We therefore snapshot both timestamps before firing and restore them after,
|
|
136
|
-
* leaving the persisted schedule untouched.
|
|
137
|
-
*/
|
|
138
|
-
export async function runIoScheduleNow(id) {
|
|
139
|
-
const all = listIoSchedules();
|
|
140
|
-
const s = all.find((x) => x.id === id);
|
|
141
|
-
if (!s)
|
|
142
|
-
return false;
|
|
143
|
-
const previousLast = s.last_run_at;
|
|
144
|
-
const previousNext = s.next_run_at;
|
|
145
|
-
try {
|
|
146
|
-
await fireSchedule(s);
|
|
147
|
-
}
|
|
148
|
-
finally {
|
|
149
|
-
// Restore the original timestamps even if fireSchedule threw, so a
|
|
150
|
-
// failed manual run cannot silently shift the schedule either.
|
|
151
|
-
setIoScheduleTimestamps(id, previousLast, previousNext);
|
|
66
|
+
if (ioSchedulerInterval) {
|
|
67
|
+
clearInterval(ioSchedulerInterval);
|
|
68
|
+
ioSchedulerInterval = undefined;
|
|
152
69
|
}
|
|
153
|
-
return true;
|
|
154
70
|
}
|
|
155
71
|
//# sourceMappingURL=io-scheduler.js.map
|
|
@@ -1,84 +1,112 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { loadConfig } from "../config.js";
|
|
2
|
+
import { getClient } from "./client.js";
|
|
3
|
+
// Cache discovered models so we don't call listModels() on every task
|
|
4
|
+
let discoveredModels;
|
|
5
|
+
/**
|
|
6
|
+
* Built-in model capability hints. Used as fallback when billing info
|
|
7
|
+
* isn't available from the SDK. Higher = more capable.
|
|
8
|
+
*/
|
|
9
|
+
const MODEL_CAPABILITY_HINTS = {
|
|
10
|
+
"claude-opus-4.7": 90,
|
|
11
|
+
"claude-opus-4.6": 88,
|
|
12
|
+
"claude-opus-4.5": 85,
|
|
13
|
+
"gpt-5.5": 87,
|
|
14
|
+
"gpt-5.4": 84,
|
|
15
|
+
"gpt-5.3-codex": 83,
|
|
16
|
+
"gpt-5.2-codex": 82,
|
|
17
|
+
"gpt-5.2": 80,
|
|
18
|
+
"claude-sonnet-4.6": 70,
|
|
19
|
+
"claude-sonnet-4.5": 68,
|
|
20
|
+
"gpt-4.1": 65,
|
|
21
|
+
"claude-haiku-4.5": 40,
|
|
22
|
+
"gpt-5.4-mini": 42,
|
|
23
|
+
"gpt-5-mini": 38,
|
|
6
24
|
};
|
|
7
|
-
// Resolved model for each tier (populated at startup)
|
|
8
|
-
let resolvedTiers = null;
|
|
9
|
-
const HIGH_KEYWORDS = [
|
|
10
|
-
"architect", "refactor", "redesign", "debug", "design",
|
|
11
|
-
"complex", "migration", "security", "performance", "optimize",
|
|
12
|
-
"rewrite", "overhaul", "investigate", "diagnose", "plan",
|
|
13
|
-
];
|
|
14
|
-
const LOW_KEYWORDS = [
|
|
15
|
-
"read", "list", "format", "lookup", "check", "status",
|
|
16
|
-
"simple", "rename", "typo", "log", "print", "echo",
|
|
17
|
-
"delete file", "remove file", "copy file", "move file",
|
|
18
|
-
];
|
|
19
25
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
26
|
+
* Discover available models from the Copilot SDK and score them.
|
|
27
|
+
* Uses billing multiplier as primary capability signal, falls back to
|
|
28
|
+
* built-in hints for unknown models.
|
|
22
29
|
*/
|
|
23
|
-
export function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
export async function discoverModels() {
|
|
31
|
+
if (discoveredModels)
|
|
32
|
+
return discoveredModels;
|
|
33
|
+
try {
|
|
34
|
+
const client = await getClient();
|
|
35
|
+
const models = await client.listModels();
|
|
36
|
+
discoveredModels = models
|
|
37
|
+
.filter((m) => !m.policy || m.policy.state === "enabled")
|
|
38
|
+
.map((m) => ({
|
|
39
|
+
id: m.id,
|
|
40
|
+
// Use billing multiplier as capability proxy (higher cost = more capable)
|
|
41
|
+
// Fall back to built-in hints, then default of 50
|
|
42
|
+
score: m.billing
|
|
43
|
+
? Math.min(m.billing.multiplier * 10, 100)
|
|
44
|
+
: (MODEL_CAPABILITY_HINTS[m.id] ?? 50),
|
|
45
|
+
}))
|
|
46
|
+
.sort((a, b) => b.score - a.score);
|
|
38
47
|
}
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
catch {
|
|
49
|
+
// SDK discovery failed — fall back to defaultModel only
|
|
50
|
+
const config = loadConfig();
|
|
51
|
+
discoveredModels = [{ id: config.defaultModel, score: 65 }];
|
|
52
|
+
}
|
|
53
|
+
return discoveredModels;
|
|
41
54
|
}
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export function classifyComplexity(taskDescription) {
|
|
46
|
-
const lower = taskDescription.toLowerCase();
|
|
47
|
-
const highScore = HIGH_KEYWORDS.filter((kw) => lower.includes(kw)).length;
|
|
48
|
-
const lowScore = LOW_KEYWORDS.filter((kw) => lower.includes(kw)).length;
|
|
49
|
-
// Strong signals
|
|
50
|
-
if (highScore >= 2)
|
|
51
|
-
return "high";
|
|
52
|
-
if (lowScore >= 2)
|
|
53
|
-
return "low";
|
|
54
|
-
// Weak signals
|
|
55
|
-
if (highScore > lowScore)
|
|
56
|
-
return "high";
|
|
57
|
-
if (lowScore > highScore)
|
|
58
|
-
return "low";
|
|
59
|
-
// Default to medium for ambiguous tasks
|
|
60
|
-
return "medium";
|
|
55
|
+
/** Reset cached models (e.g. after client reconnect) */
|
|
56
|
+
export function resetModelCache() {
|
|
57
|
+
discoveredModels = undefined;
|
|
61
58
|
}
|
|
62
59
|
/**
|
|
63
|
-
*
|
|
60
|
+
* Select the best available model for a given task complexity.
|
|
61
|
+
* Discovers models from the Copilot SDK and picks based on capability —
|
|
62
|
+
* zero configuration required.
|
|
64
63
|
*/
|
|
65
|
-
export function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
export async function selectModel(complexity) {
|
|
65
|
+
const config = loadConfig();
|
|
66
|
+
const scored = await discoverModels();
|
|
67
|
+
if (scored.length === 0)
|
|
68
|
+
return config.defaultModel;
|
|
69
|
+
if (complexity === "high") {
|
|
70
|
+
// Most capable model
|
|
71
|
+
return scored[0].id;
|
|
69
72
|
}
|
|
70
|
-
|
|
73
|
+
if (complexity === "low") {
|
|
74
|
+
// Cheapest model
|
|
75
|
+
return scored[scored.length - 1].id;
|
|
76
|
+
}
|
|
77
|
+
// Medium — pick a model around the middle of the ranked list
|
|
78
|
+
const midIdx = Math.floor(scored.length / 2);
|
|
79
|
+
return scored[midIdx].id;
|
|
71
80
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
export function classifyComplexity(task) {
|
|
82
|
+
const lower = task.toLowerCase();
|
|
83
|
+
const highPatterns = [
|
|
84
|
+
"architect",
|
|
85
|
+
"design system",
|
|
86
|
+
"refactor",
|
|
87
|
+
"security audit",
|
|
88
|
+
"performance optimization",
|
|
89
|
+
"migration",
|
|
90
|
+
"complex",
|
|
91
|
+
"deep analysis",
|
|
92
|
+
"debug",
|
|
93
|
+
"race condition",
|
|
94
|
+
];
|
|
95
|
+
if (highPatterns.some((p) => lower.includes(p)))
|
|
96
|
+
return "high";
|
|
97
|
+
const lowPatterns = [
|
|
98
|
+
"format",
|
|
99
|
+
"rename",
|
|
100
|
+
"typo",
|
|
101
|
+
"simple",
|
|
102
|
+
"lookup",
|
|
103
|
+
"list",
|
|
104
|
+
"read",
|
|
105
|
+
"status",
|
|
106
|
+
"check",
|
|
107
|
+
];
|
|
108
|
+
if (lowPatterns.some((p) => lower.includes(p)))
|
|
109
|
+
return "low";
|
|
110
|
+
return "medium";
|
|
83
111
|
}
|
|
84
112
|
//# sourceMappingURL=model-router.js.map
|