heyio 4.0.3 → 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.
@@ -75,18 +75,15 @@ var init_api = __esm({
75
75
  });
76
76
 
77
77
  // packages/shared/dist/constants.js
78
- var APP_NAME, APP_VERSION, API_PORT, API_HOST, DEFAULT_MODEL, FAST_MODEL, STANDARD_MODEL, PREMIUM_MODEL, SESSION_RESET_THRESHOLD, SCHEDULER_INTERVAL_MS, QA_MAX_REVISIONS, MANDATORY_ROLES, EVENT_NAMES;
78
+ var APP_NAME, APP_VERSION, API_PORT, API_HOST, DEFAULT_MODEL, SESSION_RESET_THRESHOLD, SCHEDULER_INTERVAL_MS, QA_MAX_REVISIONS, MANDATORY_ROLES, EVENT_NAMES;
79
79
  var init_constants = __esm({
80
80
  "packages/shared/dist/constants.js"() {
81
81
  "use strict";
82
82
  APP_NAME = "io";
83
- APP_VERSION = "4.0.0";
83
+ APP_VERSION = "4.0.4";
84
84
  API_PORT = 7777;
85
85
  API_HOST = "0.0.0.0";
86
- DEFAULT_MODEL = "gpt-4.1";
87
- FAST_MODEL = "gpt-4.1-mini";
88
- STANDARD_MODEL = "claude-sonnet-4.6";
89
- PREMIUM_MODEL = "claude-sonnet-4.6";
86
+ DEFAULT_MODEL = "gpt-4o";
90
87
  SESSION_RESET_THRESHOLD = 50;
91
88
  SCHEDULER_INTERVAL_MS = 6e4;
92
89
  QA_MAX_REVISIONS = 3;
@@ -2067,6 +2064,27 @@ var init_db = __esm({
2067
2064
  "CREATE INDEX IF NOT EXISTS idx_activity_objective_id ON activity(objective_id)",
2068
2065
  "CREATE INDEX IF NOT EXISTS idx_agent_history_agent_id_created_at ON agent_history(agent_id, created_at)"
2069
2066
  ]
2067
+ },
2068
+ {
2069
+ version: 2,
2070
+ name: "add-model-pricing-and-dual-costs",
2071
+ statements: [
2072
+ `CREATE TABLE IF NOT EXISTS model_pricing (
2073
+ id TEXT PRIMARY KEY,
2074
+ display_name TEXT NOT NULL,
2075
+ premium_multiplier REAL,
2076
+ token_input_multiplier REAL,
2077
+ token_output_multiplier REAL,
2078
+ cached_input_multiplier REAL,
2079
+ tier TEXT NOT NULL,
2080
+ available INTEGER NOT NULL DEFAULT 1,
2081
+ updated_at TEXT NOT NULL
2082
+ )`,
2083
+ "CREATE INDEX IF NOT EXISTS idx_model_pricing_tier ON model_pricing(tier)",
2084
+ "CREATE INDEX IF NOT EXISTS idx_model_pricing_available ON model_pricing(available)",
2085
+ "ALTER TABLE token_usage ADD COLUMN premium_request_cost REAL",
2086
+ "ALTER TABLE token_usage ADD COLUMN token_unit_cost REAL"
2087
+ ]
2070
2088
  }
2071
2089
  ];
2072
2090
  client = null;
@@ -11817,8 +11835,8 @@ async function recordUsage(data, db) {
11817
11835
  createdAt: data.createdAt ?? nowIso()
11818
11836
  };
11819
11837
  await database.execute({
11820
- sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, created_at)
11821
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
11838
+ sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, premium_request_cost, token_unit_cost, created_at)
11839
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
11822
11840
  args: [
11823
11841
  usage.id,
11824
11842
  usage.squadId,
@@ -11827,6 +11845,8 @@ async function recordUsage(data, db) {
11827
11845
  usage.inputTokens,
11828
11846
  usage.outputTokens,
11829
11847
  usage.cost,
11848
+ data.premiumRequestCost ?? null,
11849
+ data.tokenUnitCost ?? null,
11830
11850
  usage.createdAt
11831
11851
  ]
11832
11852
  });
@@ -51543,6 +51563,9 @@ function readEnvOverrides() {
51543
51563
  if (process.env.IO_SESSION_RESET_THRESHOLD !== void 0) {
51544
51564
  overrides.sessionResetThreshold = process.env.IO_SESSION_RESET_THRESHOLD;
51545
51565
  }
51566
+ if (process.env.IO_PRICING_REFRESH_HOURS !== void 0) {
51567
+ overrides.pricingRefreshHours = process.env.IO_PRICING_REFRESH_HOURS;
51568
+ }
51546
51569
  return overrides;
51547
51570
  }
51548
51571
  function loadConfig() {
@@ -51569,7 +51592,8 @@ var init_config = __esm({
51569
51592
  telegramUserId: external_exports.string().trim().min(1).nullable().default(null),
51570
51593
  supabaseUrl: external_exports.string().trim().min(1).nullable().default(null),
51571
51594
  supabaseAnonKey: external_exports.string().trim().min(1).nullable().default(null),
51572
- sessionResetThreshold: external_exports.coerce.number().int().positive().default(SESSION_RESET_THRESHOLD)
51595
+ sessionResetThreshold: external_exports.coerce.number().int().positive().default(SESSION_RESET_THRESHOLD),
51596
+ pricingRefreshHours: external_exports.coerce.number().positive().default(24)
51573
51597
  });
51574
51598
  }
51575
51599
  });
@@ -51650,7 +51674,8 @@ var init_settings = __esm({
51650
51674
  "telegramUserId",
51651
51675
  "supabaseUrl",
51652
51676
  "supabaseAnonKey",
51653
- "sessionResetThreshold"
51677
+ "sessionResetThreshold",
51678
+ "pricingRefreshHours"
51654
51679
  ];
51655
51680
  router5.get("/api/settings", async (_req, res) => {
51656
51681
  try {
@@ -51795,7 +51820,78 @@ function chooseEntryFileName(url2, contentType) {
51795
51820
  function isMissingFileError2(error51) {
51796
51821
  return !!error51 && typeof error51 === "object" && "code" in error51 && error51.code === "ENOENT";
51797
51822
  }
51798
- var import_express6, router6;
51823
+ async function discoverSkillsSh(query) {
51824
+ if (!query) return [];
51825
+ const endpoint = `https://skills.sh/api/search?q=${encodeURIComponent(query)}&limit=50`;
51826
+ const response = await fetch(endpoint, { signal: AbortSignal.timeout(1e4) });
51827
+ if (!response.ok) {
51828
+ throw new Error(`skills.sh returned ${response.status}`);
51829
+ }
51830
+ const data = await response.json();
51831
+ const installedSkills = await readInstalledSkills();
51832
+ const installedIds = new Set(installedSkills.map((s) => s.id));
51833
+ return (data.skills ?? []).map((entry) => {
51834
+ const skillUrl = entry.source ? `https://raw.githubusercontent.com/${entry.source}/main/skills/${entry.skillId}/SKILL.md` : "";
51835
+ return {
51836
+ name: entry.name || entry.skillId,
51837
+ title: entry.name || entry.skillId,
51838
+ description: `${entry.source ?? ""}`,
51839
+ url: skillUrl,
51840
+ source: "skillssh",
51841
+ installed: installedIds.has(`skillssh:${normalizeSlug(entry.skillId || entry.name)}`),
51842
+ registrySource: entry.source ?? void 0,
51843
+ skillId: entry.skillId ?? void 0,
51844
+ installs: entry.installs ?? 0
51845
+ };
51846
+ });
51847
+ }
51848
+ async function discoverAwesomeCopilot(query) {
51849
+ const now = Date.now();
51850
+ if (!awesomeCopilotCache || now - awesomeCopilotCache.fetchedAt > AWESOME_COPILOT_CACHE_TTL) {
51851
+ awesomeCopilotCache = { skills: await fetchAwesomeCopilotList(), fetchedAt: now };
51852
+ }
51853
+ const skills = awesomeCopilotCache.skills;
51854
+ if (!query) return skills;
51855
+ const needle = query.toLowerCase();
51856
+ return skills.filter(
51857
+ (s) => s.name.toLowerCase().includes(needle) || s.title.toLowerCase().includes(needle) || s.description.toLowerCase().includes(needle)
51858
+ );
51859
+ }
51860
+ async function fetchAwesomeCopilotList() {
51861
+ const treeUrl = "https://api.github.com/repos/github/awesome-copilot/git/trees/main?recursive=1";
51862
+ const headers = {
51863
+ Accept: "application/vnd.github.v3+json",
51864
+ "User-Agent": "io-daemon"
51865
+ };
51866
+ if (process.env.GITHUB_TOKEN) {
51867
+ headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
51868
+ }
51869
+ const response = await fetch(treeUrl, { headers, signal: AbortSignal.timeout(15e3) });
51870
+ if (!response.ok) {
51871
+ throw new Error(`GitHub API returned ${response.status}`);
51872
+ }
51873
+ const data = await response.json();
51874
+ const installedSkills = await readInstalledSkills();
51875
+ const installedIds = new Set(installedSkills.map((s) => s.id));
51876
+ const skillEntries = (data.tree ?? []).filter(
51877
+ (entry) => entry.type === "blob" && entry.path.startsWith("skills/") && entry.path.endsWith("/SKILL.md")
51878
+ );
51879
+ return skillEntries.map((entry) => {
51880
+ const parts = entry.path.split("/");
51881
+ const skillName = parts[1] ?? "unknown";
51882
+ const slug = normalizeSlug(skillName);
51883
+ const rawUrl = `https://raw.githubusercontent.com/github/awesome-copilot/main/${entry.path}`;
51884
+ return {
51885
+ name: skillName,
51886
+ title: skillName.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
51887
+ description: "From github/awesome-copilot",
51888
+ url: rawUrl,
51889
+ source: "awesome-copilot",
51890
+ installed: installedIds.has(`awesome-copilot:${slug}`)
51891
+ };
51892
+ });
51893
+ }
51894
+ var import_express6, router6, awesomeCopilotCache, AWESOME_COPILOT_CACHE_TTL;
51799
51895
  var init_skills = __esm({
51800
51896
  "packages/daemon/src/api/routes/skills.ts"() {
51801
51897
  "use strict";
@@ -51844,9 +51940,28 @@ var init_skills = __esm({
51844
51940
  });
51845
51941
  }
51846
51942
  });
51847
- router6.get("/api/skills/discover", async (_req, res) => {
51848
- res.status(200).json([]);
51943
+ router6.get("/api/skills/discover", async (req, res) => {
51944
+ try {
51945
+ const source = typeof req.query.source === "string" ? req.query.source : "";
51946
+ const query = typeof req.query.q === "string" ? req.query.q.trim() : "";
51947
+ if (source === "skillssh") {
51948
+ const skills = await discoverSkillsSh(query);
51949
+ res.status(200).json(skills);
51950
+ } else if (source === "awesome-copilot") {
51951
+ const skills = await discoverAwesomeCopilot(query);
51952
+ res.status(200).json(skills);
51953
+ } else {
51954
+ res.status(400).json({ error: `Unknown source: ${source}` });
51955
+ }
51956
+ } catch (error51) {
51957
+ res.status(500).json({
51958
+ error: "Failed to discover skills",
51959
+ details: error51 instanceof Error ? error51.message : "Unknown error"
51960
+ });
51961
+ }
51849
51962
  });
51963
+ awesomeCopilotCache = null;
51964
+ AWESOME_COPILOT_CACHE_TTL = 60 * 60 * 1e3;
51850
51965
  }
51851
51966
  });
51852
51967
 
@@ -66900,6 +67015,493 @@ var init_logger = __esm({
66900
67015
  }
66901
67016
  });
66902
67017
 
67018
+ // packages/daemon/src/models/catalog.ts
67019
+ import { execFileSync } from "node:child_process";
67020
+ function resolveGitHubToken() {
67021
+ const envToken = process.env.GITHUB_TOKEN?.trim();
67022
+ if (envToken && envToken.length > 0) {
67023
+ return envToken;
67024
+ }
67025
+ try {
67026
+ const token = execFileSync("gh", ["auth", "token"], {
67027
+ encoding: "utf8",
67028
+ stdio: ["ignore", "pipe", "pipe"]
67029
+ }).trim();
67030
+ return token.length > 0 ? token : void 0;
67031
+ } catch {
67032
+ return void 0;
67033
+ }
67034
+ }
67035
+ async function fetchModelCatalog() {
67036
+ const token = resolveGitHubToken();
67037
+ if (!token) {
67038
+ throw new Error("No GitHub token available for model catalog fetch");
67039
+ }
67040
+ const response = await fetch(CATALOG_URL, {
67041
+ headers: {
67042
+ Accept: "application/json",
67043
+ Authorization: `Bearer ${token}`,
67044
+ "X-GitHub-Api-Version": "2024-12-01"
67045
+ }
67046
+ });
67047
+ if (!response.ok) {
67048
+ throw new Error(`Model catalog fetch failed: ${response.status} ${response.statusText}`);
67049
+ }
67050
+ const data = await response.json();
67051
+ if (!Array.isArray(data)) {
67052
+ throw new Error("Model catalog response is not an array");
67053
+ }
67054
+ const models = [];
67055
+ for (const entry of data) {
67056
+ const id = entry.id ?? entry.name;
67057
+ const displayName = entry.friendly_name ?? entry.name ?? id;
67058
+ if (id && typeof id === "string") {
67059
+ models.push({ id, displayName: displayName ?? id });
67060
+ }
67061
+ }
67062
+ return models;
67063
+ }
67064
+ var CATALOG_URL;
67065
+ var init_catalog = __esm({
67066
+ "packages/daemon/src/models/catalog.ts"() {
67067
+ "use strict";
67068
+ CATALOG_URL = "https://models.github.ai/catalog/models";
67069
+ }
67070
+ });
67071
+
67072
+ // packages/daemon/src/models/pricing-scraper.ts
67073
+ async function scrapeTokenUnitPricing() {
67074
+ const html = await fetchPage(TOKEN_UNIT_COSTS_URL);
67075
+ return parseTokenUnitTable(html);
67076
+ }
67077
+ async function scrapePremiumRequestPricing() {
67078
+ const html = await fetchPage(PREMIUM_MULTIPLIERS_URL);
67079
+ return parsePremiumMultiplierTable(html);
67080
+ }
67081
+ async function fetchPage(url2) {
67082
+ const response = await fetch(url2, {
67083
+ headers: {
67084
+ Accept: "text/html",
67085
+ "User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
67086
+ },
67087
+ redirect: "follow"
67088
+ });
67089
+ if (!response.ok) {
67090
+ throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
67091
+ }
67092
+ return response.text();
67093
+ }
67094
+ function parseTokenUnitTable(html) {
67095
+ const results = [];
67096
+ const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]*)<\/td>\s*<td[^>]*>([^<]+)<\/td>/gi;
67097
+ for (const match of html.matchAll(tableRowPattern)) {
67098
+ const modelName = cleanCellText(match[1]);
67099
+ const inputMultiplier = Number.parseFloat(match[2]);
67100
+ const cachedInput = match[3].trim().toLowerCase();
67101
+ const outputMultiplier = Number.parseFloat(match[4]);
67102
+ if (modelName && !Number.isNaN(inputMultiplier) && !Number.isNaN(outputMultiplier)) {
67103
+ results.push({
67104
+ modelName,
67105
+ inputMultiplier,
67106
+ cachedInputMultiplier: cachedInput === "n/a" || cachedInput === "" ? null : Number.parseFloat(cachedInput) || null,
67107
+ outputMultiplier
67108
+ });
67109
+ }
67110
+ }
67111
+ return results;
67112
+ }
67113
+ function parsePremiumMultiplierTable(html) {
67114
+ const results = [];
67115
+ const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<\/tr>/gi;
67116
+ for (const match of html.matchAll(tableRowPattern)) {
67117
+ const modelName = cleanCellText(match[1]);
67118
+ const multiplier = Number.parseFloat(match[2]);
67119
+ if (modelName && !Number.isNaN(multiplier) && multiplier > 0) {
67120
+ results.push({ modelName, multiplier });
67121
+ }
67122
+ }
67123
+ return results;
67124
+ }
67125
+ function cleanCellText(text) {
67126
+ return text.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " ").trim();
67127
+ }
67128
+ var TOKEN_UNIT_COSTS_URL, PREMIUM_MULTIPLIERS_URL;
67129
+ var init_pricing_scraper = __esm({
67130
+ "packages/daemon/src/models/pricing-scraper.ts"() {
67131
+ "use strict";
67132
+ TOKEN_UNIT_COSTS_URL = "https://docs.github.com/en/billing/reference/costs-for-github-models";
67133
+ PREMIUM_MULTIPLIERS_URL = "https://docs.github.com/en/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans";
67134
+ }
67135
+ });
67136
+
67137
+ // packages/daemon/src/models/seed.ts
67138
+ var SEED_MODELS;
67139
+ var init_seed = __esm({
67140
+ "packages/daemon/src/models/seed.ts"() {
67141
+ "use strict";
67142
+ SEED_MODELS = [
67143
+ {
67144
+ id: "gpt-4o-mini",
67145
+ displayName: "OpenAI GPT-4o mini",
67146
+ premiumMultiplier: 0.33,
67147
+ tokenInputMultiplier: 0.015,
67148
+ tokenOutputMultiplier: 0.06,
67149
+ cachedInputMultiplier: 75e-4,
67150
+ tier: "trivial",
67151
+ available: true
67152
+ },
67153
+ {
67154
+ id: "gpt-4o",
67155
+ displayName: "OpenAI GPT-4o",
67156
+ premiumMultiplier: 0.33,
67157
+ tokenInputMultiplier: 0.25,
67158
+ tokenOutputMultiplier: 1,
67159
+ cachedInputMultiplier: 0.125,
67160
+ tier: "trivial",
67161
+ available: true
67162
+ },
67163
+ {
67164
+ id: "gpt-5-mini",
67165
+ displayName: "GPT-5 mini",
67166
+ premiumMultiplier: 0.33,
67167
+ tokenInputMultiplier: null,
67168
+ tokenOutputMultiplier: null,
67169
+ cachedInputMultiplier: null,
67170
+ tier: "trivial",
67171
+ available: true
67172
+ },
67173
+ {
67174
+ id: "claude-sonnet-4",
67175
+ displayName: "Claude Sonnet 4",
67176
+ premiumMultiplier: 6,
67177
+ tokenInputMultiplier: 0.6,
67178
+ tokenOutputMultiplier: 2.4,
67179
+ cachedInputMultiplier: null,
67180
+ tier: "premium",
67181
+ available: true
67182
+ },
67183
+ {
67184
+ id: "claude-haiku-4.5",
67185
+ displayName: "Claude Haiku 4.5",
67186
+ premiumMultiplier: 0.33,
67187
+ tokenInputMultiplier: null,
67188
+ tokenOutputMultiplier: null,
67189
+ cachedInputMultiplier: null,
67190
+ tier: "trivial",
67191
+ available: true
67192
+ },
67193
+ {
67194
+ id: "gemini-2.5-pro",
67195
+ displayName: "Gemini 2.5 Pro",
67196
+ premiumMultiplier: 1,
67197
+ tokenInputMultiplier: null,
67198
+ tokenOutputMultiplier: null,
67199
+ cachedInputMultiplier: null,
67200
+ tier: "fast",
67201
+ available: true
67202
+ },
67203
+ {
67204
+ id: "gpt-5.1",
67205
+ displayName: "GPT-5.1",
67206
+ premiumMultiplier: 3,
67207
+ tokenInputMultiplier: null,
67208
+ tokenOutputMultiplier: null,
67209
+ cachedInputMultiplier: null,
67210
+ tier: "standard",
67211
+ available: true
67212
+ },
67213
+ {
67214
+ id: "gpt-5.2",
67215
+ displayName: "GPT-5.2",
67216
+ premiumMultiplier: 3,
67217
+ tokenInputMultiplier: null,
67218
+ tokenOutputMultiplier: null,
67219
+ cachedInputMultiplier: null,
67220
+ tier: "standard",
67221
+ available: true
67222
+ },
67223
+ {
67224
+ id: "gpt-5.4",
67225
+ displayName: "GPT-5.4",
67226
+ premiumMultiplier: 6,
67227
+ tokenInputMultiplier: null,
67228
+ tokenOutputMultiplier: null,
67229
+ cachedInputMultiplier: null,
67230
+ tier: "premium",
67231
+ available: true
67232
+ },
67233
+ {
67234
+ id: "claude-opus-4.5",
67235
+ displayName: "Claude Opus 4.5",
67236
+ premiumMultiplier: 15,
67237
+ tokenInputMultiplier: null,
67238
+ tokenOutputMultiplier: null,
67239
+ cachedInputMultiplier: null,
67240
+ tier: "premium",
67241
+ available: true
67242
+ },
67243
+ {
67244
+ id: "claude-opus-4.6",
67245
+ displayName: "Claude Opus 4.6",
67246
+ premiumMultiplier: 27,
67247
+ tokenInputMultiplier: null,
67248
+ tokenOutputMultiplier: null,
67249
+ cachedInputMultiplier: null,
67250
+ tier: "ultra",
67251
+ available: true
67252
+ },
67253
+ {
67254
+ id: "claude-opus-4.7",
67255
+ displayName: "Claude Opus 4.7",
67256
+ premiumMultiplier: 27,
67257
+ tokenInputMultiplier: null,
67258
+ tokenOutputMultiplier: null,
67259
+ cachedInputMultiplier: null,
67260
+ tier: "ultra",
67261
+ available: true
67262
+ },
67263
+ {
67264
+ id: "gpt-5.5",
67265
+ displayName: "GPT-5.5",
67266
+ premiumMultiplier: 57,
67267
+ tokenInputMultiplier: null,
67268
+ tokenOutputMultiplier: null,
67269
+ cachedInputMultiplier: null,
67270
+ tier: "ultra",
67271
+ available: true
67272
+ }
67273
+ ];
67274
+ }
67275
+ });
67276
+
67277
+ // packages/daemon/src/models/types.ts
67278
+ function computeTierFromMultiplier(premiumMultiplier) {
67279
+ if (premiumMultiplier === null) {
67280
+ return "standard";
67281
+ }
67282
+ for (const [tier, range] of Object.entries(TIER_RANGES)) {
67283
+ if (premiumMultiplier >= range.min && premiumMultiplier <= range.max) {
67284
+ return tier;
67285
+ }
67286
+ }
67287
+ return "ultra";
67288
+ }
67289
+ var TIER_RANGES, TOKEN_UNIT_PRICE;
67290
+ var init_types = __esm({
67291
+ "packages/daemon/src/models/types.ts"() {
67292
+ "use strict";
67293
+ TIER_RANGES = {
67294
+ trivial: { min: 0, max: 0.33 },
67295
+ fast: { min: 0.34, max: 1 },
67296
+ standard: { min: 1.1, max: 5 },
67297
+ premium: { min: 5.1, max: 15 },
67298
+ ultra: { min: 15.1, max: Number.POSITIVE_INFINITY }
67299
+ };
67300
+ TOKEN_UNIT_PRICE = 1e-5;
67301
+ }
67302
+ });
67303
+
67304
+ // packages/daemon/src/models/registry.ts
67305
+ function normalizeModelName(name) {
67306
+ return name.toLowerCase().replace(/^openai\s+/i, "").replace(/\s+/g, "-").replace(/[^a-z0-9.\-]/g, "").trim();
67307
+ }
67308
+ async function fetchCatalogIntoMap(modelMap, result, logger) {
67309
+ try {
67310
+ const catalogModels = await fetchModelCatalog();
67311
+ result.catalogFetched = true;
67312
+ for (const m of catalogModels) {
67313
+ const key = normalizeModelName(m.id);
67314
+ modelMap.set(key, { id: m.id, displayName: m.displayName, available: true });
67315
+ }
67316
+ } catch (error51) {
67317
+ const msg = error51 instanceof Error ? error51.message : String(error51);
67318
+ result.errors.push(`Catalog fetch failed: ${msg}`);
67319
+ logger?.warn(`Model catalog fetch failed: ${msg}`);
67320
+ }
67321
+ }
67322
+ async function scrapeTokenPricingIntoMap(modelMap, result, logger) {
67323
+ try {
67324
+ const tokenPricing = await scrapeTokenUnitPricing();
67325
+ result.tokenPricingScraped = true;
67326
+ for (const tp of tokenPricing) {
67327
+ const key = normalizeModelName(tp.modelName);
67328
+ const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
67329
+ if (existing) {
67330
+ existing.tokenInputMultiplier = tp.inputMultiplier;
67331
+ existing.tokenOutputMultiplier = tp.outputMultiplier;
67332
+ existing.cachedInputMultiplier = tp.cachedInputMultiplier;
67333
+ } else {
67334
+ modelMap.set(key, {
67335
+ id: key,
67336
+ displayName: tp.modelName,
67337
+ tokenInputMultiplier: tp.inputMultiplier,
67338
+ tokenOutputMultiplier: tp.outputMultiplier,
67339
+ cachedInputMultiplier: tp.cachedInputMultiplier,
67340
+ available: true
67341
+ });
67342
+ }
67343
+ }
67344
+ } catch (error51) {
67345
+ const msg = error51 instanceof Error ? error51.message : String(error51);
67346
+ result.errors.push(`Token pricing scrape failed: ${msg}`);
67347
+ logger?.warn(`Token pricing scrape failed: ${msg}`);
67348
+ }
67349
+ }
67350
+ async function scrapePremiumPricingIntoMap(modelMap, result, logger) {
67351
+ try {
67352
+ const premiumPricing = await scrapePremiumRequestPricing();
67353
+ result.premiumPricingScraped = true;
67354
+ for (const pp of premiumPricing) {
67355
+ const key = normalizeModelName(pp.modelName);
67356
+ const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
67357
+ if (existing) {
67358
+ existing.premiumMultiplier = pp.multiplier;
67359
+ } else {
67360
+ modelMap.set(key, {
67361
+ id: key,
67362
+ displayName: pp.modelName,
67363
+ premiumMultiplier: pp.multiplier,
67364
+ available: true
67365
+ });
67366
+ }
67367
+ }
67368
+ } catch (error51) {
67369
+ const msg = error51 instanceof Error ? error51.message : String(error51);
67370
+ result.errors.push(`Premium pricing scrape failed: ${msg}`);
67371
+ logger?.warn(`Premium pricing scrape failed: ${msg}`);
67372
+ }
67373
+ }
67374
+ async function refreshModelPricing(logger) {
67375
+ const result = {
67376
+ modelsUpdated: 0,
67377
+ catalogFetched: false,
67378
+ tokenPricingScraped: false,
67379
+ premiumPricingScraped: false,
67380
+ errors: []
67381
+ };
67382
+ const modelMap = /* @__PURE__ */ new Map();
67383
+ await fetchCatalogIntoMap(modelMap, result, logger);
67384
+ await scrapeTokenPricingIntoMap(modelMap, result, logger);
67385
+ await scrapePremiumPricingIntoMap(modelMap, result, logger);
67386
+ if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped) {
67387
+ logger?.warn("All pricing sources failed, using seed data");
67388
+ await seedFromFallback();
67389
+ result.modelsUpdated = SEED_MODELS.length;
67390
+ return result;
67391
+ }
67392
+ const db = await getDatabase();
67393
+ const now = nowIso();
67394
+ for (const model of modelMap.values()) {
67395
+ const tier = computeTierFromMultiplier(model.premiumMultiplier ?? null);
67396
+ await upsertModel(db, {
67397
+ id: model.id,
67398
+ displayName: model.displayName,
67399
+ premiumMultiplier: model.premiumMultiplier ?? null,
67400
+ tokenInputMultiplier: model.tokenInputMultiplier ?? null,
67401
+ tokenOutputMultiplier: model.tokenOutputMultiplier ?? null,
67402
+ cachedInputMultiplier: model.cachedInputMultiplier ?? null,
67403
+ tier,
67404
+ available: model.available ?? true,
67405
+ updatedAt: now
67406
+ });
67407
+ result.modelsUpdated++;
67408
+ }
67409
+ return result;
67410
+ }
67411
+ async function seedFromFallback() {
67412
+ const db = await getDatabase();
67413
+ const now = nowIso();
67414
+ for (const model of SEED_MODELS) {
67415
+ await upsertModel(db, { ...model, updatedAt: now });
67416
+ }
67417
+ }
67418
+ async function getModelPricing(modelId) {
67419
+ const db = await getDatabase();
67420
+ const result = await db.execute({
67421
+ sql: "SELECT * FROM model_pricing WHERE id = ?",
67422
+ args: [modelId]
67423
+ });
67424
+ if (result.rows.length === 0) {
67425
+ return null;
67426
+ }
67427
+ return rowToModelPricing(result.rows[0]);
67428
+ }
67429
+ function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
67430
+ if (inputMultiplier === null || outputMultiplier === null) {
67431
+ return 0;
67432
+ }
67433
+ const tokenUnits = inputTokens * inputMultiplier + outputTokens * outputMultiplier;
67434
+ return tokenUnits * TOKEN_UNIT_PRICE;
67435
+ }
67436
+ async function upsertModel(db, model) {
67437
+ await db.execute({
67438
+ sql: `INSERT INTO model_pricing (id, display_name, premium_multiplier, token_input_multiplier, token_output_multiplier, cached_input_multiplier, tier, available, updated_at)
67439
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
67440
+ ON CONFLICT(id) DO UPDATE SET
67441
+ display_name = excluded.display_name,
67442
+ premium_multiplier = COALESCE(excluded.premium_multiplier, model_pricing.premium_multiplier),
67443
+ token_input_multiplier = COALESCE(excluded.token_input_multiplier, model_pricing.token_input_multiplier),
67444
+ token_output_multiplier = COALESCE(excluded.token_output_multiplier, model_pricing.token_output_multiplier),
67445
+ cached_input_multiplier = COALESCE(excluded.cached_input_multiplier, model_pricing.cached_input_multiplier),
67446
+ tier = excluded.tier,
67447
+ available = excluded.available,
67448
+ updated_at = excluded.updated_at`,
67449
+ args: [
67450
+ model.id,
67451
+ model.displayName,
67452
+ model.premiumMultiplier,
67453
+ model.tokenInputMultiplier,
67454
+ model.tokenOutputMultiplier,
67455
+ model.cachedInputMultiplier,
67456
+ model.tier,
67457
+ model.available ? 1 : 0,
67458
+ model.updatedAt
67459
+ ]
67460
+ });
67461
+ }
67462
+ function rowToModelPricing(row) {
67463
+ return {
67464
+ id: asString(row.id),
67465
+ displayName: asString(row.display_name),
67466
+ premiumMultiplier: asNullableNumber(row.premium_multiplier),
67467
+ tokenInputMultiplier: asNullableNumber(row.token_input_multiplier),
67468
+ tokenOutputMultiplier: asNullableNumber(row.token_output_multiplier),
67469
+ cachedInputMultiplier: asNullableNumber(row.cached_input_multiplier),
67470
+ tier: asString(row.tier),
67471
+ available: asNumber(row.available) === 1,
67472
+ updatedAt: asString(row.updated_at)
67473
+ };
67474
+ }
67475
+ function findClosestKey(map2, targetKey) {
67476
+ for (const [key, value] of map2) {
67477
+ if (key.includes(targetKey) || targetKey.includes(key)) {
67478
+ return value;
67479
+ }
67480
+ }
67481
+ return void 0;
67482
+ }
67483
+ var init_registry = __esm({
67484
+ "packages/daemon/src/models/registry.ts"() {
67485
+ "use strict";
67486
+ init_db();
67487
+ init_catalog();
67488
+ init_pricing_scraper();
67489
+ init_seed();
67490
+ init_types();
67491
+ }
67492
+ });
67493
+
67494
+ // packages/daemon/src/models/index.ts
67495
+ var init_models = __esm({
67496
+ "packages/daemon/src/models/index.ts"() {
67497
+ "use strict";
67498
+ init_catalog();
67499
+ init_registry();
67500
+ init_seed();
67501
+ init_types();
67502
+ }
67503
+ });
67504
+
66903
67505
  // packages/daemon/src/orchestrator/system-prompt.ts
66904
67506
  function formatSquadRoster(squads) {
66905
67507
  if (squads.length === 0) {
@@ -67003,148 +67605,8 @@ var init_reset = __esm({
67003
67605
  }
67004
67606
  });
67005
67607
 
67006
- // packages/daemon/src/copilot/router.ts
67007
- function normalizeMessage(message2) {
67008
- return message2.trim().toLowerCase();
67009
- }
67010
- function getWordCount(message2) {
67011
- const normalized = message2.trim();
67012
- if (normalized.length === 0) {
67013
- return 0;
67014
- }
67015
- return normalized.split(/\s+/u).length;
67016
- }
67017
- function containsAny(message2, keywords) {
67018
- return keywords.some((keyword) => message2.includes(keyword));
67019
- }
67020
- function startsWithSimpleQuestion(message2) {
67021
- return SIMPLE_QUESTION_PREFIXES.some(
67022
- (prefix) => message2 === prefix || message2.startsWith(`${prefix} `)
67023
- );
67024
- }
67025
- function classifyMessage(message2) {
67026
- const normalized = normalizeMessage(message2);
67027
- const wordCount = getWordCount(normalized);
67028
- if (normalized.length === 0) {
67029
- return "fast";
67030
- }
67031
- if (containsAny(normalized, PREMIUM_KEYWORDS) || normalized.includes("```") || wordCount >= 80) {
67032
- return "premium";
67033
- }
67034
- if (wordCount <= 20 && (containsAny(normalized, FAST_KEYWORDS) || startsWithSimpleQuestion(normalized) || /^(yes|no|sure|okay|ok|thanks)[!.?]*$/u.test(normalized))) {
67035
- return "fast";
67036
- }
67037
- if (wordCount <= 8 && /^(what|when|where|who)\b/u.test(normalized)) {
67038
- return "fast";
67039
- }
67040
- return "standard";
67041
- }
67042
- function getModelForTier(tier) {
67043
- switch (tier) {
67044
- case "fast":
67045
- return FAST_MODEL;
67046
- case "premium":
67047
- return PREMIUM_MODEL;
67048
- default:
67049
- return STANDARD_MODEL;
67050
- }
67051
- }
67052
- function routeMessage(message2) {
67053
- const requestedTier = classifyMessage(message2);
67054
- if (activeTier === null) {
67055
- activeTier = requestedTier;
67056
- messagesSinceLastSwitch = 0;
67057
- return {
67058
- requestedTier,
67059
- effectiveTier: activeTier,
67060
- model: getModelForTier(activeTier),
67061
- switched: true,
67062
- cooldownRemaining: MODEL_SWITCH_COOLDOWN_MESSAGES
67063
- };
67064
- }
67065
- if (requestedTier !== activeTier && messagesSinceLastSwitch >= MODEL_SWITCH_COOLDOWN_MESSAGES) {
67066
- activeTier = requestedTier;
67067
- messagesSinceLastSwitch = 0;
67068
- return {
67069
- requestedTier,
67070
- effectiveTier: activeTier,
67071
- model: getModelForTier(activeTier),
67072
- switched: true,
67073
- cooldownRemaining: MODEL_SWITCH_COOLDOWN_MESSAGES
67074
- };
67075
- }
67076
- messagesSinceLastSwitch += 1;
67077
- return {
67078
- requestedTier,
67079
- effectiveTier: activeTier,
67080
- model: getModelForTier(activeTier),
67081
- switched: false,
67082
- cooldownRemaining: Math.max(MODEL_SWITCH_COOLDOWN_MESSAGES - messagesSinceLastSwitch, 0)
67083
- };
67084
- }
67085
- var FAST_KEYWORDS, PREMIUM_KEYWORDS, SIMPLE_QUESTION_PREFIXES, MODEL_SWITCH_COOLDOWN_MESSAGES, activeTier, messagesSinceLastSwitch;
67086
- var init_router = __esm({
67087
- "packages/daemon/src/copilot/router.ts"() {
67088
- "use strict";
67089
- init_dist();
67090
- FAST_KEYWORDS = [
67091
- "hi",
67092
- "hello",
67093
- "hey",
67094
- "thanks",
67095
- "thank you",
67096
- "status",
67097
- "list",
67098
- "show",
67099
- "ping",
67100
- "uptime",
67101
- "what time",
67102
- "ok",
67103
- "okay"
67104
- ];
67105
- PREMIUM_KEYWORDS = [
67106
- "architecture",
67107
- "architect",
67108
- "design",
67109
- "tradeoff",
67110
- "trade-off",
67111
- "plan",
67112
- "planning",
67113
- "strategy",
67114
- "roadmap",
67115
- "migration",
67116
- "refactor",
67117
- "generate",
67118
- "implement",
67119
- "build",
67120
- "codebase",
67121
- "repository",
67122
- "system prompt",
67123
- "multi-step",
67124
- "complex",
67125
- "reason",
67126
- "compare"
67127
- ];
67128
- SIMPLE_QUESTION_PREFIXES = [
67129
- "is",
67130
- "are",
67131
- "do",
67132
- "does",
67133
- "did",
67134
- "can",
67135
- "could",
67136
- "should",
67137
- "would",
67138
- "will"
67139
- ];
67140
- MODEL_SWITCH_COOLDOWN_MESSAGES = 3;
67141
- activeTier = null;
67142
- messagesSinceLastSwitch = MODEL_SWITCH_COOLDOWN_MESSAGES;
67143
- }
67144
- });
67145
-
67146
67608
  // packages/daemon/src/copilot/client.ts
67147
- import { execFileSync } from "node:child_process";
67609
+ import { execFileSync as execFileSync2 } from "node:child_process";
67148
67610
  import { mkdirSync as mkdirSync2 } from "node:fs";
67149
67611
  import { join as join6 } from "node:path";
67150
67612
  import { CopilotClient } from "@github/copilot-sdk";
@@ -67154,7 +67616,7 @@ function readTokenFromEnvironment() {
67154
67616
  }
67155
67617
  function readTokenFromGhCli() {
67156
67618
  try {
67157
- const token = execFileSync("gh", ["auth", "token"], {
67619
+ const token = execFileSync2("gh", ["auth", "token"], {
67158
67620
  encoding: "utf8",
67159
67621
  stdio: ["ignore", "pipe", "pipe"]
67160
67622
  }).trim();
@@ -67163,7 +67625,7 @@ function readTokenFromGhCli() {
67163
67625
  return void 0;
67164
67626
  }
67165
67627
  }
67166
- function resolveGitHubToken() {
67628
+ function resolveGitHubToken2() {
67167
67629
  return readTokenFromEnvironment() ?? readTokenFromGhCli();
67168
67630
  }
67169
67631
  function buildClientOptions(token) {
@@ -67199,7 +67661,7 @@ async function initCopilotClient() {
67199
67661
  }
67200
67662
  copilotClientInitPromise = (async () => {
67201
67663
  try {
67202
- const token = resolveGitHubToken();
67664
+ const token = resolveGitHubToken2();
67203
67665
  if (token === void 0) {
67204
67666
  throw new Error(
67205
67667
  `Unable to find a GitHub token for Copilot SDK authentication. ${COPILOT_AUTH_ERROR_HINT}`
@@ -68115,13 +68577,24 @@ function mergeUsage(target, usage) {
68115
68577
  }
68116
68578
  async function persistUsage(member, usageEvents) {
68117
68579
  for (const usage of usageEvents) {
68580
+ const model = usage.model;
68581
+ const pricing = await getModelPricing(model);
68582
+ const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
68583
+ const tokenUnitCost = pricing ? calculateTokenUnitCost(
68584
+ usage.inputTokens ?? 0,
68585
+ usage.outputTokens ?? 0,
68586
+ pricing.tokenInputMultiplier,
68587
+ pricing.tokenOutputMultiplier
68588
+ ) : 0;
68118
68589
  await recordUsage({
68119
68590
  squadId: member.squadId,
68120
68591
  agentId: member.id,
68121
- model: usage.model,
68592
+ model,
68122
68593
  inputTokens: usage.inputTokens ?? 0,
68123
68594
  outputTokens: usage.outputTokens ?? 0,
68124
- cost: usage.cost ?? 0
68595
+ cost: 0,
68596
+ premiumRequestCost,
68597
+ tokenUnitCost
68125
68598
  });
68126
68599
  }
68127
68600
  }
@@ -68339,7 +68812,7 @@ async function executeAgentTask(member, task, worktreePath, options2) {
68339
68812
  client2 = new CopilotClient2({ workingDirectory: worktreePath });
68340
68813
  await client2.start();
68341
68814
  const session = await client2.createSession({
68342
- model: member.model ?? STANDARD_MODEL,
68815
+ model: member.model ?? DEFAULT_MODEL,
68343
68816
  workingDirectory: worktreePath,
68344
68817
  tools,
68345
68818
  availableTools: ["custom:*"],
@@ -68396,6 +68869,7 @@ var init_agent = __esm({
68396
68869
  "packages/daemon/src/execution/agent.ts"() {
68397
68870
  "use strict";
68398
68871
  init_dist();
68872
+ init_registry();
68399
68873
  init_store2();
68400
68874
  init_history();
68401
68875
  execAsync2 = promisify2(exec2);
@@ -68594,7 +69068,7 @@ Return strict JSON in this shape:
68594
69068
  client2 = new CopilotClient3({ workingDirectory: repoPath });
68595
69069
  await client2.start();
68596
69070
  const session = await client2.createSession({
68597
- model: PREMIUM_MODEL,
69071
+ model: DEFAULT_MODEL,
68598
69072
  workingDirectory: repoPath,
68599
69073
  onPermissionRequest: approveAll3,
68600
69074
  systemMessage: {
@@ -68885,7 +69359,7 @@ Return strict JSON:
68885
69359
  client2 = new CopilotClient4({ workingDirectory: worktreePath });
68886
69360
  await client2.start();
68887
69361
  const session = await client2.createSession({
68888
- model: qaMember.model ?? STANDARD_MODEL,
69362
+ model: qaMember.model ?? DEFAULT_MODEL,
68889
69363
  workingDirectory: worktreePath,
68890
69364
  onPermissionRequest: approveAll4,
68891
69365
  systemMessage: {
@@ -69017,7 +69491,7 @@ Return strict JSON:
69017
69491
  client2 = new CopilotClient5();
69018
69492
  await client2.start();
69019
69493
  const session = await client2.createSession({
69020
- model: teamLead.model ?? PREMIUM_MODEL,
69494
+ model: teamLead.model ?? DEFAULT_MODEL,
69021
69495
  onPermissionRequest: approveAll5,
69022
69496
  systemMessage: {
69023
69497
  content: `${TEAM_LEAD_PROMPT}
@@ -69507,12 +69981,9 @@ function buildMandatoryRoles() {
69507
69981
  function modelForRole(role) {
69508
69982
  const normalized = slugifyRole(role);
69509
69983
  if (normalized === "team-lead") {
69510
- return PREMIUM_MODEL;
69984
+ return DEFAULT_MODEL;
69511
69985
  }
69512
- if (normalized === "qa") {
69513
- return STANDARD_MODEL;
69514
- }
69515
- return STANDARD_MODEL;
69986
+ return DEFAULT_MODEL;
69516
69987
  }
69517
69988
  function systemPromptForRole(role, repoContext) {
69518
69989
  const normalized = slugifyRole(role);
@@ -69544,7 +70015,7 @@ ${ROLE_GENERATION_PROMPT}`;
69544
70015
  client2 = new CopilotClient6();
69545
70016
  await client2.start();
69546
70017
  const session = await client2.createSession({
69547
- model: PREMIUM_MODEL,
70018
+ model: DEFAULT_MODEL,
69548
70019
  onPermissionRequest: approveAll6,
69549
70020
  systemMessage: {
69550
70021
  content: ROLE_GENERATION_PROMPT
@@ -69983,9 +70454,9 @@ var init_orchestrator = __esm({
69983
70454
  "use strict";
69984
70455
  init_dist();
69985
70456
  init_reset();
69986
- init_router();
69987
70457
  init_session();
69988
70458
  init_logger();
70459
+ init_registry();
69989
70460
  init_skills2();
69990
70461
  init_store2();
69991
70462
  init_episodes();
@@ -70078,8 +70549,7 @@ var init_orchestrator = __esm({
70078
70549
  skillsContext,
70079
70550
  conversationSummary: [this.latestResetSummary, conversationSummary].filter(Boolean).join("\n\n")
70080
70551
  });
70081
- const route = routeMessage(normalizedMessage);
70082
- await this.refreshSession(route.model, systemPrompt);
70552
+ await this.refreshSession(this.config.defaultModel, systemPrompt);
70083
70553
  const sendResult = await sendMessage(
70084
70554
  this.requireActiveSession(),
70085
70555
  normalizedMessage,
@@ -70207,11 +70677,22 @@ var init_orchestrator = __esm({
70207
70677
  if (usage.inputTokens === 0 && usage.outputTokens === 0) {
70208
70678
  return;
70209
70679
  }
70680
+ const model = usage.model || this.activeModel || this.config.defaultModel;
70681
+ const pricing = await getModelPricing(model);
70682
+ const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
70683
+ const tokenUnitCost = pricing ? calculateTokenUnitCost(
70684
+ usage.inputTokens,
70685
+ usage.outputTokens,
70686
+ pricing.tokenInputMultiplier,
70687
+ pricing.tokenOutputMultiplier
70688
+ ) : 0;
70210
70689
  await recordUsage({
70211
- model: usage.model || this.activeModel || this.config.defaultModel,
70690
+ model,
70212
70691
  inputTokens: usage.inputTokens,
70213
70692
  outputTokens: usage.outputTokens,
70214
- cost: 0
70693
+ cost: 0,
70694
+ premiumRequestCost,
70695
+ tokenUnitCost
70215
70696
  });
70216
70697
  }
70217
70698
  };
@@ -83818,6 +84299,7 @@ function registerShutdownHandlers(logger, onShutdown) {
83818
84299
  }
83819
84300
  async function main() {
83820
84301
  let logger;
84302
+ let pricingRefreshTimer = null;
83821
84303
  try {
83822
84304
  ensureDataDirectories();
83823
84305
  const config2 = loadConfig();
@@ -83825,6 +84307,12 @@ async function main() {
83825
84307
  logger.info(`IO Daemon v${APP_VERSION} starting...`);
83826
84308
  await initDatabase();
83827
84309
  logger.info("Database initialized");
84310
+ const pricingResult = await refreshModelPricing(logger);
84311
+ if (pricingResult.modelsUpdated === 0) {
84312
+ logger.warn("Model pricing refresh returned 0 models, seeding with fallback");
84313
+ await seedFromFallback();
84314
+ }
84315
+ logger.info({ modelsUpdated: pricingResult.modelsUpdated }, "Model pricing initialized");
83828
84316
  await scanSkills();
83829
84317
  logger.info("Skills scanned");
83830
84318
  const orchestrator2 = createOrchestrator(config2, eventBus);
@@ -83833,12 +84321,21 @@ async function main() {
83833
84321
  const scheduler = createScheduler(orchestrator2, eventBus);
83834
84322
  scheduler.start();
83835
84323
  logger.info("Scheduler started");
84324
+ const refreshIntervalMs = config2.pricingRefreshHours * 60 * 60 * 1e3;
84325
+ pricingRefreshTimer = setInterval(() => {
84326
+ void refreshModelPricing(logger).catch((err) => {
84327
+ logger?.warn({ err }, "Periodic model pricing refresh failed");
84328
+ });
84329
+ }, refreshIntervalMs);
83836
84330
  setChatOrchestrator(orchestrator2);
83837
84331
  const apiServer = createApiServer(config2);
83838
84332
  const telegramBot = createTelegramBot(config2, orchestrator2);
83839
84333
  telegramBot?.start();
83840
84334
  createTelegramNotifier(telegramBot, config2, eventBus);
83841
84335
  registerShutdownHandlers(logger, async () => {
84336
+ if (pricingRefreshTimer) {
84337
+ clearInterval(pricingRefreshTimer);
84338
+ }
83842
84339
  scheduler.stop();
83843
84340
  telegramBot?.stop();
83844
84341
  apiServer.server.close();
@@ -83863,6 +84360,7 @@ var init_index = __esm({
83863
84360
  init_data_dir();
83864
84361
  init_event_bus();
83865
84362
  init_logger();
84363
+ init_models();
83866
84364
  init_orchestrator2();
83867
84365
  init_scheduler();
83868
84366
  init_skills2();