heyio 4.0.2 → 4.0.4
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/daemon/cli.js +678 -174
- package/dist/daemon/index.js +628 -164
- package/dist/web/assets/{index-CbptHIYU.js → index-YRIn8uJ2.js} +76 -76
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
package/dist/daemon/index.js
CHANGED
|
@@ -60175,13 +60175,10 @@ var require_mod2 = __commonJS({
|
|
|
60175
60175
|
|
|
60176
60176
|
// packages/shared/dist/constants.js
|
|
60177
60177
|
var APP_NAME = "io";
|
|
60178
|
-
var APP_VERSION = "4.0.
|
|
60178
|
+
var APP_VERSION = "4.0.4";
|
|
60179
60179
|
var API_PORT = 7777;
|
|
60180
60180
|
var API_HOST = "0.0.0.0";
|
|
60181
|
-
var DEFAULT_MODEL = "gpt-
|
|
60182
|
-
var FAST_MODEL = "gpt-4.1-mini";
|
|
60183
|
-
var STANDARD_MODEL = "claude-sonnet-4.6";
|
|
60184
|
-
var PREMIUM_MODEL = "claude-sonnet-4.6";
|
|
60181
|
+
var DEFAULT_MODEL = "gpt-4o";
|
|
60185
60182
|
var SESSION_RESET_THRESHOLD = 50;
|
|
60186
60183
|
var SCHEDULER_INTERVAL_MS = 6e4;
|
|
60187
60184
|
var QA_MAX_REVISIONS = 3;
|
|
@@ -61831,6 +61828,27 @@ var MIGRATIONS = [
|
|
|
61831
61828
|
"CREATE INDEX IF NOT EXISTS idx_activity_objective_id ON activity(objective_id)",
|
|
61832
61829
|
"CREATE INDEX IF NOT EXISTS idx_agent_history_agent_id_created_at ON agent_history(agent_id, created_at)"
|
|
61833
61830
|
]
|
|
61831
|
+
},
|
|
61832
|
+
{
|
|
61833
|
+
version: 2,
|
|
61834
|
+
name: "add-model-pricing-and-dual-costs",
|
|
61835
|
+
statements: [
|
|
61836
|
+
`CREATE TABLE IF NOT EXISTS model_pricing (
|
|
61837
|
+
id TEXT PRIMARY KEY,
|
|
61838
|
+
display_name TEXT NOT NULL,
|
|
61839
|
+
premium_multiplier REAL,
|
|
61840
|
+
token_input_multiplier REAL,
|
|
61841
|
+
token_output_multiplier REAL,
|
|
61842
|
+
cached_input_multiplier REAL,
|
|
61843
|
+
tier TEXT NOT NULL,
|
|
61844
|
+
available INTEGER NOT NULL DEFAULT 1,
|
|
61845
|
+
updated_at TEXT NOT NULL
|
|
61846
|
+
)`,
|
|
61847
|
+
"CREATE INDEX IF NOT EXISTS idx_model_pricing_tier ON model_pricing(tier)",
|
|
61848
|
+
"CREATE INDEX IF NOT EXISTS idx_model_pricing_available ON model_pricing(available)",
|
|
61849
|
+
"ALTER TABLE token_usage ADD COLUMN premium_request_cost REAL",
|
|
61850
|
+
"ALTER TABLE token_usage ADD COLUMN token_unit_cost REAL"
|
|
61851
|
+
]
|
|
61834
61852
|
}
|
|
61835
61853
|
];
|
|
61836
61854
|
var client = null;
|
|
@@ -62515,8 +62533,8 @@ async function recordUsage(data, db) {
|
|
|
62515
62533
|
createdAt: data.createdAt ?? nowIso()
|
|
62516
62534
|
};
|
|
62517
62535
|
await database.execute({
|
|
62518
|
-
sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, created_at)
|
|
62519
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
62536
|
+
sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, premium_request_cost, token_unit_cost, created_at)
|
|
62537
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
62520
62538
|
args: [
|
|
62521
62539
|
usage.id,
|
|
62522
62540
|
usage.squadId,
|
|
@@ -62525,6 +62543,8 @@ async function recordUsage(data, db) {
|
|
|
62525
62543
|
usage.inputTokens,
|
|
62526
62544
|
usage.outputTokens,
|
|
62527
62545
|
usage.cost,
|
|
62546
|
+
data.premiumRequestCost ?? null,
|
|
62547
|
+
data.tokenUnitCost ?? null,
|
|
62528
62548
|
usage.createdAt
|
|
62529
62549
|
]
|
|
62530
62550
|
});
|
|
@@ -77848,7 +77868,8 @@ var configSchema = external_exports.object({
|
|
|
77848
77868
|
telegramUserId: external_exports.string().trim().min(1).nullable().default(null),
|
|
77849
77869
|
supabaseUrl: external_exports.string().trim().min(1).nullable().default(null),
|
|
77850
77870
|
supabaseAnonKey: external_exports.string().trim().min(1).nullable().default(null),
|
|
77851
|
-
sessionResetThreshold: external_exports.coerce.number().int().positive().default(SESSION_RESET_THRESHOLD)
|
|
77871
|
+
sessionResetThreshold: external_exports.coerce.number().int().positive().default(SESSION_RESET_THRESHOLD),
|
|
77872
|
+
pricingRefreshHours: external_exports.coerce.number().positive().default(24)
|
|
77852
77873
|
});
|
|
77853
77874
|
function parseConfigFile() {
|
|
77854
77875
|
if (!existsSync(CONFIG_PATH)) {
|
|
@@ -77901,6 +77922,9 @@ function readEnvOverrides() {
|
|
|
77901
77922
|
if (process.env.IO_SESSION_RESET_THRESHOLD !== void 0) {
|
|
77902
77923
|
overrides.sessionResetThreshold = process.env.IO_SESSION_RESET_THRESHOLD;
|
|
77903
77924
|
}
|
|
77925
|
+
if (process.env.IO_PRICING_REFRESH_HOURS !== void 0) {
|
|
77926
|
+
overrides.pricingRefreshHours = process.env.IO_PRICING_REFRESH_HOURS;
|
|
77927
|
+
}
|
|
77904
77928
|
return overrides;
|
|
77905
77929
|
}
|
|
77906
77930
|
function loadConfig() {
|
|
@@ -77922,7 +77946,8 @@ var APP_SETTING_KEYS = [
|
|
|
77922
77946
|
"telegramUserId",
|
|
77923
77947
|
"supabaseUrl",
|
|
77924
77948
|
"supabaseAnonKey",
|
|
77925
|
-
"sessionResetThreshold"
|
|
77949
|
+
"sessionResetThreshold",
|
|
77950
|
+
"pricingRefreshHours"
|
|
77926
77951
|
];
|
|
77927
77952
|
router5.get("/api/settings", async (_req, res) => {
|
|
77928
77953
|
try {
|
|
@@ -78063,8 +78088,25 @@ router6.delete("/api/skills/:id", async (req, res) => {
|
|
|
78063
78088
|
});
|
|
78064
78089
|
}
|
|
78065
78090
|
});
|
|
78066
|
-
router6.get("/api/skills/discover", async (
|
|
78067
|
-
|
|
78091
|
+
router6.get("/api/skills/discover", async (req, res) => {
|
|
78092
|
+
try {
|
|
78093
|
+
const source = typeof req.query.source === "string" ? req.query.source : "";
|
|
78094
|
+
const query = typeof req.query.q === "string" ? req.query.q.trim() : "";
|
|
78095
|
+
if (source === "skillssh") {
|
|
78096
|
+
const skills = await discoverSkillsSh(query);
|
|
78097
|
+
res.status(200).json(skills);
|
|
78098
|
+
} else if (source === "awesome-copilot") {
|
|
78099
|
+
const skills = await discoverAwesomeCopilot(query);
|
|
78100
|
+
res.status(200).json(skills);
|
|
78101
|
+
} else {
|
|
78102
|
+
res.status(400).json({ error: `Unknown source: ${source}` });
|
|
78103
|
+
}
|
|
78104
|
+
} catch (error51) {
|
|
78105
|
+
res.status(500).json({
|
|
78106
|
+
error: "Failed to discover skills",
|
|
78107
|
+
details: error51 instanceof Error ? error51.message : "Unknown error"
|
|
78108
|
+
});
|
|
78109
|
+
}
|
|
78068
78110
|
});
|
|
78069
78111
|
async function installSkill(request) {
|
|
78070
78112
|
await mkdir3(SKILLS_DIR, { recursive: true });
|
|
@@ -78169,6 +78211,79 @@ function chooseEntryFileName(url2, contentType) {
|
|
|
78169
78211
|
function isMissingFileError2(error51) {
|
|
78170
78212
|
return !!error51 && typeof error51 === "object" && "code" in error51 && error51.code === "ENOENT";
|
|
78171
78213
|
}
|
|
78214
|
+
async function discoverSkillsSh(query) {
|
|
78215
|
+
if (!query) return [];
|
|
78216
|
+
const endpoint = `https://skills.sh/api/search?q=${encodeURIComponent(query)}&limit=50`;
|
|
78217
|
+
const response = await fetch(endpoint, { signal: AbortSignal.timeout(1e4) });
|
|
78218
|
+
if (!response.ok) {
|
|
78219
|
+
throw new Error(`skills.sh returned ${response.status}`);
|
|
78220
|
+
}
|
|
78221
|
+
const data = await response.json();
|
|
78222
|
+
const installedSkills = await readInstalledSkills();
|
|
78223
|
+
const installedIds = new Set(installedSkills.map((s) => s.id));
|
|
78224
|
+
return (data.skills ?? []).map((entry) => {
|
|
78225
|
+
const skillUrl = entry.source ? `https://raw.githubusercontent.com/${entry.source}/main/skills/${entry.skillId}/SKILL.md` : "";
|
|
78226
|
+
return {
|
|
78227
|
+
name: entry.name || entry.skillId,
|
|
78228
|
+
title: entry.name || entry.skillId,
|
|
78229
|
+
description: `${entry.source ?? ""}`,
|
|
78230
|
+
url: skillUrl,
|
|
78231
|
+
source: "skillssh",
|
|
78232
|
+
installed: installedIds.has(`skillssh:${normalizeSlug(entry.skillId || entry.name)}`),
|
|
78233
|
+
registrySource: entry.source ?? void 0,
|
|
78234
|
+
skillId: entry.skillId ?? void 0,
|
|
78235
|
+
installs: entry.installs ?? 0
|
|
78236
|
+
};
|
|
78237
|
+
});
|
|
78238
|
+
}
|
|
78239
|
+
var awesomeCopilotCache = null;
|
|
78240
|
+
var AWESOME_COPILOT_CACHE_TTL = 60 * 60 * 1e3;
|
|
78241
|
+
async function discoverAwesomeCopilot(query) {
|
|
78242
|
+
const now = Date.now();
|
|
78243
|
+
if (!awesomeCopilotCache || now - awesomeCopilotCache.fetchedAt > AWESOME_COPILOT_CACHE_TTL) {
|
|
78244
|
+
awesomeCopilotCache = { skills: await fetchAwesomeCopilotList(), fetchedAt: now };
|
|
78245
|
+
}
|
|
78246
|
+
const skills = awesomeCopilotCache.skills;
|
|
78247
|
+
if (!query) return skills;
|
|
78248
|
+
const needle = query.toLowerCase();
|
|
78249
|
+
return skills.filter(
|
|
78250
|
+
(s) => s.name.toLowerCase().includes(needle) || s.title.toLowerCase().includes(needle) || s.description.toLowerCase().includes(needle)
|
|
78251
|
+
);
|
|
78252
|
+
}
|
|
78253
|
+
async function fetchAwesomeCopilotList() {
|
|
78254
|
+
const treeUrl = "https://api.github.com/repos/github/awesome-copilot/git/trees/main?recursive=1";
|
|
78255
|
+
const headers = {
|
|
78256
|
+
Accept: "application/vnd.github.v3+json",
|
|
78257
|
+
"User-Agent": "io-daemon"
|
|
78258
|
+
};
|
|
78259
|
+
if (process.env.GITHUB_TOKEN) {
|
|
78260
|
+
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
78261
|
+
}
|
|
78262
|
+
const response = await fetch(treeUrl, { headers, signal: AbortSignal.timeout(15e3) });
|
|
78263
|
+
if (!response.ok) {
|
|
78264
|
+
throw new Error(`GitHub API returned ${response.status}`);
|
|
78265
|
+
}
|
|
78266
|
+
const data = await response.json();
|
|
78267
|
+
const installedSkills = await readInstalledSkills();
|
|
78268
|
+
const installedIds = new Set(installedSkills.map((s) => s.id));
|
|
78269
|
+
const skillEntries = (data.tree ?? []).filter(
|
|
78270
|
+
(entry) => entry.type === "blob" && entry.path.startsWith("skills/") && entry.path.endsWith("/SKILL.md")
|
|
78271
|
+
);
|
|
78272
|
+
return skillEntries.map((entry) => {
|
|
78273
|
+
const parts = entry.path.split("/");
|
|
78274
|
+
const skillName = parts[1] ?? "unknown";
|
|
78275
|
+
const slug = normalizeSlug(skillName);
|
|
78276
|
+
const rawUrl = `https://raw.githubusercontent.com/github/awesome-copilot/main/${entry.path}`;
|
|
78277
|
+
return {
|
|
78278
|
+
name: skillName,
|
|
78279
|
+
title: skillName.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
78280
|
+
description: "From github/awesome-copilot",
|
|
78281
|
+
url: rawUrl,
|
|
78282
|
+
source: "awesome-copilot",
|
|
78283
|
+
installed: installedIds.has(`awesome-copilot:${slug}`)
|
|
78284
|
+
};
|
|
78285
|
+
});
|
|
78286
|
+
}
|
|
78172
78287
|
|
|
78173
78288
|
// packages/daemon/src/api/routes/squads.ts
|
|
78174
78289
|
var import_express7 = __toESM(require_express2(), 1);
|
|
@@ -78994,6 +79109,12 @@ function createApiServer(config2) {
|
|
|
78994
79109
|
next();
|
|
78995
79110
|
});
|
|
78996
79111
|
app.use(import_express10.default.json({ limit: "1mb" }));
|
|
79112
|
+
app.get("/api/auth/config", (_req, res) => {
|
|
79113
|
+
res.json({
|
|
79114
|
+
supabaseUrl: config2.supabaseUrl ?? null,
|
|
79115
|
+
supabaseAnonKey: config2.supabaseAnonKey ?? null
|
|
79116
|
+
});
|
|
79117
|
+
});
|
|
78997
79118
|
app.use("/api", createAuthMiddleware(config2));
|
|
78998
79119
|
app.use(router2);
|
|
78999
79120
|
app.use(router7);
|
|
@@ -79108,6 +79229,448 @@ function createLogger(name) {
|
|
|
79108
79229
|
return getRootLogger().child({ subsystem: name });
|
|
79109
79230
|
}
|
|
79110
79231
|
|
|
79232
|
+
// packages/daemon/src/models/catalog.ts
|
|
79233
|
+
import { execFileSync } from "node:child_process";
|
|
79234
|
+
var CATALOG_URL = "https://models.github.ai/catalog/models";
|
|
79235
|
+
function resolveGitHubToken() {
|
|
79236
|
+
const envToken = process.env.GITHUB_TOKEN?.trim();
|
|
79237
|
+
if (envToken && envToken.length > 0) {
|
|
79238
|
+
return envToken;
|
|
79239
|
+
}
|
|
79240
|
+
try {
|
|
79241
|
+
const token = execFileSync("gh", ["auth", "token"], {
|
|
79242
|
+
encoding: "utf8",
|
|
79243
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
79244
|
+
}).trim();
|
|
79245
|
+
return token.length > 0 ? token : void 0;
|
|
79246
|
+
} catch {
|
|
79247
|
+
return void 0;
|
|
79248
|
+
}
|
|
79249
|
+
}
|
|
79250
|
+
async function fetchModelCatalog() {
|
|
79251
|
+
const token = resolveGitHubToken();
|
|
79252
|
+
if (!token) {
|
|
79253
|
+
throw new Error("No GitHub token available for model catalog fetch");
|
|
79254
|
+
}
|
|
79255
|
+
const response = await fetch(CATALOG_URL, {
|
|
79256
|
+
headers: {
|
|
79257
|
+
Accept: "application/json",
|
|
79258
|
+
Authorization: `Bearer ${token}`,
|
|
79259
|
+
"X-GitHub-Api-Version": "2024-12-01"
|
|
79260
|
+
}
|
|
79261
|
+
});
|
|
79262
|
+
if (!response.ok) {
|
|
79263
|
+
throw new Error(`Model catalog fetch failed: ${response.status} ${response.statusText}`);
|
|
79264
|
+
}
|
|
79265
|
+
const data = await response.json();
|
|
79266
|
+
if (!Array.isArray(data)) {
|
|
79267
|
+
throw new Error("Model catalog response is not an array");
|
|
79268
|
+
}
|
|
79269
|
+
const models = [];
|
|
79270
|
+
for (const entry of data) {
|
|
79271
|
+
const id = entry.id ?? entry.name;
|
|
79272
|
+
const displayName = entry.friendly_name ?? entry.name ?? id;
|
|
79273
|
+
if (id && typeof id === "string") {
|
|
79274
|
+
models.push({ id, displayName: displayName ?? id });
|
|
79275
|
+
}
|
|
79276
|
+
}
|
|
79277
|
+
return models;
|
|
79278
|
+
}
|
|
79279
|
+
|
|
79280
|
+
// packages/daemon/src/models/pricing-scraper.ts
|
|
79281
|
+
var TOKEN_UNIT_COSTS_URL = "https://docs.github.com/en/billing/reference/costs-for-github-models";
|
|
79282
|
+
var PREMIUM_MULTIPLIERS_URL = "https://docs.github.com/en/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans";
|
|
79283
|
+
async function scrapeTokenUnitPricing() {
|
|
79284
|
+
const html = await fetchPage(TOKEN_UNIT_COSTS_URL);
|
|
79285
|
+
return parseTokenUnitTable(html);
|
|
79286
|
+
}
|
|
79287
|
+
async function scrapePremiumRequestPricing() {
|
|
79288
|
+
const html = await fetchPage(PREMIUM_MULTIPLIERS_URL);
|
|
79289
|
+
return parsePremiumMultiplierTable(html);
|
|
79290
|
+
}
|
|
79291
|
+
async function fetchPage(url2) {
|
|
79292
|
+
const response = await fetch(url2, {
|
|
79293
|
+
headers: {
|
|
79294
|
+
Accept: "text/html",
|
|
79295
|
+
"User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
|
|
79296
|
+
},
|
|
79297
|
+
redirect: "follow"
|
|
79298
|
+
});
|
|
79299
|
+
if (!response.ok) {
|
|
79300
|
+
throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
|
|
79301
|
+
}
|
|
79302
|
+
return response.text();
|
|
79303
|
+
}
|
|
79304
|
+
function parseTokenUnitTable(html) {
|
|
79305
|
+
const results = [];
|
|
79306
|
+
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]*)<\/td>\s*<td[^>]*>([^<]+)<\/td>/gi;
|
|
79307
|
+
for (const match of html.matchAll(tableRowPattern)) {
|
|
79308
|
+
const modelName = cleanCellText(match[1]);
|
|
79309
|
+
const inputMultiplier = Number.parseFloat(match[2]);
|
|
79310
|
+
const cachedInput = match[3].trim().toLowerCase();
|
|
79311
|
+
const outputMultiplier = Number.parseFloat(match[4]);
|
|
79312
|
+
if (modelName && !Number.isNaN(inputMultiplier) && !Number.isNaN(outputMultiplier)) {
|
|
79313
|
+
results.push({
|
|
79314
|
+
modelName,
|
|
79315
|
+
inputMultiplier,
|
|
79316
|
+
cachedInputMultiplier: cachedInput === "n/a" || cachedInput === "" ? null : Number.parseFloat(cachedInput) || null,
|
|
79317
|
+
outputMultiplier
|
|
79318
|
+
});
|
|
79319
|
+
}
|
|
79320
|
+
}
|
|
79321
|
+
return results;
|
|
79322
|
+
}
|
|
79323
|
+
function parsePremiumMultiplierTable(html) {
|
|
79324
|
+
const results = [];
|
|
79325
|
+
const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<\/tr>/gi;
|
|
79326
|
+
for (const match of html.matchAll(tableRowPattern)) {
|
|
79327
|
+
const modelName = cleanCellText(match[1]);
|
|
79328
|
+
const multiplier = Number.parseFloat(match[2]);
|
|
79329
|
+
if (modelName && !Number.isNaN(multiplier) && multiplier > 0) {
|
|
79330
|
+
results.push({ modelName, multiplier });
|
|
79331
|
+
}
|
|
79332
|
+
}
|
|
79333
|
+
return results;
|
|
79334
|
+
}
|
|
79335
|
+
function cleanCellText(text) {
|
|
79336
|
+
return text.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " ").trim();
|
|
79337
|
+
}
|
|
79338
|
+
|
|
79339
|
+
// packages/daemon/src/models/seed.ts
|
|
79340
|
+
var SEED_MODELS = [
|
|
79341
|
+
{
|
|
79342
|
+
id: "gpt-4o-mini",
|
|
79343
|
+
displayName: "OpenAI GPT-4o mini",
|
|
79344
|
+
premiumMultiplier: 0.33,
|
|
79345
|
+
tokenInputMultiplier: 0.015,
|
|
79346
|
+
tokenOutputMultiplier: 0.06,
|
|
79347
|
+
cachedInputMultiplier: 75e-4,
|
|
79348
|
+
tier: "trivial",
|
|
79349
|
+
available: true
|
|
79350
|
+
},
|
|
79351
|
+
{
|
|
79352
|
+
id: "gpt-4o",
|
|
79353
|
+
displayName: "OpenAI GPT-4o",
|
|
79354
|
+
premiumMultiplier: 0.33,
|
|
79355
|
+
tokenInputMultiplier: 0.25,
|
|
79356
|
+
tokenOutputMultiplier: 1,
|
|
79357
|
+
cachedInputMultiplier: 0.125,
|
|
79358
|
+
tier: "trivial",
|
|
79359
|
+
available: true
|
|
79360
|
+
},
|
|
79361
|
+
{
|
|
79362
|
+
id: "gpt-5-mini",
|
|
79363
|
+
displayName: "GPT-5 mini",
|
|
79364
|
+
premiumMultiplier: 0.33,
|
|
79365
|
+
tokenInputMultiplier: null,
|
|
79366
|
+
tokenOutputMultiplier: null,
|
|
79367
|
+
cachedInputMultiplier: null,
|
|
79368
|
+
tier: "trivial",
|
|
79369
|
+
available: true
|
|
79370
|
+
},
|
|
79371
|
+
{
|
|
79372
|
+
id: "claude-sonnet-4",
|
|
79373
|
+
displayName: "Claude Sonnet 4",
|
|
79374
|
+
premiumMultiplier: 6,
|
|
79375
|
+
tokenInputMultiplier: 0.6,
|
|
79376
|
+
tokenOutputMultiplier: 2.4,
|
|
79377
|
+
cachedInputMultiplier: null,
|
|
79378
|
+
tier: "premium",
|
|
79379
|
+
available: true
|
|
79380
|
+
},
|
|
79381
|
+
{
|
|
79382
|
+
id: "claude-haiku-4.5",
|
|
79383
|
+
displayName: "Claude Haiku 4.5",
|
|
79384
|
+
premiumMultiplier: 0.33,
|
|
79385
|
+
tokenInputMultiplier: null,
|
|
79386
|
+
tokenOutputMultiplier: null,
|
|
79387
|
+
cachedInputMultiplier: null,
|
|
79388
|
+
tier: "trivial",
|
|
79389
|
+
available: true
|
|
79390
|
+
},
|
|
79391
|
+
{
|
|
79392
|
+
id: "gemini-2.5-pro",
|
|
79393
|
+
displayName: "Gemini 2.5 Pro",
|
|
79394
|
+
premiumMultiplier: 1,
|
|
79395
|
+
tokenInputMultiplier: null,
|
|
79396
|
+
tokenOutputMultiplier: null,
|
|
79397
|
+
cachedInputMultiplier: null,
|
|
79398
|
+
tier: "fast",
|
|
79399
|
+
available: true
|
|
79400
|
+
},
|
|
79401
|
+
{
|
|
79402
|
+
id: "gpt-5.1",
|
|
79403
|
+
displayName: "GPT-5.1",
|
|
79404
|
+
premiumMultiplier: 3,
|
|
79405
|
+
tokenInputMultiplier: null,
|
|
79406
|
+
tokenOutputMultiplier: null,
|
|
79407
|
+
cachedInputMultiplier: null,
|
|
79408
|
+
tier: "standard",
|
|
79409
|
+
available: true
|
|
79410
|
+
},
|
|
79411
|
+
{
|
|
79412
|
+
id: "gpt-5.2",
|
|
79413
|
+
displayName: "GPT-5.2",
|
|
79414
|
+
premiumMultiplier: 3,
|
|
79415
|
+
tokenInputMultiplier: null,
|
|
79416
|
+
tokenOutputMultiplier: null,
|
|
79417
|
+
cachedInputMultiplier: null,
|
|
79418
|
+
tier: "standard",
|
|
79419
|
+
available: true
|
|
79420
|
+
},
|
|
79421
|
+
{
|
|
79422
|
+
id: "gpt-5.4",
|
|
79423
|
+
displayName: "GPT-5.4",
|
|
79424
|
+
premiumMultiplier: 6,
|
|
79425
|
+
tokenInputMultiplier: null,
|
|
79426
|
+
tokenOutputMultiplier: null,
|
|
79427
|
+
cachedInputMultiplier: null,
|
|
79428
|
+
tier: "premium",
|
|
79429
|
+
available: true
|
|
79430
|
+
},
|
|
79431
|
+
{
|
|
79432
|
+
id: "claude-opus-4.5",
|
|
79433
|
+
displayName: "Claude Opus 4.5",
|
|
79434
|
+
premiumMultiplier: 15,
|
|
79435
|
+
tokenInputMultiplier: null,
|
|
79436
|
+
tokenOutputMultiplier: null,
|
|
79437
|
+
cachedInputMultiplier: null,
|
|
79438
|
+
tier: "premium",
|
|
79439
|
+
available: true
|
|
79440
|
+
},
|
|
79441
|
+
{
|
|
79442
|
+
id: "claude-opus-4.6",
|
|
79443
|
+
displayName: "Claude Opus 4.6",
|
|
79444
|
+
premiumMultiplier: 27,
|
|
79445
|
+
tokenInputMultiplier: null,
|
|
79446
|
+
tokenOutputMultiplier: null,
|
|
79447
|
+
cachedInputMultiplier: null,
|
|
79448
|
+
tier: "ultra",
|
|
79449
|
+
available: true
|
|
79450
|
+
},
|
|
79451
|
+
{
|
|
79452
|
+
id: "claude-opus-4.7",
|
|
79453
|
+
displayName: "Claude Opus 4.7",
|
|
79454
|
+
premiumMultiplier: 27,
|
|
79455
|
+
tokenInputMultiplier: null,
|
|
79456
|
+
tokenOutputMultiplier: null,
|
|
79457
|
+
cachedInputMultiplier: null,
|
|
79458
|
+
tier: "ultra",
|
|
79459
|
+
available: true
|
|
79460
|
+
},
|
|
79461
|
+
{
|
|
79462
|
+
id: "gpt-5.5",
|
|
79463
|
+
displayName: "GPT-5.5",
|
|
79464
|
+
premiumMultiplier: 57,
|
|
79465
|
+
tokenInputMultiplier: null,
|
|
79466
|
+
tokenOutputMultiplier: null,
|
|
79467
|
+
cachedInputMultiplier: null,
|
|
79468
|
+
tier: "ultra",
|
|
79469
|
+
available: true
|
|
79470
|
+
}
|
|
79471
|
+
];
|
|
79472
|
+
|
|
79473
|
+
// packages/daemon/src/models/types.ts
|
|
79474
|
+
var TIER_RANGES = {
|
|
79475
|
+
trivial: { min: 0, max: 0.33 },
|
|
79476
|
+
fast: { min: 0.34, max: 1 },
|
|
79477
|
+
standard: { min: 1.1, max: 5 },
|
|
79478
|
+
premium: { min: 5.1, max: 15 },
|
|
79479
|
+
ultra: { min: 15.1, max: Number.POSITIVE_INFINITY }
|
|
79480
|
+
};
|
|
79481
|
+
function computeTierFromMultiplier(premiumMultiplier) {
|
|
79482
|
+
if (premiumMultiplier === null) {
|
|
79483
|
+
return "standard";
|
|
79484
|
+
}
|
|
79485
|
+
for (const [tier, range] of Object.entries(TIER_RANGES)) {
|
|
79486
|
+
if (premiumMultiplier >= range.min && premiumMultiplier <= range.max) {
|
|
79487
|
+
return tier;
|
|
79488
|
+
}
|
|
79489
|
+
}
|
|
79490
|
+
return "ultra";
|
|
79491
|
+
}
|
|
79492
|
+
var TOKEN_UNIT_PRICE = 1e-5;
|
|
79493
|
+
|
|
79494
|
+
// packages/daemon/src/models/registry.ts
|
|
79495
|
+
function normalizeModelName(name) {
|
|
79496
|
+
return name.toLowerCase().replace(/^openai\s+/i, "").replace(/\s+/g, "-").replace(/[^a-z0-9.\-]/g, "").trim();
|
|
79497
|
+
}
|
|
79498
|
+
async function fetchCatalogIntoMap(modelMap, result, logger) {
|
|
79499
|
+
try {
|
|
79500
|
+
const catalogModels = await fetchModelCatalog();
|
|
79501
|
+
result.catalogFetched = true;
|
|
79502
|
+
for (const m of catalogModels) {
|
|
79503
|
+
const key = normalizeModelName(m.id);
|
|
79504
|
+
modelMap.set(key, { id: m.id, displayName: m.displayName, available: true });
|
|
79505
|
+
}
|
|
79506
|
+
} catch (error51) {
|
|
79507
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
79508
|
+
result.errors.push(`Catalog fetch failed: ${msg}`);
|
|
79509
|
+
logger?.warn(`Model catalog fetch failed: ${msg}`);
|
|
79510
|
+
}
|
|
79511
|
+
}
|
|
79512
|
+
async function scrapeTokenPricingIntoMap(modelMap, result, logger) {
|
|
79513
|
+
try {
|
|
79514
|
+
const tokenPricing = await scrapeTokenUnitPricing();
|
|
79515
|
+
result.tokenPricingScraped = true;
|
|
79516
|
+
for (const tp of tokenPricing) {
|
|
79517
|
+
const key = normalizeModelName(tp.modelName);
|
|
79518
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
79519
|
+
if (existing) {
|
|
79520
|
+
existing.tokenInputMultiplier = tp.inputMultiplier;
|
|
79521
|
+
existing.tokenOutputMultiplier = tp.outputMultiplier;
|
|
79522
|
+
existing.cachedInputMultiplier = tp.cachedInputMultiplier;
|
|
79523
|
+
} else {
|
|
79524
|
+
modelMap.set(key, {
|
|
79525
|
+
id: key,
|
|
79526
|
+
displayName: tp.modelName,
|
|
79527
|
+
tokenInputMultiplier: tp.inputMultiplier,
|
|
79528
|
+
tokenOutputMultiplier: tp.outputMultiplier,
|
|
79529
|
+
cachedInputMultiplier: tp.cachedInputMultiplier,
|
|
79530
|
+
available: true
|
|
79531
|
+
});
|
|
79532
|
+
}
|
|
79533
|
+
}
|
|
79534
|
+
} catch (error51) {
|
|
79535
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
79536
|
+
result.errors.push(`Token pricing scrape failed: ${msg}`);
|
|
79537
|
+
logger?.warn(`Token pricing scrape failed: ${msg}`);
|
|
79538
|
+
}
|
|
79539
|
+
}
|
|
79540
|
+
async function scrapePremiumPricingIntoMap(modelMap, result, logger) {
|
|
79541
|
+
try {
|
|
79542
|
+
const premiumPricing = await scrapePremiumRequestPricing();
|
|
79543
|
+
result.premiumPricingScraped = true;
|
|
79544
|
+
for (const pp of premiumPricing) {
|
|
79545
|
+
const key = normalizeModelName(pp.modelName);
|
|
79546
|
+
const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
|
|
79547
|
+
if (existing) {
|
|
79548
|
+
existing.premiumMultiplier = pp.multiplier;
|
|
79549
|
+
} else {
|
|
79550
|
+
modelMap.set(key, {
|
|
79551
|
+
id: key,
|
|
79552
|
+
displayName: pp.modelName,
|
|
79553
|
+
premiumMultiplier: pp.multiplier,
|
|
79554
|
+
available: true
|
|
79555
|
+
});
|
|
79556
|
+
}
|
|
79557
|
+
}
|
|
79558
|
+
} catch (error51) {
|
|
79559
|
+
const msg = error51 instanceof Error ? error51.message : String(error51);
|
|
79560
|
+
result.errors.push(`Premium pricing scrape failed: ${msg}`);
|
|
79561
|
+
logger?.warn(`Premium pricing scrape failed: ${msg}`);
|
|
79562
|
+
}
|
|
79563
|
+
}
|
|
79564
|
+
async function refreshModelPricing(logger) {
|
|
79565
|
+
const result = {
|
|
79566
|
+
modelsUpdated: 0,
|
|
79567
|
+
catalogFetched: false,
|
|
79568
|
+
tokenPricingScraped: false,
|
|
79569
|
+
premiumPricingScraped: false,
|
|
79570
|
+
errors: []
|
|
79571
|
+
};
|
|
79572
|
+
const modelMap = /* @__PURE__ */ new Map();
|
|
79573
|
+
await fetchCatalogIntoMap(modelMap, result, logger);
|
|
79574
|
+
await scrapeTokenPricingIntoMap(modelMap, result, logger);
|
|
79575
|
+
await scrapePremiumPricingIntoMap(modelMap, result, logger);
|
|
79576
|
+
if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped) {
|
|
79577
|
+
logger?.warn("All pricing sources failed, using seed data");
|
|
79578
|
+
await seedFromFallback();
|
|
79579
|
+
result.modelsUpdated = SEED_MODELS.length;
|
|
79580
|
+
return result;
|
|
79581
|
+
}
|
|
79582
|
+
const db = await getDatabase();
|
|
79583
|
+
const now = nowIso();
|
|
79584
|
+
for (const model of modelMap.values()) {
|
|
79585
|
+
const tier = computeTierFromMultiplier(model.premiumMultiplier ?? null);
|
|
79586
|
+
await upsertModel(db, {
|
|
79587
|
+
id: model.id,
|
|
79588
|
+
displayName: model.displayName,
|
|
79589
|
+
premiumMultiplier: model.premiumMultiplier ?? null,
|
|
79590
|
+
tokenInputMultiplier: model.tokenInputMultiplier ?? null,
|
|
79591
|
+
tokenOutputMultiplier: model.tokenOutputMultiplier ?? null,
|
|
79592
|
+
cachedInputMultiplier: model.cachedInputMultiplier ?? null,
|
|
79593
|
+
tier,
|
|
79594
|
+
available: model.available ?? true,
|
|
79595
|
+
updatedAt: now
|
|
79596
|
+
});
|
|
79597
|
+
result.modelsUpdated++;
|
|
79598
|
+
}
|
|
79599
|
+
return result;
|
|
79600
|
+
}
|
|
79601
|
+
async function seedFromFallback() {
|
|
79602
|
+
const db = await getDatabase();
|
|
79603
|
+
const now = nowIso();
|
|
79604
|
+
for (const model of SEED_MODELS) {
|
|
79605
|
+
await upsertModel(db, { ...model, updatedAt: now });
|
|
79606
|
+
}
|
|
79607
|
+
}
|
|
79608
|
+
async function getModelPricing(modelId) {
|
|
79609
|
+
const db = await getDatabase();
|
|
79610
|
+
const result = await db.execute({
|
|
79611
|
+
sql: "SELECT * FROM model_pricing WHERE id = ?",
|
|
79612
|
+
args: [modelId]
|
|
79613
|
+
});
|
|
79614
|
+
if (result.rows.length === 0) {
|
|
79615
|
+
return null;
|
|
79616
|
+
}
|
|
79617
|
+
return rowToModelPricing(result.rows[0]);
|
|
79618
|
+
}
|
|
79619
|
+
function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
|
|
79620
|
+
if (inputMultiplier === null || outputMultiplier === null) {
|
|
79621
|
+
return 0;
|
|
79622
|
+
}
|
|
79623
|
+
const tokenUnits = inputTokens * inputMultiplier + outputTokens * outputMultiplier;
|
|
79624
|
+
return tokenUnits * TOKEN_UNIT_PRICE;
|
|
79625
|
+
}
|
|
79626
|
+
async function upsertModel(db, model) {
|
|
79627
|
+
await db.execute({
|
|
79628
|
+
sql: `INSERT INTO model_pricing (id, display_name, premium_multiplier, token_input_multiplier, token_output_multiplier, cached_input_multiplier, tier, available, updated_at)
|
|
79629
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
79630
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
79631
|
+
display_name = excluded.display_name,
|
|
79632
|
+
premium_multiplier = COALESCE(excluded.premium_multiplier, model_pricing.premium_multiplier),
|
|
79633
|
+
token_input_multiplier = COALESCE(excluded.token_input_multiplier, model_pricing.token_input_multiplier),
|
|
79634
|
+
token_output_multiplier = COALESCE(excluded.token_output_multiplier, model_pricing.token_output_multiplier),
|
|
79635
|
+
cached_input_multiplier = COALESCE(excluded.cached_input_multiplier, model_pricing.cached_input_multiplier),
|
|
79636
|
+
tier = excluded.tier,
|
|
79637
|
+
available = excluded.available,
|
|
79638
|
+
updated_at = excluded.updated_at`,
|
|
79639
|
+
args: [
|
|
79640
|
+
model.id,
|
|
79641
|
+
model.displayName,
|
|
79642
|
+
model.premiumMultiplier,
|
|
79643
|
+
model.tokenInputMultiplier,
|
|
79644
|
+
model.tokenOutputMultiplier,
|
|
79645
|
+
model.cachedInputMultiplier,
|
|
79646
|
+
model.tier,
|
|
79647
|
+
model.available ? 1 : 0,
|
|
79648
|
+
model.updatedAt
|
|
79649
|
+
]
|
|
79650
|
+
});
|
|
79651
|
+
}
|
|
79652
|
+
function rowToModelPricing(row) {
|
|
79653
|
+
return {
|
|
79654
|
+
id: asString(row.id),
|
|
79655
|
+
displayName: asString(row.display_name),
|
|
79656
|
+
premiumMultiplier: asNullableNumber(row.premium_multiplier),
|
|
79657
|
+
tokenInputMultiplier: asNullableNumber(row.token_input_multiplier),
|
|
79658
|
+
tokenOutputMultiplier: asNullableNumber(row.token_output_multiplier),
|
|
79659
|
+
cachedInputMultiplier: asNullableNumber(row.cached_input_multiplier),
|
|
79660
|
+
tier: asString(row.tier),
|
|
79661
|
+
available: asNumber(row.available) === 1,
|
|
79662
|
+
updatedAt: asString(row.updated_at)
|
|
79663
|
+
};
|
|
79664
|
+
}
|
|
79665
|
+
function findClosestKey(map2, targetKey) {
|
|
79666
|
+
for (const [key, value] of map2) {
|
|
79667
|
+
if (key.includes(targetKey) || targetKey.includes(key)) {
|
|
79668
|
+
return value;
|
|
79669
|
+
}
|
|
79670
|
+
}
|
|
79671
|
+
return void 0;
|
|
79672
|
+
}
|
|
79673
|
+
|
|
79111
79674
|
// packages/daemon/src/orchestrator/system-prompt.ts
|
|
79112
79675
|
function formatSquadRoster(squads) {
|
|
79113
79676
|
if (squads.length === 0) {
|
|
@@ -79200,146 +79763,13 @@ function buildFreshSessionContext(summary, wikiContext) {
|
|
|
79200
79763
|
return sections.join("\n\n");
|
|
79201
79764
|
}
|
|
79202
79765
|
|
|
79203
|
-
// packages/daemon/src/copilot/router.ts
|
|
79204
|
-
var FAST_KEYWORDS = [
|
|
79205
|
-
"hi",
|
|
79206
|
-
"hello",
|
|
79207
|
-
"hey",
|
|
79208
|
-
"thanks",
|
|
79209
|
-
"thank you",
|
|
79210
|
-
"status",
|
|
79211
|
-
"list",
|
|
79212
|
-
"show",
|
|
79213
|
-
"ping",
|
|
79214
|
-
"uptime",
|
|
79215
|
-
"what time",
|
|
79216
|
-
"ok",
|
|
79217
|
-
"okay"
|
|
79218
|
-
];
|
|
79219
|
-
var PREMIUM_KEYWORDS = [
|
|
79220
|
-
"architecture",
|
|
79221
|
-
"architect",
|
|
79222
|
-
"design",
|
|
79223
|
-
"tradeoff",
|
|
79224
|
-
"trade-off",
|
|
79225
|
-
"plan",
|
|
79226
|
-
"planning",
|
|
79227
|
-
"strategy",
|
|
79228
|
-
"roadmap",
|
|
79229
|
-
"migration",
|
|
79230
|
-
"refactor",
|
|
79231
|
-
"generate",
|
|
79232
|
-
"implement",
|
|
79233
|
-
"build",
|
|
79234
|
-
"codebase",
|
|
79235
|
-
"repository",
|
|
79236
|
-
"system prompt",
|
|
79237
|
-
"multi-step",
|
|
79238
|
-
"complex",
|
|
79239
|
-
"reason",
|
|
79240
|
-
"compare"
|
|
79241
|
-
];
|
|
79242
|
-
var SIMPLE_QUESTION_PREFIXES = [
|
|
79243
|
-
"is",
|
|
79244
|
-
"are",
|
|
79245
|
-
"do",
|
|
79246
|
-
"does",
|
|
79247
|
-
"did",
|
|
79248
|
-
"can",
|
|
79249
|
-
"could",
|
|
79250
|
-
"should",
|
|
79251
|
-
"would",
|
|
79252
|
-
"will"
|
|
79253
|
-
];
|
|
79254
|
-
var MODEL_SWITCH_COOLDOWN_MESSAGES = 3;
|
|
79255
|
-
var activeTier = null;
|
|
79256
|
-
var messagesSinceLastSwitch = MODEL_SWITCH_COOLDOWN_MESSAGES;
|
|
79257
|
-
function normalizeMessage(message2) {
|
|
79258
|
-
return message2.trim().toLowerCase();
|
|
79259
|
-
}
|
|
79260
|
-
function getWordCount(message2) {
|
|
79261
|
-
const normalized = message2.trim();
|
|
79262
|
-
if (normalized.length === 0) {
|
|
79263
|
-
return 0;
|
|
79264
|
-
}
|
|
79265
|
-
return normalized.split(/\s+/u).length;
|
|
79266
|
-
}
|
|
79267
|
-
function containsAny(message2, keywords) {
|
|
79268
|
-
return keywords.some((keyword) => message2.includes(keyword));
|
|
79269
|
-
}
|
|
79270
|
-
function startsWithSimpleQuestion(message2) {
|
|
79271
|
-
return SIMPLE_QUESTION_PREFIXES.some(
|
|
79272
|
-
(prefix) => message2 === prefix || message2.startsWith(`${prefix} `)
|
|
79273
|
-
);
|
|
79274
|
-
}
|
|
79275
|
-
function classifyMessage(message2) {
|
|
79276
|
-
const normalized = normalizeMessage(message2);
|
|
79277
|
-
const wordCount = getWordCount(normalized);
|
|
79278
|
-
if (normalized.length === 0) {
|
|
79279
|
-
return "fast";
|
|
79280
|
-
}
|
|
79281
|
-
if (containsAny(normalized, PREMIUM_KEYWORDS) || normalized.includes("```") || wordCount >= 80) {
|
|
79282
|
-
return "premium";
|
|
79283
|
-
}
|
|
79284
|
-
if (wordCount <= 20 && (containsAny(normalized, FAST_KEYWORDS) || startsWithSimpleQuestion(normalized) || /^(yes|no|sure|okay|ok|thanks)[!.?]*$/u.test(normalized))) {
|
|
79285
|
-
return "fast";
|
|
79286
|
-
}
|
|
79287
|
-
if (wordCount <= 8 && /^(what|when|where|who)\b/u.test(normalized)) {
|
|
79288
|
-
return "fast";
|
|
79289
|
-
}
|
|
79290
|
-
return "standard";
|
|
79291
|
-
}
|
|
79292
|
-
function getModelForTier(tier) {
|
|
79293
|
-
switch (tier) {
|
|
79294
|
-
case "fast":
|
|
79295
|
-
return FAST_MODEL;
|
|
79296
|
-
case "premium":
|
|
79297
|
-
return PREMIUM_MODEL;
|
|
79298
|
-
default:
|
|
79299
|
-
return STANDARD_MODEL;
|
|
79300
|
-
}
|
|
79301
|
-
}
|
|
79302
|
-
function routeMessage(message2) {
|
|
79303
|
-
const requestedTier = classifyMessage(message2);
|
|
79304
|
-
if (activeTier === null) {
|
|
79305
|
-
activeTier = requestedTier;
|
|
79306
|
-
messagesSinceLastSwitch = 0;
|
|
79307
|
-
return {
|
|
79308
|
-
requestedTier,
|
|
79309
|
-
effectiveTier: activeTier,
|
|
79310
|
-
model: getModelForTier(activeTier),
|
|
79311
|
-
switched: true,
|
|
79312
|
-
cooldownRemaining: MODEL_SWITCH_COOLDOWN_MESSAGES
|
|
79313
|
-
};
|
|
79314
|
-
}
|
|
79315
|
-
if (requestedTier !== activeTier && messagesSinceLastSwitch >= MODEL_SWITCH_COOLDOWN_MESSAGES) {
|
|
79316
|
-
activeTier = requestedTier;
|
|
79317
|
-
messagesSinceLastSwitch = 0;
|
|
79318
|
-
return {
|
|
79319
|
-
requestedTier,
|
|
79320
|
-
effectiveTier: activeTier,
|
|
79321
|
-
model: getModelForTier(activeTier),
|
|
79322
|
-
switched: true,
|
|
79323
|
-
cooldownRemaining: MODEL_SWITCH_COOLDOWN_MESSAGES
|
|
79324
|
-
};
|
|
79325
|
-
}
|
|
79326
|
-
messagesSinceLastSwitch += 1;
|
|
79327
|
-
return {
|
|
79328
|
-
requestedTier,
|
|
79329
|
-
effectiveTier: activeTier,
|
|
79330
|
-
model: getModelForTier(activeTier),
|
|
79331
|
-
switched: false,
|
|
79332
|
-
cooldownRemaining: Math.max(MODEL_SWITCH_COOLDOWN_MESSAGES - messagesSinceLastSwitch, 0)
|
|
79333
|
-
};
|
|
79334
|
-
}
|
|
79335
|
-
|
|
79336
79766
|
// packages/daemon/src/copilot/session.ts
|
|
79337
79767
|
import {
|
|
79338
79768
|
approveAll
|
|
79339
79769
|
} from "@github/copilot-sdk";
|
|
79340
79770
|
|
|
79341
79771
|
// packages/daemon/src/copilot/client.ts
|
|
79342
|
-
import { execFileSync } from "node:child_process";
|
|
79772
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
79343
79773
|
import { mkdirSync as mkdirSync2 } from "node:fs";
|
|
79344
79774
|
import { join as join6 } from "node:path";
|
|
79345
79775
|
import { CopilotClient } from "@github/copilot-sdk";
|
|
@@ -79353,7 +79783,7 @@ function readTokenFromEnvironment() {
|
|
|
79353
79783
|
}
|
|
79354
79784
|
function readTokenFromGhCli() {
|
|
79355
79785
|
try {
|
|
79356
|
-
const token =
|
|
79786
|
+
const token = execFileSync2("gh", ["auth", "token"], {
|
|
79357
79787
|
encoding: "utf8",
|
|
79358
79788
|
stdio: ["ignore", "pipe", "pipe"]
|
|
79359
79789
|
}).trim();
|
|
@@ -79362,7 +79792,7 @@ function readTokenFromGhCli() {
|
|
|
79362
79792
|
return void 0;
|
|
79363
79793
|
}
|
|
79364
79794
|
}
|
|
79365
|
-
function
|
|
79795
|
+
function resolveGitHubToken2() {
|
|
79366
79796
|
return readTokenFromEnvironment() ?? readTokenFromGhCli();
|
|
79367
79797
|
}
|
|
79368
79798
|
function buildClientOptions(token) {
|
|
@@ -79398,7 +79828,7 @@ async function initCopilotClient() {
|
|
|
79398
79828
|
}
|
|
79399
79829
|
copilotClientInitPromise = (async () => {
|
|
79400
79830
|
try {
|
|
79401
|
-
const token =
|
|
79831
|
+
const token = resolveGitHubToken2();
|
|
79402
79832
|
if (token === void 0) {
|
|
79403
79833
|
throw new Error(
|
|
79404
79834
|
`Unable to find a GitHub token for Copilot SDK authentication. ${COPILOT_AUTH_ERROR_HINT}`
|
|
@@ -80226,13 +80656,24 @@ function mergeUsage(target, usage) {
|
|
|
80226
80656
|
}
|
|
80227
80657
|
async function persistUsage(member, usageEvents) {
|
|
80228
80658
|
for (const usage of usageEvents) {
|
|
80659
|
+
const model = usage.model;
|
|
80660
|
+
const pricing = await getModelPricing(model);
|
|
80661
|
+
const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
|
|
80662
|
+
const tokenUnitCost = pricing ? calculateTokenUnitCost(
|
|
80663
|
+
usage.inputTokens ?? 0,
|
|
80664
|
+
usage.outputTokens ?? 0,
|
|
80665
|
+
pricing.tokenInputMultiplier,
|
|
80666
|
+
pricing.tokenOutputMultiplier
|
|
80667
|
+
) : 0;
|
|
80229
80668
|
await recordUsage({
|
|
80230
80669
|
squadId: member.squadId,
|
|
80231
80670
|
agentId: member.id,
|
|
80232
|
-
model
|
|
80671
|
+
model,
|
|
80233
80672
|
inputTokens: usage.inputTokens ?? 0,
|
|
80234
80673
|
outputTokens: usage.outputTokens ?? 0,
|
|
80235
|
-
cost:
|
|
80674
|
+
cost: 0,
|
|
80675
|
+
premiumRequestCost,
|
|
80676
|
+
tokenUnitCost
|
|
80236
80677
|
});
|
|
80237
80678
|
}
|
|
80238
80679
|
}
|
|
@@ -80450,7 +80891,7 @@ async function executeAgentTask(member, task, worktreePath, options2) {
|
|
|
80450
80891
|
client2 = new CopilotClient2({ workingDirectory: worktreePath });
|
|
80451
80892
|
await client2.start();
|
|
80452
80893
|
const session = await client2.createSession({
|
|
80453
|
-
model: member.model ??
|
|
80894
|
+
model: member.model ?? DEFAULT_MODEL,
|
|
80454
80895
|
workingDirectory: worktreePath,
|
|
80455
80896
|
tools,
|
|
80456
80897
|
availableTools: ["custom:*"],
|
|
@@ -80691,7 +81132,7 @@ Return strict JSON in this shape:
|
|
|
80691
81132
|
client2 = new CopilotClient3({ workingDirectory: repoPath });
|
|
80692
81133
|
await client2.start();
|
|
80693
81134
|
const session = await client2.createSession({
|
|
80694
|
-
model:
|
|
81135
|
+
model: DEFAULT_MODEL,
|
|
80695
81136
|
workingDirectory: repoPath,
|
|
80696
81137
|
onPermissionRequest: approveAll3,
|
|
80697
81138
|
systemMessage: {
|
|
@@ -80962,7 +81403,7 @@ Return strict JSON:
|
|
|
80962
81403
|
client2 = new CopilotClient4({ workingDirectory: worktreePath });
|
|
80963
81404
|
await client2.start();
|
|
80964
81405
|
const session = await client2.createSession({
|
|
80965
|
-
model: qaMember.model ??
|
|
81406
|
+
model: qaMember.model ?? DEFAULT_MODEL,
|
|
80966
81407
|
workingDirectory: worktreePath,
|
|
80967
81408
|
onPermissionRequest: approveAll4,
|
|
80968
81409
|
systemMessage: {
|
|
@@ -81082,7 +81523,7 @@ Return strict JSON:
|
|
|
81082
81523
|
client2 = new CopilotClient5();
|
|
81083
81524
|
await client2.start();
|
|
81084
81525
|
const session = await client2.createSession({
|
|
81085
|
-
model: teamLead.model ??
|
|
81526
|
+
model: teamLead.model ?? DEFAULT_MODEL,
|
|
81086
81527
|
onPermissionRequest: approveAll5,
|
|
81087
81528
|
systemMessage: {
|
|
81088
81529
|
content: `${TEAM_LEAD_PROMPT}
|
|
@@ -81537,12 +81978,9 @@ function buildMandatoryRoles() {
|
|
|
81537
81978
|
function modelForRole(role) {
|
|
81538
81979
|
const normalized = slugifyRole(role);
|
|
81539
81980
|
if (normalized === "team-lead") {
|
|
81540
|
-
return
|
|
81541
|
-
}
|
|
81542
|
-
if (normalized === "qa") {
|
|
81543
|
-
return STANDARD_MODEL;
|
|
81981
|
+
return DEFAULT_MODEL;
|
|
81544
81982
|
}
|
|
81545
|
-
return
|
|
81983
|
+
return DEFAULT_MODEL;
|
|
81546
81984
|
}
|
|
81547
81985
|
function systemPromptForRole(role, repoContext) {
|
|
81548
81986
|
const normalized = slugifyRole(role);
|
|
@@ -81574,7 +82012,7 @@ ${ROLE_GENERATION_PROMPT}`;
|
|
|
81574
82012
|
client2 = new CopilotClient6();
|
|
81575
82013
|
await client2.start();
|
|
81576
82014
|
const session = await client2.createSession({
|
|
81577
|
-
model:
|
|
82015
|
+
model: DEFAULT_MODEL,
|
|
81578
82016
|
onPermissionRequest: approveAll6,
|
|
81579
82017
|
systemMessage: {
|
|
81580
82018
|
content: ROLE_GENERATION_PROMPT
|
|
@@ -82048,8 +82486,7 @@ var Orchestrator = class {
|
|
|
82048
82486
|
skillsContext,
|
|
82049
82487
|
conversationSummary: [this.latestResetSummary, conversationSummary].filter(Boolean).join("\n\n")
|
|
82050
82488
|
});
|
|
82051
|
-
|
|
82052
|
-
await this.refreshSession(route.model, systemPrompt);
|
|
82489
|
+
await this.refreshSession(this.config.defaultModel, systemPrompt);
|
|
82053
82490
|
const sendResult = await sendMessage(
|
|
82054
82491
|
this.requireActiveSession(),
|
|
82055
82492
|
normalizedMessage,
|
|
@@ -82177,11 +82614,22 @@ var Orchestrator = class {
|
|
|
82177
82614
|
if (usage.inputTokens === 0 && usage.outputTokens === 0) {
|
|
82178
82615
|
return;
|
|
82179
82616
|
}
|
|
82617
|
+
const model = usage.model || this.activeModel || this.config.defaultModel;
|
|
82618
|
+
const pricing = await getModelPricing(model);
|
|
82619
|
+
const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
|
|
82620
|
+
const tokenUnitCost = pricing ? calculateTokenUnitCost(
|
|
82621
|
+
usage.inputTokens,
|
|
82622
|
+
usage.outputTokens,
|
|
82623
|
+
pricing.tokenInputMultiplier,
|
|
82624
|
+
pricing.tokenOutputMultiplier
|
|
82625
|
+
) : 0;
|
|
82180
82626
|
await recordUsage({
|
|
82181
|
-
model
|
|
82627
|
+
model,
|
|
82182
82628
|
inputTokens: usage.inputTokens,
|
|
82183
82629
|
outputTokens: usage.outputTokens,
|
|
82184
|
-
cost: 0
|
|
82630
|
+
cost: 0,
|
|
82631
|
+
premiumRequestCost,
|
|
82632
|
+
tokenUnitCost
|
|
82185
82633
|
});
|
|
82186
82634
|
}
|
|
82187
82635
|
};
|
|
@@ -82478,6 +82926,7 @@ function registerShutdownHandlers(logger, onShutdown) {
|
|
|
82478
82926
|
}
|
|
82479
82927
|
async function main() {
|
|
82480
82928
|
let logger;
|
|
82929
|
+
let pricingRefreshTimer = null;
|
|
82481
82930
|
try {
|
|
82482
82931
|
ensureDataDirectories();
|
|
82483
82932
|
const config2 = loadConfig();
|
|
@@ -82485,6 +82934,12 @@ async function main() {
|
|
|
82485
82934
|
logger.info(`IO Daemon v${APP_VERSION} starting...`);
|
|
82486
82935
|
await initDatabase();
|
|
82487
82936
|
logger.info("Database initialized");
|
|
82937
|
+
const pricingResult = await refreshModelPricing(logger);
|
|
82938
|
+
if (pricingResult.modelsUpdated === 0) {
|
|
82939
|
+
logger.warn("Model pricing refresh returned 0 models, seeding with fallback");
|
|
82940
|
+
await seedFromFallback();
|
|
82941
|
+
}
|
|
82942
|
+
logger.info({ modelsUpdated: pricingResult.modelsUpdated }, "Model pricing initialized");
|
|
82488
82943
|
await scanSkills();
|
|
82489
82944
|
logger.info("Skills scanned");
|
|
82490
82945
|
const orchestrator2 = createOrchestrator(config2, eventBus);
|
|
@@ -82493,12 +82948,21 @@ async function main() {
|
|
|
82493
82948
|
const scheduler = createScheduler(orchestrator2, eventBus);
|
|
82494
82949
|
scheduler.start();
|
|
82495
82950
|
logger.info("Scheduler started");
|
|
82951
|
+
const refreshIntervalMs = config2.pricingRefreshHours * 60 * 60 * 1e3;
|
|
82952
|
+
pricingRefreshTimer = setInterval(() => {
|
|
82953
|
+
void refreshModelPricing(logger).catch((err) => {
|
|
82954
|
+
logger?.warn({ err }, "Periodic model pricing refresh failed");
|
|
82955
|
+
});
|
|
82956
|
+
}, refreshIntervalMs);
|
|
82496
82957
|
setChatOrchestrator(orchestrator2);
|
|
82497
82958
|
const apiServer = createApiServer(config2);
|
|
82498
82959
|
const telegramBot = createTelegramBot(config2, orchestrator2);
|
|
82499
82960
|
telegramBot?.start();
|
|
82500
82961
|
createTelegramNotifier(telegramBot, config2, eventBus);
|
|
82501
82962
|
registerShutdownHandlers(logger, async () => {
|
|
82963
|
+
if (pricingRefreshTimer) {
|
|
82964
|
+
clearInterval(pricingRefreshTimer);
|
|
82965
|
+
}
|
|
82502
82966
|
scheduler.stop();
|
|
82503
82967
|
telegramBot?.stop();
|
|
82504
82968
|
apiServer.server.close();
|