heyio 4.0.3 → 4.0.5

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.5";
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
  });
@@ -31154,7 +31174,7 @@ var require_view = __commonJS({
31154
31174
  var dirname9 = path.dirname;
31155
31175
  var basename6 = path.basename;
31156
31176
  var extname4 = path.extname;
31157
- var join14 = path.join;
31177
+ var join15 = path.join;
31158
31178
  var resolve5 = path.resolve;
31159
31179
  module2.exports = View;
31160
31180
  function View(name, options2) {
@@ -31216,12 +31236,12 @@ var require_view = __commonJS({
31216
31236
  };
31217
31237
  View.prototype.resolve = function resolve6(dir, file2) {
31218
31238
  var ext = this.ext;
31219
- var path2 = join14(dir, file2);
31239
+ var path2 = join15(dir, file2);
31220
31240
  var stat4 = tryStat(path2);
31221
31241
  if (stat4 && stat4.isFile()) {
31222
31242
  return path2;
31223
31243
  }
31224
- path2 = join14(dir, basename6(file2, ext), "index" + ext);
31244
+ path2 = join15(dir, basename6(file2, ext), "index" + ext);
31225
31245
  stat4 = tryStat(path2);
31226
31246
  if (stat4 && stat4.isFile()) {
31227
31247
  return path2;
@@ -34926,7 +34946,7 @@ var require_send = __commonJS({
34926
34946
  var Stream = __require("stream");
34927
34947
  var util = __require("util");
34928
34948
  var extname4 = path.extname;
34929
- var join14 = path.join;
34949
+ var join15 = path.join;
34930
34950
  var normalize = path.normalize;
34931
34951
  var resolve5 = path.resolve;
34932
34952
  var sep = path.sep;
@@ -35098,7 +35118,7 @@ var require_send = __commonJS({
35098
35118
  return res;
35099
35119
  }
35100
35120
  parts = path2.split(sep);
35101
- path2 = normalize(join14(root, path2));
35121
+ path2 = normalize(join15(root, path2));
35102
35122
  } else {
35103
35123
  if (UP_PATH_REGEXP.test(path2)) {
35104
35124
  debug('malicious path "%s"', path2);
@@ -35231,7 +35251,7 @@ var require_send = __commonJS({
35231
35251
  if (err) return self.onStatError(err);
35232
35252
  return self.error(404);
35233
35253
  }
35234
- var p = join14(path2, self._index[i]);
35254
+ var p = join15(path2, self._index[i]);
35235
35255
  debug('stat "%s"', p);
35236
35256
  fs.stat(p, function(err2, stat4) {
35237
35257
  if (err2) return next(err2);
@@ -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 {
@@ -51702,13 +51727,20 @@ async function installSkill(request) {
51702
51727
  const directory = join3(SKILLS_DIR, directoryName);
51703
51728
  const existing = installedSkills.find((skill2) => skill2.id === id);
51704
51729
  let entryFile = existing?.entryFile ?? null;
51705
- if (request.url) {
51706
- const response = await fetch(request.url);
51730
+ let resolvedUrl = request.url;
51731
+ if (source === "skillssh" && resolvedUrl) {
51732
+ const fetchResult = await fetch(resolvedUrl);
51733
+ if (!fetchResult.ok) {
51734
+ resolvedUrl = await resolveSkillsShUrl(request) ?? resolvedUrl;
51735
+ }
51736
+ }
51737
+ if (resolvedUrl) {
51738
+ const response = await fetch(resolvedUrl);
51707
51739
  if (!response.ok) {
51708
- throw new Error(`Failed to fetch skill from ${request.url}`);
51740
+ throw new Error(`Failed to fetch skill from ${resolvedUrl} (${response.status})`);
51709
51741
  }
51710
51742
  const body = await response.text();
51711
- entryFile = chooseEntryFileName(request.url, response.headers.get("content-type"));
51743
+ entryFile = chooseEntryFileName(resolvedUrl, response.headers.get("content-type"));
51712
51744
  await mkdir3(directory, { recursive: true });
51713
51745
  await writeFile2(join3(directory, entryFile), body, "utf8");
51714
51746
  } else {
@@ -51795,7 +51827,114 @@ function chooseEntryFileName(url2, contentType) {
51795
51827
  function isMissingFileError2(error51) {
51796
51828
  return !!error51 && typeof error51 === "object" && "code" in error51 && error51.code === "ENOENT";
51797
51829
  }
51798
- var import_express6, router6;
51830
+ async function resolveSkillsShUrl(request) {
51831
+ const slug = request.slug?.trim();
51832
+ if (!slug) return null;
51833
+ try {
51834
+ const searchResponse = await fetch(
51835
+ `https://skills.sh/api/search?q=${encodeURIComponent(slug)}&limit=10`,
51836
+ { signal: AbortSignal.timeout(1e4) }
51837
+ );
51838
+ if (!searchResponse.ok) return null;
51839
+ const data = await searchResponse.json();
51840
+ const match = data.skills?.find(
51841
+ (s) => s.skillId === slug || s.name === slug || s.id.endsWith(`/${slug}`)
51842
+ );
51843
+ if (!match?.source) return null;
51844
+ const headers = {
51845
+ Accept: "application/vnd.github.v3+json",
51846
+ "User-Agent": "io-daemon"
51847
+ };
51848
+ if (process.env.GITHUB_TOKEN) {
51849
+ headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
51850
+ }
51851
+ const treeResponse = await fetch(
51852
+ `https://api.github.com/repos/${match.source}/git/trees/main?recursive=1`,
51853
+ { headers, signal: AbortSignal.timeout(15e3) }
51854
+ );
51855
+ if (!treeResponse.ok) return null;
51856
+ const treeData = await treeResponse.json();
51857
+ const skillFile = treeData.tree?.find(
51858
+ (entry) => entry.type === "blob" && entry.path.endsWith(`/${match.skillId}/SKILL.md`)
51859
+ );
51860
+ if (!skillFile) return null;
51861
+ return `https://raw.githubusercontent.com/${match.source}/main/${skillFile.path}`;
51862
+ } catch {
51863
+ return null;
51864
+ }
51865
+ }
51866
+ async function discoverSkillsSh(query) {
51867
+ if (!query) return [];
51868
+ const endpoint = `https://skills.sh/api/search?q=${encodeURIComponent(query)}&limit=50`;
51869
+ const response = await fetch(endpoint, { signal: AbortSignal.timeout(1e4) });
51870
+ if (!response.ok) {
51871
+ throw new Error(`skills.sh 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
+ return (data.skills ?? []).map((entry) => {
51877
+ const skillUrl = entry.source ? `https://raw.githubusercontent.com/${entry.source}/main/skills/${entry.skillId}/SKILL.md` : "";
51878
+ return {
51879
+ name: entry.name || entry.skillId,
51880
+ title: entry.name || entry.skillId,
51881
+ description: `${entry.source ?? ""}`,
51882
+ url: skillUrl,
51883
+ source: "skillssh",
51884
+ installed: installedIds.has(`skillssh:${normalizeSlug(entry.skillId || entry.name)}`),
51885
+ registrySource: entry.source ?? void 0,
51886
+ skillId: entry.skillId ?? void 0,
51887
+ installs: entry.installs ?? 0
51888
+ };
51889
+ });
51890
+ }
51891
+ async function discoverAwesomeCopilot(query) {
51892
+ const now = Date.now();
51893
+ if (!awesomeCopilotCache || now - awesomeCopilotCache.fetchedAt > AWESOME_COPILOT_CACHE_TTL) {
51894
+ awesomeCopilotCache = { skills: await fetchAwesomeCopilotList(), fetchedAt: now };
51895
+ }
51896
+ const skills = awesomeCopilotCache.skills;
51897
+ if (!query) return skills;
51898
+ const needle = query.toLowerCase();
51899
+ return skills.filter(
51900
+ (s) => s.name.toLowerCase().includes(needle) || s.title.toLowerCase().includes(needle) || s.description.toLowerCase().includes(needle)
51901
+ );
51902
+ }
51903
+ async function fetchAwesomeCopilotList() {
51904
+ const treeUrl = "https://api.github.com/repos/github/awesome-copilot/git/trees/main?recursive=1";
51905
+ const headers = {
51906
+ Accept: "application/vnd.github.v3+json",
51907
+ "User-Agent": "io-daemon"
51908
+ };
51909
+ if (process.env.GITHUB_TOKEN) {
51910
+ headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
51911
+ }
51912
+ const response = await fetch(treeUrl, { headers, signal: AbortSignal.timeout(15e3) });
51913
+ if (!response.ok) {
51914
+ throw new Error(`GitHub API returned ${response.status}`);
51915
+ }
51916
+ const data = await response.json();
51917
+ const installedSkills = await readInstalledSkills();
51918
+ const installedIds = new Set(installedSkills.map((s) => s.id));
51919
+ const skillEntries = (data.tree ?? []).filter(
51920
+ (entry) => entry.type === "blob" && entry.path.startsWith("skills/") && entry.path.endsWith("/SKILL.md")
51921
+ );
51922
+ return skillEntries.map((entry) => {
51923
+ const parts = entry.path.split("/");
51924
+ const skillName = parts[1] ?? "unknown";
51925
+ const slug = normalizeSlug(skillName);
51926
+ const rawUrl = `https://raw.githubusercontent.com/github/awesome-copilot/main/${entry.path}`;
51927
+ return {
51928
+ name: skillName,
51929
+ title: skillName.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
51930
+ description: "From github/awesome-copilot",
51931
+ url: rawUrl,
51932
+ source: "awesome-copilot",
51933
+ installed: installedIds.has(`awesome-copilot:${slug}`)
51934
+ };
51935
+ });
51936
+ }
51937
+ var import_express6, router6, awesomeCopilotCache, AWESOME_COPILOT_CACHE_TTL;
51799
51938
  var init_skills = __esm({
51800
51939
  "packages/daemon/src/api/routes/skills.ts"() {
51801
51940
  "use strict";
@@ -51815,11 +51954,16 @@ var init_skills = __esm({
51815
51954
  router6.post("/api/skills/install", async (req, res) => {
51816
51955
  try {
51817
51956
  const body = req.body;
51818
- if (!body?.url && !(body?.source && body?.slug)) {
51957
+ if (!body?.url && !(body?.source && (body?.slug || body?.skillId || body?.name))) {
51819
51958
  res.status(400).json({ error: "Provide url or source+slug to install a skill" });
51820
51959
  return;
51821
51960
  }
51822
- const result = await installSkill(body);
51961
+ const normalized = {
51962
+ url: body.url,
51963
+ source: body.source,
51964
+ slug: body.slug || body.skillId || body.name
51965
+ };
51966
+ const result = await installSkill(normalized);
51823
51967
  res.status(result.created ? 201 : 200).json(result.skill);
51824
51968
  } catch (error51) {
51825
51969
  const statusCode = error51 instanceof Error && /Invalid skill|fetch/i.test(error51.message) ? 400 : 500;
@@ -51844,9 +51988,28 @@ var init_skills = __esm({
51844
51988
  });
51845
51989
  }
51846
51990
  });
51847
- router6.get("/api/skills/discover", async (_req, res) => {
51848
- res.status(200).json([]);
51991
+ router6.get("/api/skills/discover", async (req, res) => {
51992
+ try {
51993
+ const source = typeof req.query.source === "string" ? req.query.source : "";
51994
+ const query = typeof req.query.q === "string" ? req.query.q.trim() : "";
51995
+ if (source === "skillssh") {
51996
+ const skills = await discoverSkillsSh(query);
51997
+ res.status(200).json(skills);
51998
+ } else if (source === "awesome-copilot") {
51999
+ const skills = await discoverAwesomeCopilot(query);
52000
+ res.status(200).json(skills);
52001
+ } else {
52002
+ res.status(400).json({ error: `Unknown source: ${source}` });
52003
+ }
52004
+ } catch (error51) {
52005
+ res.status(500).json({
52006
+ error: "Failed to discover skills",
52007
+ details: error51 instanceof Error ? error51.message : "Unknown error"
52008
+ });
52009
+ }
51849
52010
  });
52011
+ awesomeCopilotCache = null;
52012
+ AWESOME_COPILOT_CACHE_TTL = 60 * 60 * 1e3;
51850
52013
  }
51851
52014
  });
51852
52015
 
@@ -59888,7 +60051,7 @@ var init_websocket = __esm({
59888
60051
  // packages/daemon/src/api/server.ts
59889
60052
  import { existsSync as existsSync2 } from "node:fs";
59890
60053
  import { createServer } from "node:http";
59891
- import { dirname as dirname4, resolve as resolve2 } from "node:path";
60054
+ import { dirname as dirname4, join as join5, resolve as resolve2 } from "node:path";
59892
60055
  import { fileURLToPath } from "node:url";
59893
60056
  function createApiServer(config2) {
59894
60057
  const app = (0, import_express10.default)();
@@ -59926,6 +60089,9 @@ function createApiServer(config2) {
59926
60089
  });
59927
60090
  if (existsSync2(webDirectory)) {
59928
60091
  app.use(import_express10.default.static(webDirectory));
60092
+ app.get("*", (_req, res) => {
60093
+ res.sendFile(join5(webDirectory, "index.html"));
60094
+ });
59929
60095
  }
59930
60096
  app.use((error51, _req, res, next) => {
59931
60097
  if (res.headersSent) {
@@ -62008,7 +62174,7 @@ var require_thread_stream = __commonJS({
62008
62174
  var { version: version2 } = require_package();
62009
62175
  var { EventEmitter: EventEmitter2 } = __require("events");
62010
62176
  var { Worker } = __require("worker_threads");
62011
- var { join: join14 } = __require("path");
62177
+ var { join: join15 } = __require("path");
62012
62178
  var { pathToFileURL: pathToFileURL2 } = __require("url");
62013
62179
  var { wait } = require_wait();
62014
62180
  var {
@@ -62051,7 +62217,7 @@ var require_thread_stream = __commonJS({
62051
62217
  function createWorker(stream, opts) {
62052
62218
  const { filename, workerData } = opts;
62053
62219
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
62054
- const toExecute = bundlerOverrides["thread-stream-worker"] || join14(__dirname, "lib", "worker.js");
62220
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join15(__dirname, "lib", "worker.js");
62055
62221
  const worker = new Worker(toExecute, {
62056
62222
  ...opts.workerOpts,
62057
62223
  trackUnmanagedFds: false,
@@ -62454,7 +62620,7 @@ var require_transport = __commonJS({
62454
62620
  "use strict";
62455
62621
  var { createRequire } = __require("module");
62456
62622
  var getCallers = require_caller();
62457
- var { join: join14, isAbsolute: isAbsolute2, sep } = __require("node:path");
62623
+ var { join: join15, isAbsolute: isAbsolute2, sep } = __require("node:path");
62458
62624
  var sleep = require_atomic_sleep();
62459
62625
  var onExit = require_on_exit_leak_free();
62460
62626
  var ThreadStream = require_thread_stream();
@@ -62517,7 +62683,7 @@ var require_transport = __commonJS({
62517
62683
  throw new Error("only one of target or targets can be specified");
62518
62684
  }
62519
62685
  if (targets) {
62520
- target = bundlerOverrides["pino-worker"] || join14(__dirname, "worker.js");
62686
+ target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
62521
62687
  options2.targets = targets.filter((dest) => dest.target).map((dest) => {
62522
62688
  return {
62523
62689
  ...dest,
@@ -62535,7 +62701,7 @@ var require_transport = __commonJS({
62535
62701
  });
62536
62702
  });
62537
62703
  } else if (pipeline) {
62538
- target = bundlerOverrides["pino-worker"] || join14(__dirname, "worker.js");
62704
+ target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
62539
62705
  options2.pipelines = [pipeline.map((dest) => {
62540
62706
  return {
62541
62707
  ...dest,
@@ -62557,7 +62723,7 @@ var require_transport = __commonJS({
62557
62723
  return origin;
62558
62724
  }
62559
62725
  if (origin === "pino/file") {
62560
- return join14(__dirname, "..", "file.js");
62726
+ return join15(__dirname, "..", "file.js");
62561
62727
  }
62562
62728
  let fixTarget2;
62563
62729
  for (const filePath of callers) {
@@ -63546,7 +63712,7 @@ var require_safe_stable_stringify = __commonJS({
63546
63712
  return circularValue;
63547
63713
  }
63548
63714
  let res = "";
63549
- let join14 = ",";
63715
+ let join15 = ",";
63550
63716
  const originalIndentation = indentation;
63551
63717
  if (Array.isArray(value)) {
63552
63718
  if (value.length === 0) {
@@ -63560,7 +63726,7 @@ var require_safe_stable_stringify = __commonJS({
63560
63726
  indentation += spacer;
63561
63727
  res += `
63562
63728
  ${indentation}`;
63563
- join14 = `,
63729
+ join15 = `,
63564
63730
  ${indentation}`;
63565
63731
  }
63566
63732
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -63568,13 +63734,13 @@ ${indentation}`;
63568
63734
  for (; i < maximumValuesToStringify - 1; i++) {
63569
63735
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
63570
63736
  res += tmp2 !== void 0 ? tmp2 : "null";
63571
- res += join14;
63737
+ res += join15;
63572
63738
  }
63573
63739
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
63574
63740
  res += tmp !== void 0 ? tmp : "null";
63575
63741
  if (value.length - 1 > maximumBreadth) {
63576
63742
  const removedKeys = value.length - maximumBreadth - 1;
63577
- res += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
63743
+ res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
63578
63744
  }
63579
63745
  if (spacer !== "") {
63580
63746
  res += `
@@ -63595,7 +63761,7 @@ ${originalIndentation}`;
63595
63761
  let separator = "";
63596
63762
  if (spacer !== "") {
63597
63763
  indentation += spacer;
63598
- join14 = `,
63764
+ join15 = `,
63599
63765
  ${indentation}`;
63600
63766
  whitespace = " ";
63601
63767
  }
@@ -63609,13 +63775,13 @@ ${indentation}`;
63609
63775
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
63610
63776
  if (tmp !== void 0) {
63611
63777
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
63612
- separator = join14;
63778
+ separator = join15;
63613
63779
  }
63614
63780
  }
63615
63781
  if (keyLength > maximumBreadth) {
63616
63782
  const removedKeys = keyLength - maximumBreadth;
63617
63783
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
63618
- separator = join14;
63784
+ separator = join15;
63619
63785
  }
63620
63786
  if (spacer !== "" && separator.length > 1) {
63621
63787
  res = `
@@ -63656,7 +63822,7 @@ ${originalIndentation}`;
63656
63822
  }
63657
63823
  const originalIndentation = indentation;
63658
63824
  let res = "";
63659
- let join14 = ",";
63825
+ let join15 = ",";
63660
63826
  if (Array.isArray(value)) {
63661
63827
  if (value.length === 0) {
63662
63828
  return "[]";
@@ -63669,7 +63835,7 @@ ${originalIndentation}`;
63669
63835
  indentation += spacer;
63670
63836
  res += `
63671
63837
  ${indentation}`;
63672
- join14 = `,
63838
+ join15 = `,
63673
63839
  ${indentation}`;
63674
63840
  }
63675
63841
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -63677,13 +63843,13 @@ ${indentation}`;
63677
63843
  for (; i < maximumValuesToStringify - 1; i++) {
63678
63844
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
63679
63845
  res += tmp2 !== void 0 ? tmp2 : "null";
63680
- res += join14;
63846
+ res += join15;
63681
63847
  }
63682
63848
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
63683
63849
  res += tmp !== void 0 ? tmp : "null";
63684
63850
  if (value.length - 1 > maximumBreadth) {
63685
63851
  const removedKeys = value.length - maximumBreadth - 1;
63686
- res += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
63852
+ res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
63687
63853
  }
63688
63854
  if (spacer !== "") {
63689
63855
  res += `
@@ -63696,7 +63862,7 @@ ${originalIndentation}`;
63696
63862
  let whitespace = "";
63697
63863
  if (spacer !== "") {
63698
63864
  indentation += spacer;
63699
- join14 = `,
63865
+ join15 = `,
63700
63866
  ${indentation}`;
63701
63867
  whitespace = " ";
63702
63868
  }
@@ -63705,7 +63871,7 @@ ${indentation}`;
63705
63871
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
63706
63872
  if (tmp !== void 0) {
63707
63873
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
63708
- separator = join14;
63874
+ separator = join15;
63709
63875
  }
63710
63876
  }
63711
63877
  if (spacer !== "" && separator.length > 1) {
@@ -63763,20 +63929,20 @@ ${originalIndentation}`;
63763
63929
  indentation += spacer;
63764
63930
  let res2 = `
63765
63931
  ${indentation}`;
63766
- const join15 = `,
63932
+ const join16 = `,
63767
63933
  ${indentation}`;
63768
63934
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
63769
63935
  let i = 0;
63770
63936
  for (; i < maximumValuesToStringify - 1; i++) {
63771
63937
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
63772
63938
  res2 += tmp2 !== void 0 ? tmp2 : "null";
63773
- res2 += join15;
63939
+ res2 += join16;
63774
63940
  }
63775
63941
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
63776
63942
  res2 += tmp !== void 0 ? tmp : "null";
63777
63943
  if (value.length - 1 > maximumBreadth) {
63778
63944
  const removedKeys = value.length - maximumBreadth - 1;
63779
- res2 += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
63945
+ res2 += `${join16}"... ${getItemCount(removedKeys)} not stringified"`;
63780
63946
  }
63781
63947
  res2 += `
63782
63948
  ${originalIndentation}`;
@@ -63792,16 +63958,16 @@ ${originalIndentation}`;
63792
63958
  return '"[Object]"';
63793
63959
  }
63794
63960
  indentation += spacer;
63795
- const join14 = `,
63961
+ const join15 = `,
63796
63962
  ${indentation}`;
63797
63963
  let res = "";
63798
63964
  let separator = "";
63799
63965
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
63800
63966
  if (isTypedArrayWithEntries(value)) {
63801
- res += stringifyTypedArray(value, join14, maximumBreadth);
63967
+ res += stringifyTypedArray(value, join15, maximumBreadth);
63802
63968
  keys = keys.slice(value.length);
63803
63969
  maximumPropertiesToStringify -= value.length;
63804
- separator = join14;
63970
+ separator = join15;
63805
63971
  }
63806
63972
  if (deterministic) {
63807
63973
  keys = sort(keys, comparator);
@@ -63812,13 +63978,13 @@ ${indentation}`;
63812
63978
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
63813
63979
  if (tmp !== void 0) {
63814
63980
  res += `${separator}${strEscape(key2)}: ${tmp}`;
63815
- separator = join14;
63981
+ separator = join15;
63816
63982
  }
63817
63983
  }
63818
63984
  if (keyLength > maximumBreadth) {
63819
63985
  const removedKeys = keyLength - maximumBreadth;
63820
63986
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
63821
- separator = join14;
63987
+ separator = join15;
63822
63988
  }
63823
63989
  if (separator !== "") {
63824
63990
  res = `
@@ -66848,7 +67014,7 @@ var require_pino_pretty = __commonJS({
66848
67014
  });
66849
67015
 
66850
67016
  // packages/daemon/src/logging/logger.ts
66851
- import { join as join5 } from "node:path";
67017
+ import { join as join6 } from "node:path";
66852
67018
  function shouldPrettyPrint(logLevel) {
66853
67019
  return process.env.NODE_ENV !== "production" || process.env.LOG_LEVEL === "debug" || logLevel === "debug" || logLevel === "trace";
66854
67020
  }
@@ -66895,11 +67061,498 @@ var init_logger = __esm({
66895
67061
  init_paths();
66896
67062
  import_pino = __toESM(require_pino(), 1);
66897
67063
  import_pino_pretty = __toESM(require_pino_pretty(), 1);
66898
- LOG_FILE_PATH = join5(LOGS_DIR, "io.log");
67064
+ LOG_FILE_PATH = join6(LOGS_DIR, "io.log");
66899
67065
  rootLogger = null;
66900
67066
  }
66901
67067
  });
66902
67068
 
67069
+ // packages/daemon/src/models/catalog.ts
67070
+ import { execFileSync } from "node:child_process";
67071
+ function resolveGitHubToken() {
67072
+ const envToken = process.env.GITHUB_TOKEN?.trim();
67073
+ if (envToken && envToken.length > 0) {
67074
+ return envToken;
67075
+ }
67076
+ try {
67077
+ const token = execFileSync("gh", ["auth", "token"], {
67078
+ encoding: "utf8",
67079
+ stdio: ["ignore", "pipe", "pipe"]
67080
+ }).trim();
67081
+ return token.length > 0 ? token : void 0;
67082
+ } catch {
67083
+ return void 0;
67084
+ }
67085
+ }
67086
+ async function fetchModelCatalog() {
67087
+ const token = resolveGitHubToken();
67088
+ if (!token) {
67089
+ throw new Error("No GitHub token available for model catalog fetch");
67090
+ }
67091
+ const response = await fetch(CATALOG_URL, {
67092
+ headers: {
67093
+ Accept: "application/json",
67094
+ Authorization: `Bearer ${token}`,
67095
+ "X-GitHub-Api-Version": "2024-12-01"
67096
+ }
67097
+ });
67098
+ if (!response.ok) {
67099
+ throw new Error(`Model catalog fetch failed: ${response.status} ${response.statusText}`);
67100
+ }
67101
+ const data = await response.json();
67102
+ if (!Array.isArray(data)) {
67103
+ throw new Error("Model catalog response is not an array");
67104
+ }
67105
+ const models = [];
67106
+ for (const entry of data) {
67107
+ const id = entry.id ?? entry.name;
67108
+ const displayName = entry.friendly_name ?? entry.name ?? id;
67109
+ if (id && typeof id === "string") {
67110
+ models.push({ id, displayName: displayName ?? id });
67111
+ }
67112
+ }
67113
+ return models;
67114
+ }
67115
+ var CATALOG_URL;
67116
+ var init_catalog = __esm({
67117
+ "packages/daemon/src/models/catalog.ts"() {
67118
+ "use strict";
67119
+ CATALOG_URL = "https://models.github.ai/catalog/models";
67120
+ }
67121
+ });
67122
+
67123
+ // packages/daemon/src/models/pricing-scraper.ts
67124
+ async function scrapeTokenUnitPricing() {
67125
+ const html = await fetchPage(TOKEN_UNIT_COSTS_URL);
67126
+ return parseTokenUnitTable(html);
67127
+ }
67128
+ async function scrapePremiumRequestPricing() {
67129
+ const html = await fetchPage(PREMIUM_MULTIPLIERS_URL);
67130
+ return parsePremiumMultiplierTable(html);
67131
+ }
67132
+ async function fetchPage(url2) {
67133
+ const response = await fetch(url2, {
67134
+ headers: {
67135
+ Accept: "text/html",
67136
+ "User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
67137
+ },
67138
+ redirect: "follow"
67139
+ });
67140
+ if (!response.ok) {
67141
+ throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
67142
+ }
67143
+ return response.text();
67144
+ }
67145
+ function parseTokenUnitTable(html) {
67146
+ const results = [];
67147
+ const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]*)<\/td>\s*<td[^>]*>([^<]+)<\/td>/gi;
67148
+ for (const match of html.matchAll(tableRowPattern)) {
67149
+ const modelName = cleanCellText(match[1]);
67150
+ const inputMultiplier = Number.parseFloat(match[2]);
67151
+ const cachedInput = match[3].trim().toLowerCase();
67152
+ const outputMultiplier = Number.parseFloat(match[4]);
67153
+ if (modelName && !Number.isNaN(inputMultiplier) && !Number.isNaN(outputMultiplier)) {
67154
+ results.push({
67155
+ modelName,
67156
+ inputMultiplier,
67157
+ cachedInputMultiplier: cachedInput === "n/a" || cachedInput === "" ? null : Number.parseFloat(cachedInput) || null,
67158
+ outputMultiplier
67159
+ });
67160
+ }
67161
+ }
67162
+ return results;
67163
+ }
67164
+ function parsePremiumMultiplierTable(html) {
67165
+ const results = [];
67166
+ const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<\/tr>/gi;
67167
+ for (const match of html.matchAll(tableRowPattern)) {
67168
+ const modelName = cleanCellText(match[1]);
67169
+ const multiplier = Number.parseFloat(match[2]);
67170
+ if (modelName && !Number.isNaN(multiplier) && multiplier > 0) {
67171
+ results.push({ modelName, multiplier });
67172
+ }
67173
+ }
67174
+ return results;
67175
+ }
67176
+ function cleanCellText(text) {
67177
+ return text.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " ").trim();
67178
+ }
67179
+ var TOKEN_UNIT_COSTS_URL, PREMIUM_MULTIPLIERS_URL;
67180
+ var init_pricing_scraper = __esm({
67181
+ "packages/daemon/src/models/pricing-scraper.ts"() {
67182
+ "use strict";
67183
+ TOKEN_UNIT_COSTS_URL = "https://docs.github.com/en/billing/reference/costs-for-github-models";
67184
+ PREMIUM_MULTIPLIERS_URL = "https://docs.github.com/en/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans";
67185
+ }
67186
+ });
67187
+
67188
+ // packages/daemon/src/models/seed.ts
67189
+ var SEED_MODELS;
67190
+ var init_seed = __esm({
67191
+ "packages/daemon/src/models/seed.ts"() {
67192
+ "use strict";
67193
+ SEED_MODELS = [
67194
+ {
67195
+ id: "gpt-4o-mini",
67196
+ displayName: "OpenAI GPT-4o mini",
67197
+ premiumMultiplier: 0.33,
67198
+ tokenInputMultiplier: 0.015,
67199
+ tokenOutputMultiplier: 0.06,
67200
+ cachedInputMultiplier: 75e-4,
67201
+ tier: "trivial",
67202
+ available: true
67203
+ },
67204
+ {
67205
+ id: "gpt-4o",
67206
+ displayName: "OpenAI GPT-4o",
67207
+ premiumMultiplier: 0.33,
67208
+ tokenInputMultiplier: 0.25,
67209
+ tokenOutputMultiplier: 1,
67210
+ cachedInputMultiplier: 0.125,
67211
+ tier: "trivial",
67212
+ available: true
67213
+ },
67214
+ {
67215
+ id: "gpt-5-mini",
67216
+ displayName: "GPT-5 mini",
67217
+ premiumMultiplier: 0.33,
67218
+ tokenInputMultiplier: null,
67219
+ tokenOutputMultiplier: null,
67220
+ cachedInputMultiplier: null,
67221
+ tier: "trivial",
67222
+ available: true
67223
+ },
67224
+ {
67225
+ id: "claude-sonnet-4",
67226
+ displayName: "Claude Sonnet 4",
67227
+ premiumMultiplier: 6,
67228
+ tokenInputMultiplier: 0.6,
67229
+ tokenOutputMultiplier: 2.4,
67230
+ cachedInputMultiplier: null,
67231
+ tier: "premium",
67232
+ available: true
67233
+ },
67234
+ {
67235
+ id: "claude-haiku-4.5",
67236
+ displayName: "Claude Haiku 4.5",
67237
+ premiumMultiplier: 0.33,
67238
+ tokenInputMultiplier: null,
67239
+ tokenOutputMultiplier: null,
67240
+ cachedInputMultiplier: null,
67241
+ tier: "trivial",
67242
+ available: true
67243
+ },
67244
+ {
67245
+ id: "gemini-2.5-pro",
67246
+ displayName: "Gemini 2.5 Pro",
67247
+ premiumMultiplier: 1,
67248
+ tokenInputMultiplier: null,
67249
+ tokenOutputMultiplier: null,
67250
+ cachedInputMultiplier: null,
67251
+ tier: "fast",
67252
+ available: true
67253
+ },
67254
+ {
67255
+ id: "gpt-5.1",
67256
+ displayName: "GPT-5.1",
67257
+ premiumMultiplier: 3,
67258
+ tokenInputMultiplier: null,
67259
+ tokenOutputMultiplier: null,
67260
+ cachedInputMultiplier: null,
67261
+ tier: "standard",
67262
+ available: true
67263
+ },
67264
+ {
67265
+ id: "gpt-5.2",
67266
+ displayName: "GPT-5.2",
67267
+ premiumMultiplier: 3,
67268
+ tokenInputMultiplier: null,
67269
+ tokenOutputMultiplier: null,
67270
+ cachedInputMultiplier: null,
67271
+ tier: "standard",
67272
+ available: true
67273
+ },
67274
+ {
67275
+ id: "gpt-5.4",
67276
+ displayName: "GPT-5.4",
67277
+ premiumMultiplier: 6,
67278
+ tokenInputMultiplier: null,
67279
+ tokenOutputMultiplier: null,
67280
+ cachedInputMultiplier: null,
67281
+ tier: "premium",
67282
+ available: true
67283
+ },
67284
+ {
67285
+ id: "claude-opus-4.5",
67286
+ displayName: "Claude Opus 4.5",
67287
+ premiumMultiplier: 15,
67288
+ tokenInputMultiplier: null,
67289
+ tokenOutputMultiplier: null,
67290
+ cachedInputMultiplier: null,
67291
+ tier: "premium",
67292
+ available: true
67293
+ },
67294
+ {
67295
+ id: "claude-opus-4.6",
67296
+ displayName: "Claude Opus 4.6",
67297
+ premiumMultiplier: 27,
67298
+ tokenInputMultiplier: null,
67299
+ tokenOutputMultiplier: null,
67300
+ cachedInputMultiplier: null,
67301
+ tier: "ultra",
67302
+ available: true
67303
+ },
67304
+ {
67305
+ id: "claude-opus-4.7",
67306
+ displayName: "Claude Opus 4.7",
67307
+ premiumMultiplier: 27,
67308
+ tokenInputMultiplier: null,
67309
+ tokenOutputMultiplier: null,
67310
+ cachedInputMultiplier: null,
67311
+ tier: "ultra",
67312
+ available: true
67313
+ },
67314
+ {
67315
+ id: "gpt-5.5",
67316
+ displayName: "GPT-5.5",
67317
+ premiumMultiplier: 57,
67318
+ tokenInputMultiplier: null,
67319
+ tokenOutputMultiplier: null,
67320
+ cachedInputMultiplier: null,
67321
+ tier: "ultra",
67322
+ available: true
67323
+ }
67324
+ ];
67325
+ }
67326
+ });
67327
+
67328
+ // packages/daemon/src/models/types.ts
67329
+ function computeTierFromMultiplier(premiumMultiplier) {
67330
+ if (premiumMultiplier === null) {
67331
+ return "standard";
67332
+ }
67333
+ for (const [tier, range] of Object.entries(TIER_RANGES)) {
67334
+ if (premiumMultiplier >= range.min && premiumMultiplier <= range.max) {
67335
+ return tier;
67336
+ }
67337
+ }
67338
+ return "ultra";
67339
+ }
67340
+ var TIER_RANGES, TOKEN_UNIT_PRICE;
67341
+ var init_types = __esm({
67342
+ "packages/daemon/src/models/types.ts"() {
67343
+ "use strict";
67344
+ TIER_RANGES = {
67345
+ trivial: { min: 0, max: 0.33 },
67346
+ fast: { min: 0.34, max: 1 },
67347
+ standard: { min: 1.1, max: 5 },
67348
+ premium: { min: 5.1, max: 15 },
67349
+ ultra: { min: 15.1, max: Number.POSITIVE_INFINITY }
67350
+ };
67351
+ TOKEN_UNIT_PRICE = 1e-5;
67352
+ }
67353
+ });
67354
+
67355
+ // packages/daemon/src/models/registry.ts
67356
+ function normalizeModelName(name) {
67357
+ return name.toLowerCase().replace(/^openai\s+/i, "").replace(/\s+/g, "-").replace(/[^a-z0-9.\-]/g, "").trim();
67358
+ }
67359
+ async function fetchCatalogIntoMap(modelMap, result, logger) {
67360
+ try {
67361
+ const catalogModels = await fetchModelCatalog();
67362
+ result.catalogFetched = true;
67363
+ for (const m of catalogModels) {
67364
+ const key = normalizeModelName(m.id);
67365
+ modelMap.set(key, { id: m.id, displayName: m.displayName, available: true });
67366
+ }
67367
+ } catch (error51) {
67368
+ const msg = error51 instanceof Error ? error51.message : String(error51);
67369
+ result.errors.push(`Catalog fetch failed: ${msg}`);
67370
+ logger?.warn(`Model catalog fetch failed: ${msg}`);
67371
+ }
67372
+ }
67373
+ async function scrapeTokenPricingIntoMap(modelMap, result, logger) {
67374
+ try {
67375
+ const tokenPricing = await scrapeTokenUnitPricing();
67376
+ result.tokenPricingScraped = true;
67377
+ for (const tp of tokenPricing) {
67378
+ const key = normalizeModelName(tp.modelName);
67379
+ const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
67380
+ if (existing) {
67381
+ existing.tokenInputMultiplier = tp.inputMultiplier;
67382
+ existing.tokenOutputMultiplier = tp.outputMultiplier;
67383
+ existing.cachedInputMultiplier = tp.cachedInputMultiplier;
67384
+ } else {
67385
+ modelMap.set(key, {
67386
+ id: key,
67387
+ displayName: tp.modelName,
67388
+ tokenInputMultiplier: tp.inputMultiplier,
67389
+ tokenOutputMultiplier: tp.outputMultiplier,
67390
+ cachedInputMultiplier: tp.cachedInputMultiplier,
67391
+ available: true
67392
+ });
67393
+ }
67394
+ }
67395
+ } catch (error51) {
67396
+ const msg = error51 instanceof Error ? error51.message : String(error51);
67397
+ result.errors.push(`Token pricing scrape failed: ${msg}`);
67398
+ logger?.warn(`Token pricing scrape failed: ${msg}`);
67399
+ }
67400
+ }
67401
+ async function scrapePremiumPricingIntoMap(modelMap, result, logger) {
67402
+ try {
67403
+ const premiumPricing = await scrapePremiumRequestPricing();
67404
+ result.premiumPricingScraped = true;
67405
+ for (const pp of premiumPricing) {
67406
+ const key = normalizeModelName(pp.modelName);
67407
+ const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
67408
+ if (existing) {
67409
+ existing.premiumMultiplier = pp.multiplier;
67410
+ } else {
67411
+ modelMap.set(key, {
67412
+ id: key,
67413
+ displayName: pp.modelName,
67414
+ premiumMultiplier: pp.multiplier,
67415
+ available: true
67416
+ });
67417
+ }
67418
+ }
67419
+ } catch (error51) {
67420
+ const msg = error51 instanceof Error ? error51.message : String(error51);
67421
+ result.errors.push(`Premium pricing scrape failed: ${msg}`);
67422
+ logger?.warn(`Premium pricing scrape failed: ${msg}`);
67423
+ }
67424
+ }
67425
+ async function refreshModelPricing(logger) {
67426
+ const result = {
67427
+ modelsUpdated: 0,
67428
+ catalogFetched: false,
67429
+ tokenPricingScraped: false,
67430
+ premiumPricingScraped: false,
67431
+ errors: []
67432
+ };
67433
+ const modelMap = /* @__PURE__ */ new Map();
67434
+ await fetchCatalogIntoMap(modelMap, result, logger);
67435
+ await scrapeTokenPricingIntoMap(modelMap, result, logger);
67436
+ await scrapePremiumPricingIntoMap(modelMap, result, logger);
67437
+ if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped) {
67438
+ logger?.warn("All pricing sources failed, using seed data");
67439
+ await seedFromFallback();
67440
+ result.modelsUpdated = SEED_MODELS.length;
67441
+ return result;
67442
+ }
67443
+ const db = await getDatabase();
67444
+ const now = nowIso();
67445
+ for (const model of modelMap.values()) {
67446
+ const tier = computeTierFromMultiplier(model.premiumMultiplier ?? null);
67447
+ await upsertModel(db, {
67448
+ id: model.id,
67449
+ displayName: model.displayName,
67450
+ premiumMultiplier: model.premiumMultiplier ?? null,
67451
+ tokenInputMultiplier: model.tokenInputMultiplier ?? null,
67452
+ tokenOutputMultiplier: model.tokenOutputMultiplier ?? null,
67453
+ cachedInputMultiplier: model.cachedInputMultiplier ?? null,
67454
+ tier,
67455
+ available: model.available ?? true,
67456
+ updatedAt: now
67457
+ });
67458
+ result.modelsUpdated++;
67459
+ }
67460
+ return result;
67461
+ }
67462
+ async function seedFromFallback() {
67463
+ const db = await getDatabase();
67464
+ const now = nowIso();
67465
+ for (const model of SEED_MODELS) {
67466
+ await upsertModel(db, { ...model, updatedAt: now });
67467
+ }
67468
+ }
67469
+ async function getModelPricing(modelId) {
67470
+ const db = await getDatabase();
67471
+ const result = await db.execute({
67472
+ sql: "SELECT * FROM model_pricing WHERE id = ?",
67473
+ args: [modelId]
67474
+ });
67475
+ if (result.rows.length === 0) {
67476
+ return null;
67477
+ }
67478
+ return rowToModelPricing(result.rows[0]);
67479
+ }
67480
+ function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
67481
+ if (inputMultiplier === null || outputMultiplier === null) {
67482
+ return 0;
67483
+ }
67484
+ const tokenUnits = inputTokens * inputMultiplier + outputTokens * outputMultiplier;
67485
+ return tokenUnits * TOKEN_UNIT_PRICE;
67486
+ }
67487
+ async function upsertModel(db, model) {
67488
+ await db.execute({
67489
+ sql: `INSERT INTO model_pricing (id, display_name, premium_multiplier, token_input_multiplier, token_output_multiplier, cached_input_multiplier, tier, available, updated_at)
67490
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
67491
+ ON CONFLICT(id) DO UPDATE SET
67492
+ display_name = excluded.display_name,
67493
+ premium_multiplier = COALESCE(excluded.premium_multiplier, model_pricing.premium_multiplier),
67494
+ token_input_multiplier = COALESCE(excluded.token_input_multiplier, model_pricing.token_input_multiplier),
67495
+ token_output_multiplier = COALESCE(excluded.token_output_multiplier, model_pricing.token_output_multiplier),
67496
+ cached_input_multiplier = COALESCE(excluded.cached_input_multiplier, model_pricing.cached_input_multiplier),
67497
+ tier = excluded.tier,
67498
+ available = excluded.available,
67499
+ updated_at = excluded.updated_at`,
67500
+ args: [
67501
+ model.id,
67502
+ model.displayName,
67503
+ model.premiumMultiplier,
67504
+ model.tokenInputMultiplier,
67505
+ model.tokenOutputMultiplier,
67506
+ model.cachedInputMultiplier,
67507
+ model.tier,
67508
+ model.available ? 1 : 0,
67509
+ model.updatedAt
67510
+ ]
67511
+ });
67512
+ }
67513
+ function rowToModelPricing(row) {
67514
+ return {
67515
+ id: asString(row.id),
67516
+ displayName: asString(row.display_name),
67517
+ premiumMultiplier: asNullableNumber(row.premium_multiplier),
67518
+ tokenInputMultiplier: asNullableNumber(row.token_input_multiplier),
67519
+ tokenOutputMultiplier: asNullableNumber(row.token_output_multiplier),
67520
+ cachedInputMultiplier: asNullableNumber(row.cached_input_multiplier),
67521
+ tier: asString(row.tier),
67522
+ available: asNumber(row.available) === 1,
67523
+ updatedAt: asString(row.updated_at)
67524
+ };
67525
+ }
67526
+ function findClosestKey(map2, targetKey) {
67527
+ for (const [key, value] of map2) {
67528
+ if (key.includes(targetKey) || targetKey.includes(key)) {
67529
+ return value;
67530
+ }
67531
+ }
67532
+ return void 0;
67533
+ }
67534
+ var init_registry = __esm({
67535
+ "packages/daemon/src/models/registry.ts"() {
67536
+ "use strict";
67537
+ init_db();
67538
+ init_catalog();
67539
+ init_pricing_scraper();
67540
+ init_seed();
67541
+ init_types();
67542
+ }
67543
+ });
67544
+
67545
+ // packages/daemon/src/models/index.ts
67546
+ var init_models = __esm({
67547
+ "packages/daemon/src/models/index.ts"() {
67548
+ "use strict";
67549
+ init_catalog();
67550
+ init_registry();
67551
+ init_seed();
67552
+ init_types();
67553
+ }
67554
+ });
67555
+
66903
67556
  // packages/daemon/src/orchestrator/system-prompt.ts
66904
67557
  function formatSquadRoster(squads) {
66905
67558
  if (squads.length === 0) {
@@ -67003,150 +67656,10 @@ var init_reset = __esm({
67003
67656
  }
67004
67657
  });
67005
67658
 
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
67659
  // packages/daemon/src/copilot/client.ts
67147
- import { execFileSync } from "node:child_process";
67660
+ import { execFileSync as execFileSync2 } from "node:child_process";
67148
67661
  import { mkdirSync as mkdirSync2 } from "node:fs";
67149
- import { join as join6 } from "node:path";
67662
+ import { join as join7 } from "node:path";
67150
67663
  import { CopilotClient } from "@github/copilot-sdk";
67151
67664
  function readTokenFromEnvironment() {
67152
67665
  const token = process.env.GITHUB_TOKEN?.trim();
@@ -67154,7 +67667,7 @@ function readTokenFromEnvironment() {
67154
67667
  }
67155
67668
  function readTokenFromGhCli() {
67156
67669
  try {
67157
- const token = execFileSync("gh", ["auth", "token"], {
67670
+ const token = execFileSync2("gh", ["auth", "token"], {
67158
67671
  encoding: "utf8",
67159
67672
  stdio: ["ignore", "pipe", "pipe"]
67160
67673
  }).trim();
@@ -67163,7 +67676,7 @@ function readTokenFromGhCli() {
67163
67676
  return void 0;
67164
67677
  }
67165
67678
  }
67166
- function resolveGitHubToken() {
67679
+ function resolveGitHubToken2() {
67167
67680
  return readTokenFromEnvironment() ?? readTokenFromGhCli();
67168
67681
  }
67169
67682
  function buildClientOptions(token) {
@@ -67199,7 +67712,7 @@ async function initCopilotClient() {
67199
67712
  }
67200
67713
  copilotClientInitPromise = (async () => {
67201
67714
  try {
67202
- const token = resolveGitHubToken();
67715
+ const token = resolveGitHubToken2();
67203
67716
  if (token === void 0) {
67204
67717
  throw new Error(
67205
67718
  `Unable to find a GitHub token for Copilot SDK authentication. ${COPILOT_AUTH_ERROR_HINT}`
@@ -67232,7 +67745,7 @@ var init_client = __esm({
67232
67745
  "use strict";
67233
67746
  init_paths();
67234
67747
  COPILOT_AUTH_ERROR_HINT = "Set GITHUB_TOKEN or authenticate with the GitHub CLI (`gh auth login`) so `gh auth token` returns a valid token.";
67235
- COPILOT_BASE_DIRECTORY = join6(DATA_DIR, "copilot-sdk");
67748
+ COPILOT_BASE_DIRECTORY = join7(DATA_DIR, "copilot-sdk");
67236
67749
  copilotClientSingleton = null;
67237
67750
  copilotClientInitPromise = null;
67238
67751
  }
@@ -67392,7 +67905,7 @@ var init_session = __esm({
67392
67905
 
67393
67906
  // packages/daemon/src/skills/loader.ts
67394
67907
  import { readFile as readFile4, readdir as readdir2 } from "node:fs/promises";
67395
- import { basename as basename3, dirname as dirname5, join as join7 } from "node:path";
67908
+ import { basename as basename3, dirname as dirname5, join as join8 } from "node:path";
67396
67909
  async function scanSkills() {
67397
67910
  const skillFilePaths = await collectSkillFiles(SKILLS_DIR);
67398
67911
  const skills = await Promise.all(skillFilePaths.map((filePath) => readSkillFromFile(filePath)));
@@ -67403,7 +67916,7 @@ async function collectSkillFiles(directory) {
67403
67916
  const entries = await readdir2(directory, { withFileTypes: true });
67404
67917
  const skillFiles = [];
67405
67918
  for (const entry of entries) {
67406
- const entryPath = join7(directory, entry.name);
67919
+ const entryPath = join8(directory, entry.name);
67407
67920
  if (entry.isDirectory()) {
67408
67921
  skillFiles.push(...await collectSkillFiles(entryPath));
67409
67922
  continue;
@@ -67447,7 +67960,7 @@ var init_loader = __esm({
67447
67960
 
67448
67961
  // packages/daemon/src/skills/manager.ts
67449
67962
  import { mkdir as mkdir5, readFile as readFile5, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
67450
- import { basename as basename4, dirname as dirname6, join as join8 } from "node:path";
67963
+ import { basename as basename4, dirname as dirname6, join as join9 } from "node:path";
67451
67964
  async function installSkill2(url2) {
67452
67965
  const response = await fetch(url2);
67453
67966
  if (!response.ok) {
@@ -67457,8 +67970,8 @@ async function installSkill2(url2) {
67457
67970
  }
67458
67971
  const skillMarkdown = await response.text();
67459
67972
  const skillId = getSkillIdFromUrl(url2);
67460
- const skillDirectory = join8(SKILLS_DIR, skillId);
67461
- const skillPath = join8(skillDirectory, "SKILL.md");
67973
+ const skillDirectory = join9(SKILLS_DIR, skillId);
67974
+ const skillPath = join9(skillDirectory, "SKILL.md");
67462
67975
  const parsed = (0, import_gray_matter3.default)(skillMarkdown);
67463
67976
  const installedSkill = {
67464
67977
  id: skillId,
@@ -67472,7 +67985,7 @@ async function installSkill2(url2) {
67472
67985
  return installedSkill;
67473
67986
  }
67474
67987
  async function removeSkill2(id) {
67475
- await rm3(join8(SKILLS_DIR, id), { recursive: true, force: true });
67988
+ await rm3(join9(SKILLS_DIR, id), { recursive: true, force: true });
67476
67989
  const lockFile = await readSkillsLock();
67477
67990
  lockFile.skills = lockFile.skills.filter((skill) => skill.id !== id);
67478
67991
  await writeSkillsLock2(lockFile);
@@ -68084,7 +68597,7 @@ var init_history = __esm({
68084
68597
  // packages/daemon/src/execution/agent.ts
68085
68598
  import { exec as exec2 } from "node:child_process";
68086
68599
  import { mkdir as mkdir7, readFile as readFile7, readdir as readdir4, stat as stat3, writeFile as writeFile6 } from "node:fs/promises";
68087
- import { dirname as dirname8, extname as extname3, isAbsolute, join as join9, relative as relative3, resolve as resolve4 } from "node:path";
68600
+ import { dirname as dirname8, extname as extname3, isAbsolute, join as join10, relative as relative3, resolve as resolve4 } from "node:path";
68088
68601
  import { promisify as promisify2 } from "node:util";
68089
68602
  import {
68090
68603
  CopilotClient as CopilotClient2,
@@ -68115,13 +68628,24 @@ function mergeUsage(target, usage) {
68115
68628
  }
68116
68629
  async function persistUsage(member, usageEvents) {
68117
68630
  for (const usage of usageEvents) {
68631
+ const model = usage.model;
68632
+ const pricing = await getModelPricing(model);
68633
+ const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
68634
+ const tokenUnitCost = pricing ? calculateTokenUnitCost(
68635
+ usage.inputTokens ?? 0,
68636
+ usage.outputTokens ?? 0,
68637
+ pricing.tokenInputMultiplier,
68638
+ pricing.tokenOutputMultiplier
68639
+ ) : 0;
68118
68640
  await recordUsage({
68119
68641
  squadId: member.squadId,
68120
68642
  agentId: member.id,
68121
- model: usage.model,
68643
+ model,
68122
68644
  inputTokens: usage.inputTokens ?? 0,
68123
68645
  outputTokens: usage.outputTokens ?? 0,
68124
- cost: usage.cost ?? 0
68646
+ cost: 0,
68647
+ premiumRequestCost,
68648
+ tokenUnitCost
68125
68649
  });
68126
68650
  }
68127
68651
  }
@@ -68139,7 +68663,7 @@ async function collectFiles(directory, recursive, output2) {
68139
68663
  if (output2.length >= MAX_LIST_RESULTS) {
68140
68664
  return;
68141
68665
  }
68142
- const fullPath = join9(directory, entry.name);
68666
+ const fullPath = join10(directory, entry.name);
68143
68667
  output2.push(fullPath);
68144
68668
  if (recursive && entry.isDirectory()) {
68145
68669
  await collectFiles(fullPath, recursive, output2);
@@ -68339,7 +68863,7 @@ async function executeAgentTask(member, task, worktreePath, options2) {
68339
68863
  client2 = new CopilotClient2({ workingDirectory: worktreePath });
68340
68864
  await client2.start();
68341
68865
  const session = await client2.createSession({
68342
- model: member.model ?? STANDARD_MODEL,
68866
+ model: member.model ?? DEFAULT_MODEL,
68343
68867
  workingDirectory: worktreePath,
68344
68868
  tools,
68345
68869
  availableTools: ["custom:*"],
@@ -68396,6 +68920,7 @@ var init_agent = __esm({
68396
68920
  "packages/daemon/src/execution/agent.ts"() {
68397
68921
  "use strict";
68398
68922
  init_dist();
68923
+ init_registry();
68399
68924
  init_store2();
68400
68925
  init_history();
68401
68926
  execAsync2 = promisify2(exec2);
@@ -68495,7 +69020,7 @@ Return strict JSON in this shape:
68495
69020
  // packages/daemon/src/execution/planning.ts
68496
69021
  import { exec as exec3 } from "node:child_process";
68497
69022
  import { access, readFile as readFile8 } from "node:fs/promises";
68498
- import { join as join10 } from "node:path";
69023
+ import { join as join11 } from "node:path";
68499
69024
  import { promisify as promisify3 } from "node:util";
68500
69025
  import { CopilotClient as CopilotClient3, approveAll as approveAll3 } from "@github/copilot-sdk";
68501
69026
  async function fileExists(path) {
@@ -68508,7 +69033,7 @@ async function fileExists(path) {
68508
69033
  }
68509
69034
  async function buildRepoContext(repoPath) {
68510
69035
  for (const candidate of README_CANDIDATES) {
68511
- const readmePath = join10(repoPath, candidate);
69036
+ const readmePath = join11(repoPath, candidate);
68512
69037
  if (await fileExists(readmePath)) {
68513
69038
  const content = await readFile8(readmePath, "utf8");
68514
69039
  return content.slice(0, MAX_REPO_CONTEXT_LENGTH);
@@ -68594,7 +69119,7 @@ Return strict JSON in this shape:
68594
69119
  client2 = new CopilotClient3({ workingDirectory: repoPath });
68595
69120
  await client2.start();
68596
69121
  const session = await client2.createSession({
68597
- model: PREMIUM_MODEL,
69122
+ model: DEFAULT_MODEL,
68598
69123
  workingDirectory: repoPath,
68599
69124
  onPermissionRequest: approveAll3,
68600
69125
  systemMessage: {
@@ -68642,7 +69167,7 @@ var init_planning = __esm({
68642
69167
  // packages/daemon/src/execution/worktree.ts
68643
69168
  import { exec as exec4 } from "node:child_process";
68644
69169
  import { mkdir as mkdir8, rm as rm4 } from "node:fs/promises";
68645
- import { join as join11 } from "node:path";
69170
+ import { join as join12 } from "node:path";
68646
69171
  import { promisify as promisify4 } from "node:util";
68647
69172
  function sanitizeBranchName(branchName) {
68648
69173
  return branchName.replace(/[^a-zA-Z0-9._-]+/g, "-");
@@ -68659,7 +69184,7 @@ async function branchExists(repoPath, branchName) {
68659
69184
  return output2.length > 0;
68660
69185
  }
68661
69186
  function getWorktreePath(repoPath, branchName) {
68662
- return join11(repoPath, ".worktrees", sanitizeBranchName(branchName));
69187
+ return join12(repoPath, ".worktrees", sanitizeBranchName(branchName));
68663
69188
  }
68664
69189
  async function listWorktrees(repoPath) {
68665
69190
  const output2 = await runGit("git worktree list --porcelain", repoPath);
@@ -68709,7 +69234,7 @@ async function listWorktrees(repoPath) {
68709
69234
  }
68710
69235
  async function createWorktree(repoPath, branchName, baseBranch) {
68711
69236
  const worktreePath = getWorktreePath(repoPath, branchName);
68712
- await mkdir8(join11(repoPath, ".worktrees"), { recursive: true });
69237
+ await mkdir8(join12(repoPath, ".worktrees"), { recursive: true });
68713
69238
  const existing = (await listWorktrees(repoPath)).find(
68714
69239
  (worktree) => worktree.path === worktreePath
68715
69240
  );
@@ -68731,7 +69256,7 @@ async function createWorktree(repoPath, branchName, baseBranch) {
68731
69256
  }
68732
69257
  }
68733
69258
  async function cleanupWorktree(worktreePath) {
68734
- const parentRepoPath = join11(worktreePath, "..", "..");
69259
+ const parentRepoPath = join12(worktreePath, "..", "..");
68735
69260
  try {
68736
69261
  await runGit(`git worktree remove ${JSON.stringify(worktreePath)}`, parentRepoPath);
68737
69262
  } catch (error51) {
@@ -68770,7 +69295,7 @@ var init_worktree = __esm({
68770
69295
  // packages/daemon/src/execution/pr.ts
68771
69296
  import { exec as exec5 } from "node:child_process";
68772
69297
  import { rm as rm5, writeFile as writeFile7 } from "node:fs/promises";
68773
- import { join as join12 } from "node:path";
69298
+ import { join as join13 } from "node:path";
68774
69299
  import { promisify as promisify5 } from "node:util";
68775
69300
  async function runCommand(command, cwd) {
68776
69301
  const { stdout } = await execAsync5(command, {
@@ -68784,7 +69309,7 @@ function extractUrl(output2) {
68784
69309
  }
68785
69310
  function buildBodyFilePath(repoPath, branchName) {
68786
69311
  const safeBranchName = branchName.replace(/[^a-zA-Z0-9._-]+/g, "-");
68787
- return join12(repoPath, `.io-pr-body-${safeBranchName}.md`);
69312
+ return join13(repoPath, `.io-pr-body-${safeBranchName}.md`);
68788
69313
  }
68789
69314
  function buildPrBody(objective, plan, taskSummaries, qaOutcome) {
68790
69315
  const tasks = taskSummaries.map((summary) => `- ${summary}`).join("\n") || "- No task summaries recorded.";
@@ -68885,7 +69410,7 @@ Return strict JSON:
68885
69410
  client2 = new CopilotClient4({ workingDirectory: worktreePath });
68886
69411
  await client2.start();
68887
69412
  const session = await client2.createSession({
68888
- model: qaMember.model ?? STANDARD_MODEL,
69413
+ model: qaMember.model ?? DEFAULT_MODEL,
68889
69414
  workingDirectory: worktreePath,
68890
69415
  onPermissionRequest: approveAll4,
68891
69416
  systemMessage: {
@@ -69017,7 +69542,7 @@ Return strict JSON:
69017
69542
  client2 = new CopilotClient5();
69018
69543
  await client2.start();
69019
69544
  const session = await client2.createSession({
69020
- model: teamLead.model ?? PREMIUM_MODEL,
69545
+ model: teamLead.model ?? DEFAULT_MODEL,
69021
69546
  onPermissionRequest: approveAll5,
69022
69547
  systemMessage: {
69023
69548
  content: `${TEAM_LEAD_PROMPT}
@@ -69117,7 +69642,7 @@ var init_tasks = __esm({
69117
69642
  // packages/daemon/src/execution/runner.ts
69118
69643
  import { exec as exec7 } from "node:child_process";
69119
69644
  import { access as access2 } from "node:fs/promises";
69120
- import { basename as basename5, join as join13 } from "node:path";
69645
+ import { basename as basename5, join as join14 } from "node:path";
69121
69646
  import { promisify as promisify7 } from "node:util";
69122
69647
  async function pathExists(path) {
69123
69648
  try {
@@ -69137,12 +69662,12 @@ async function runGit2(command, cwd) {
69137
69662
  async function resolveRepoPath(repoUrl, repoName) {
69138
69663
  const candidates = [
69139
69664
  process.cwd(),
69140
- join13(process.cwd(), repoName),
69141
- join13(process.cwd(), "repos", repoName),
69142
- join13(process.cwd(), "..", repoName)
69665
+ join14(process.cwd(), repoName),
69666
+ join14(process.cwd(), "repos", repoName),
69667
+ join14(process.cwd(), "..", repoName)
69143
69668
  ];
69144
69669
  for (const candidate of candidates) {
69145
- if (!await pathExists(join13(candidate, ".git"))) {
69670
+ if (!await pathExists(join14(candidate, ".git"))) {
69146
69671
  continue;
69147
69672
  }
69148
69673
  const remoteUrl = await runGit2("git remote get-url origin", candidate).catch(() => "");
@@ -69507,12 +70032,9 @@ function buildMandatoryRoles() {
69507
70032
  function modelForRole(role) {
69508
70033
  const normalized = slugifyRole(role);
69509
70034
  if (normalized === "team-lead") {
69510
- return PREMIUM_MODEL;
70035
+ return DEFAULT_MODEL;
69511
70036
  }
69512
- if (normalized === "qa") {
69513
- return STANDARD_MODEL;
69514
- }
69515
- return STANDARD_MODEL;
70037
+ return DEFAULT_MODEL;
69516
70038
  }
69517
70039
  function systemPromptForRole(role, repoContext) {
69518
70040
  const normalized = slugifyRole(role);
@@ -69544,7 +70066,7 @@ ${ROLE_GENERATION_PROMPT}`;
69544
70066
  client2 = new CopilotClient6();
69545
70067
  await client2.start();
69546
70068
  const session = await client2.createSession({
69547
- model: PREMIUM_MODEL,
70069
+ model: DEFAULT_MODEL,
69548
70070
  onPermissionRequest: approveAll6,
69549
70071
  systemMessage: {
69550
70072
  content: ROLE_GENERATION_PROMPT
@@ -69983,9 +70505,9 @@ var init_orchestrator = __esm({
69983
70505
  "use strict";
69984
70506
  init_dist();
69985
70507
  init_reset();
69986
- init_router();
69987
70508
  init_session();
69988
70509
  init_logger();
70510
+ init_registry();
69989
70511
  init_skills2();
69990
70512
  init_store2();
69991
70513
  init_episodes();
@@ -70078,8 +70600,7 @@ var init_orchestrator = __esm({
70078
70600
  skillsContext,
70079
70601
  conversationSummary: [this.latestResetSummary, conversationSummary].filter(Boolean).join("\n\n")
70080
70602
  });
70081
- const route = routeMessage(normalizedMessage);
70082
- await this.refreshSession(route.model, systemPrompt);
70603
+ await this.refreshSession(this.config.defaultModel, systemPrompt);
70083
70604
  const sendResult = await sendMessage(
70084
70605
  this.requireActiveSession(),
70085
70606
  normalizedMessage,
@@ -70180,12 +70701,25 @@ var init_orchestrator = __esm({
70180
70701
  }
70181
70702
  async refreshSession(model, systemPrompt) {
70182
70703
  await this.disconnectSession();
70183
- this.activeSession = await createSession({
70184
- model,
70185
- systemPrompt,
70186
- tools: createBoundOrchestratorTools(this.config)
70187
- });
70188
- this.activeModel = model;
70704
+ try {
70705
+ this.activeSession = await createSession({
70706
+ model,
70707
+ systemPrompt,
70708
+ tools: createBoundOrchestratorTools(this.config)
70709
+ });
70710
+ this.activeModel = model;
70711
+ } catch (error51) {
70712
+ if (model !== DEFAULT_MODEL) {
70713
+ this.activeSession = await createSession({
70714
+ model: DEFAULT_MODEL,
70715
+ systemPrompt,
70716
+ tools: createBoundOrchestratorTools(this.config)
70717
+ });
70718
+ this.activeModel = DEFAULT_MODEL;
70719
+ } else {
70720
+ throw error51;
70721
+ }
70722
+ }
70189
70723
  }
70190
70724
  async disconnectSession() {
70191
70725
  if (!this.activeSession) {
@@ -70207,11 +70741,22 @@ var init_orchestrator = __esm({
70207
70741
  if (usage.inputTokens === 0 && usage.outputTokens === 0) {
70208
70742
  return;
70209
70743
  }
70744
+ const model = usage.model || this.activeModel || this.config.defaultModel;
70745
+ const pricing = await getModelPricing(model);
70746
+ const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
70747
+ const tokenUnitCost = pricing ? calculateTokenUnitCost(
70748
+ usage.inputTokens,
70749
+ usage.outputTokens,
70750
+ pricing.tokenInputMultiplier,
70751
+ pricing.tokenOutputMultiplier
70752
+ ) : 0;
70210
70753
  await recordUsage({
70211
- model: usage.model || this.activeModel || this.config.defaultModel,
70754
+ model,
70212
70755
  inputTokens: usage.inputTokens,
70213
70756
  outputTokens: usage.outputTokens,
70214
- cost: 0
70757
+ cost: 0,
70758
+ premiumRequestCost,
70759
+ tokenUnitCost
70215
70760
  });
70216
70761
  }
70217
70762
  };
@@ -83818,6 +84363,7 @@ function registerShutdownHandlers(logger, onShutdown) {
83818
84363
  }
83819
84364
  async function main() {
83820
84365
  let logger;
84366
+ let pricingRefreshTimer = null;
83821
84367
  try {
83822
84368
  ensureDataDirectories();
83823
84369
  const config2 = loadConfig();
@@ -83825,6 +84371,12 @@ async function main() {
83825
84371
  logger.info(`IO Daemon v${APP_VERSION} starting...`);
83826
84372
  await initDatabase();
83827
84373
  logger.info("Database initialized");
84374
+ const pricingResult = await refreshModelPricing(logger);
84375
+ if (pricingResult.modelsUpdated === 0) {
84376
+ logger.warn("Model pricing refresh returned 0 models, seeding with fallback");
84377
+ await seedFromFallback();
84378
+ }
84379
+ logger.info({ modelsUpdated: pricingResult.modelsUpdated }, "Model pricing initialized");
83828
84380
  await scanSkills();
83829
84381
  logger.info("Skills scanned");
83830
84382
  const orchestrator2 = createOrchestrator(config2, eventBus);
@@ -83833,12 +84385,21 @@ async function main() {
83833
84385
  const scheduler = createScheduler(orchestrator2, eventBus);
83834
84386
  scheduler.start();
83835
84387
  logger.info("Scheduler started");
84388
+ const refreshIntervalMs = config2.pricingRefreshHours * 60 * 60 * 1e3;
84389
+ pricingRefreshTimer = setInterval(() => {
84390
+ void refreshModelPricing(logger).catch((err) => {
84391
+ logger?.warn({ err }, "Periodic model pricing refresh failed");
84392
+ });
84393
+ }, refreshIntervalMs);
83836
84394
  setChatOrchestrator(orchestrator2);
83837
84395
  const apiServer = createApiServer(config2);
83838
84396
  const telegramBot = createTelegramBot(config2, orchestrator2);
83839
84397
  telegramBot?.start();
83840
84398
  createTelegramNotifier(telegramBot, config2, eventBus);
83841
84399
  registerShutdownHandlers(logger, async () => {
84400
+ if (pricingRefreshTimer) {
84401
+ clearInterval(pricingRefreshTimer);
84402
+ }
83842
84403
  scheduler.stop();
83843
84404
  telegramBot?.stop();
83844
84405
  apiServer.server.close();
@@ -83863,6 +84424,7 @@ var init_index = __esm({
83863
84424
  init_data_dir();
83864
84425
  init_event_bus();
83865
84426
  init_logger();
84427
+ init_models();
83866
84428
  init_orchestrator2();
83867
84429
  init_scheduler();
83868
84430
  init_skills2();