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.
@@ -27989,7 +27989,7 @@ var require_view = __commonJS({
27989
27989
  var dirname9 = path.dirname;
27990
27990
  var basename6 = path.basename;
27991
27991
  var extname4 = path.extname;
27992
- var join14 = path.join;
27992
+ var join15 = path.join;
27993
27993
  var resolve5 = path.resolve;
27994
27994
  module2.exports = View;
27995
27995
  function View(name, options2) {
@@ -28051,12 +28051,12 @@ var require_view = __commonJS({
28051
28051
  };
28052
28052
  View.prototype.resolve = function resolve6(dir, file2) {
28053
28053
  var ext = this.ext;
28054
- var path2 = join14(dir, file2);
28054
+ var path2 = join15(dir, file2);
28055
28055
  var stat4 = tryStat(path2);
28056
28056
  if (stat4 && stat4.isFile()) {
28057
28057
  return path2;
28058
28058
  }
28059
- path2 = join14(dir, basename6(file2, ext), "index" + ext);
28059
+ path2 = join15(dir, basename6(file2, ext), "index" + ext);
28060
28060
  stat4 = tryStat(path2);
28061
28061
  if (stat4 && stat4.isFile()) {
28062
28062
  return path2;
@@ -31761,7 +31761,7 @@ var require_send = __commonJS({
31761
31761
  var Stream = __require("stream");
31762
31762
  var util = __require("util");
31763
31763
  var extname4 = path.extname;
31764
- var join14 = path.join;
31764
+ var join15 = path.join;
31765
31765
  var normalize = path.normalize;
31766
31766
  var resolve5 = path.resolve;
31767
31767
  var sep = path.sep;
@@ -31933,7 +31933,7 @@ var require_send = __commonJS({
31933
31933
  return res;
31934
31934
  }
31935
31935
  parts = path2.split(sep);
31936
- path2 = normalize(join14(root, path2));
31936
+ path2 = normalize(join15(root, path2));
31937
31937
  } else {
31938
31938
  if (UP_PATH_REGEXP.test(path2)) {
31939
31939
  debug('malicious path "%s"', path2);
@@ -32066,7 +32066,7 @@ var require_send = __commonJS({
32066
32066
  if (err) return self.onStatError(err);
32067
32067
  return self.error(404);
32068
32068
  }
32069
- var p = join14(path2, self._index[i]);
32069
+ var p = join15(path2, self._index[i]);
32070
32070
  debug('stat "%s"', p);
32071
32071
  fs.stat(p, function(err2, stat4) {
32072
32072
  if (err2) return next(err2);
@@ -42076,7 +42076,7 @@ var require_thread_stream = __commonJS({
42076
42076
  var { version: version2 } = require_package();
42077
42077
  var { EventEmitter: EventEmitter2 } = __require("events");
42078
42078
  var { Worker } = __require("worker_threads");
42079
- var { join: join14 } = __require("path");
42079
+ var { join: join15 } = __require("path");
42080
42080
  var { pathToFileURL: pathToFileURL2 } = __require("url");
42081
42081
  var { wait } = require_wait();
42082
42082
  var {
@@ -42119,7 +42119,7 @@ var require_thread_stream = __commonJS({
42119
42119
  function createWorker(stream, opts) {
42120
42120
  const { filename, workerData } = opts;
42121
42121
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
42122
- const toExecute = bundlerOverrides["thread-stream-worker"] || join14(__dirname, "lib", "worker.js");
42122
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join15(__dirname, "lib", "worker.js");
42123
42123
  const worker = new Worker(toExecute, {
42124
42124
  ...opts.workerOpts,
42125
42125
  trackUnmanagedFds: false,
@@ -42522,7 +42522,7 @@ var require_transport = __commonJS({
42522
42522
  "use strict";
42523
42523
  var { createRequire } = __require("module");
42524
42524
  var getCallers = require_caller();
42525
- var { join: join14, isAbsolute: isAbsolute2, sep } = __require("node:path");
42525
+ var { join: join15, isAbsolute: isAbsolute2, sep } = __require("node:path");
42526
42526
  var sleep = require_atomic_sleep();
42527
42527
  var onExit = require_on_exit_leak_free();
42528
42528
  var ThreadStream = require_thread_stream();
@@ -42585,7 +42585,7 @@ var require_transport = __commonJS({
42585
42585
  throw new Error("only one of target or targets can be specified");
42586
42586
  }
42587
42587
  if (targets) {
42588
- target = bundlerOverrides["pino-worker"] || join14(__dirname, "worker.js");
42588
+ target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
42589
42589
  options2.targets = targets.filter((dest) => dest.target).map((dest) => {
42590
42590
  return {
42591
42591
  ...dest,
@@ -42603,7 +42603,7 @@ var require_transport = __commonJS({
42603
42603
  });
42604
42604
  });
42605
42605
  } else if (pipeline) {
42606
- target = bundlerOverrides["pino-worker"] || join14(__dirname, "worker.js");
42606
+ target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
42607
42607
  options2.pipelines = [pipeline.map((dest) => {
42608
42608
  return {
42609
42609
  ...dest,
@@ -42625,7 +42625,7 @@ var require_transport = __commonJS({
42625
42625
  return origin;
42626
42626
  }
42627
42627
  if (origin === "pino/file") {
42628
- return join14(__dirname, "..", "file.js");
42628
+ return join15(__dirname, "..", "file.js");
42629
42629
  }
42630
42630
  let fixTarget2;
42631
42631
  for (const filePath of callers) {
@@ -43614,7 +43614,7 @@ var require_safe_stable_stringify = __commonJS({
43614
43614
  return circularValue;
43615
43615
  }
43616
43616
  let res = "";
43617
- let join14 = ",";
43617
+ let join15 = ",";
43618
43618
  const originalIndentation = indentation;
43619
43619
  if (Array.isArray(value)) {
43620
43620
  if (value.length === 0) {
@@ -43628,7 +43628,7 @@ var require_safe_stable_stringify = __commonJS({
43628
43628
  indentation += spacer;
43629
43629
  res += `
43630
43630
  ${indentation}`;
43631
- join14 = `,
43631
+ join15 = `,
43632
43632
  ${indentation}`;
43633
43633
  }
43634
43634
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -43636,13 +43636,13 @@ ${indentation}`;
43636
43636
  for (; i < maximumValuesToStringify - 1; i++) {
43637
43637
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
43638
43638
  res += tmp2 !== void 0 ? tmp2 : "null";
43639
- res += join14;
43639
+ res += join15;
43640
43640
  }
43641
43641
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
43642
43642
  res += tmp !== void 0 ? tmp : "null";
43643
43643
  if (value.length - 1 > maximumBreadth) {
43644
43644
  const removedKeys = value.length - maximumBreadth - 1;
43645
- res += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
43645
+ res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
43646
43646
  }
43647
43647
  if (spacer !== "") {
43648
43648
  res += `
@@ -43663,7 +43663,7 @@ ${originalIndentation}`;
43663
43663
  let separator = "";
43664
43664
  if (spacer !== "") {
43665
43665
  indentation += spacer;
43666
- join14 = `,
43666
+ join15 = `,
43667
43667
  ${indentation}`;
43668
43668
  whitespace = " ";
43669
43669
  }
@@ -43677,13 +43677,13 @@ ${indentation}`;
43677
43677
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
43678
43678
  if (tmp !== void 0) {
43679
43679
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
43680
- separator = join14;
43680
+ separator = join15;
43681
43681
  }
43682
43682
  }
43683
43683
  if (keyLength > maximumBreadth) {
43684
43684
  const removedKeys = keyLength - maximumBreadth;
43685
43685
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
43686
- separator = join14;
43686
+ separator = join15;
43687
43687
  }
43688
43688
  if (spacer !== "" && separator.length > 1) {
43689
43689
  res = `
@@ -43724,7 +43724,7 @@ ${originalIndentation}`;
43724
43724
  }
43725
43725
  const originalIndentation = indentation;
43726
43726
  let res = "";
43727
- let join14 = ",";
43727
+ let join15 = ",";
43728
43728
  if (Array.isArray(value)) {
43729
43729
  if (value.length === 0) {
43730
43730
  return "[]";
@@ -43737,7 +43737,7 @@ ${originalIndentation}`;
43737
43737
  indentation += spacer;
43738
43738
  res += `
43739
43739
  ${indentation}`;
43740
- join14 = `,
43740
+ join15 = `,
43741
43741
  ${indentation}`;
43742
43742
  }
43743
43743
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -43745,13 +43745,13 @@ ${indentation}`;
43745
43745
  for (; i < maximumValuesToStringify - 1; i++) {
43746
43746
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
43747
43747
  res += tmp2 !== void 0 ? tmp2 : "null";
43748
- res += join14;
43748
+ res += join15;
43749
43749
  }
43750
43750
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
43751
43751
  res += tmp !== void 0 ? tmp : "null";
43752
43752
  if (value.length - 1 > maximumBreadth) {
43753
43753
  const removedKeys = value.length - maximumBreadth - 1;
43754
- res += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
43754
+ res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
43755
43755
  }
43756
43756
  if (spacer !== "") {
43757
43757
  res += `
@@ -43764,7 +43764,7 @@ ${originalIndentation}`;
43764
43764
  let whitespace = "";
43765
43765
  if (spacer !== "") {
43766
43766
  indentation += spacer;
43767
- join14 = `,
43767
+ join15 = `,
43768
43768
  ${indentation}`;
43769
43769
  whitespace = " ";
43770
43770
  }
@@ -43773,7 +43773,7 @@ ${indentation}`;
43773
43773
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
43774
43774
  if (tmp !== void 0) {
43775
43775
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
43776
- separator = join14;
43776
+ separator = join15;
43777
43777
  }
43778
43778
  }
43779
43779
  if (spacer !== "" && separator.length > 1) {
@@ -43831,20 +43831,20 @@ ${originalIndentation}`;
43831
43831
  indentation += spacer;
43832
43832
  let res2 = `
43833
43833
  ${indentation}`;
43834
- const join15 = `,
43834
+ const join16 = `,
43835
43835
  ${indentation}`;
43836
43836
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
43837
43837
  let i = 0;
43838
43838
  for (; i < maximumValuesToStringify - 1; i++) {
43839
43839
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
43840
43840
  res2 += tmp2 !== void 0 ? tmp2 : "null";
43841
- res2 += join15;
43841
+ res2 += join16;
43842
43842
  }
43843
43843
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
43844
43844
  res2 += tmp !== void 0 ? tmp : "null";
43845
43845
  if (value.length - 1 > maximumBreadth) {
43846
43846
  const removedKeys = value.length - maximumBreadth - 1;
43847
- res2 += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
43847
+ res2 += `${join16}"... ${getItemCount(removedKeys)} not stringified"`;
43848
43848
  }
43849
43849
  res2 += `
43850
43850
  ${originalIndentation}`;
@@ -43860,16 +43860,16 @@ ${originalIndentation}`;
43860
43860
  return '"[Object]"';
43861
43861
  }
43862
43862
  indentation += spacer;
43863
- const join14 = `,
43863
+ const join15 = `,
43864
43864
  ${indentation}`;
43865
43865
  let res = "";
43866
43866
  let separator = "";
43867
43867
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
43868
43868
  if (isTypedArrayWithEntries(value)) {
43869
- res += stringifyTypedArray(value, join14, maximumBreadth);
43869
+ res += stringifyTypedArray(value, join15, maximumBreadth);
43870
43870
  keys = keys.slice(value.length);
43871
43871
  maximumPropertiesToStringify -= value.length;
43872
- separator = join14;
43872
+ separator = join15;
43873
43873
  }
43874
43874
  if (deterministic) {
43875
43875
  keys = sort(keys, comparator);
@@ -43880,13 +43880,13 @@ ${indentation}`;
43880
43880
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
43881
43881
  if (tmp !== void 0) {
43882
43882
  res += `${separator}${strEscape(key2)}: ${tmp}`;
43883
- separator = join14;
43883
+ separator = join15;
43884
43884
  }
43885
43885
  }
43886
43886
  if (keyLength > maximumBreadth) {
43887
43887
  const removedKeys = keyLength - maximumBreadth;
43888
43888
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
43889
- separator = join14;
43889
+ separator = join15;
43890
43890
  }
43891
43891
  if (separator !== "") {
43892
43892
  res = `
@@ -60175,13 +60175,10 @@ var require_mod2 = __commonJS({
60175
60175
 
60176
60176
  // packages/shared/dist/constants.js
60177
60177
  var APP_NAME = "io";
60178
- var APP_VERSION = "4.0.0";
60178
+ var APP_VERSION = "4.0.5";
60179
60179
  var API_PORT = 7777;
60180
60180
  var API_HOST = "0.0.0.0";
60181
- var DEFAULT_MODEL = "gpt-4.1";
60182
- var FAST_MODEL = "gpt-4.1-mini";
60183
- var STANDARD_MODEL = "claude-sonnet-4.6";
60184
- var PREMIUM_MODEL = "claude-sonnet-4.6";
60181
+ var DEFAULT_MODEL = "gpt-4o";
60185
60182
  var SESSION_RESET_THRESHOLD = 50;
60186
60183
  var SCHEDULER_INTERVAL_MS = 6e4;
60187
60184
  var QA_MAX_REVISIONS = 3;
@@ -61831,6 +61828,27 @@ var MIGRATIONS = [
61831
61828
  "CREATE INDEX IF NOT EXISTS idx_activity_objective_id ON activity(objective_id)",
61832
61829
  "CREATE INDEX IF NOT EXISTS idx_agent_history_agent_id_created_at ON agent_history(agent_id, created_at)"
61833
61830
  ]
61831
+ },
61832
+ {
61833
+ version: 2,
61834
+ name: "add-model-pricing-and-dual-costs",
61835
+ statements: [
61836
+ `CREATE TABLE IF NOT EXISTS model_pricing (
61837
+ id TEXT PRIMARY KEY,
61838
+ display_name TEXT NOT NULL,
61839
+ premium_multiplier REAL,
61840
+ token_input_multiplier REAL,
61841
+ token_output_multiplier REAL,
61842
+ cached_input_multiplier REAL,
61843
+ tier TEXT NOT NULL,
61844
+ available INTEGER NOT NULL DEFAULT 1,
61845
+ updated_at TEXT NOT NULL
61846
+ )`,
61847
+ "CREATE INDEX IF NOT EXISTS idx_model_pricing_tier ON model_pricing(tier)",
61848
+ "CREATE INDEX IF NOT EXISTS idx_model_pricing_available ON model_pricing(available)",
61849
+ "ALTER TABLE token_usage ADD COLUMN premium_request_cost REAL",
61850
+ "ALTER TABLE token_usage ADD COLUMN token_unit_cost REAL"
61851
+ ]
61834
61852
  }
61835
61853
  ];
61836
61854
  var client = null;
@@ -62515,8 +62533,8 @@ async function recordUsage(data, db) {
62515
62533
  createdAt: data.createdAt ?? nowIso()
62516
62534
  };
62517
62535
  await database.execute({
62518
- sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, created_at)
62519
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
62536
+ sql: `INSERT INTO token_usage (id, squad_id, agent_id, model, input_tokens, output_tokens, cost, premium_request_cost, token_unit_cost, created_at)
62537
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
62520
62538
  args: [
62521
62539
  usage.id,
62522
62540
  usage.squadId,
@@ -62525,6 +62543,8 @@ async function recordUsage(data, db) {
62525
62543
  usage.inputTokens,
62526
62544
  usage.outputTokens,
62527
62545
  usage.cost,
62546
+ data.premiumRequestCost ?? null,
62547
+ data.tokenUnitCost ?? null,
62528
62548
  usage.createdAt
62529
62549
  ]
62530
62550
  });
@@ -63017,7 +63037,7 @@ async function createNotificationItemIfMissing(eventBus2, payload, type, title,
63017
63037
  // packages/daemon/src/api/server.ts
63018
63038
  import { existsSync as existsSync2 } from "node:fs";
63019
63039
  import { createServer } from "node:http";
63020
- import { dirname as dirname4, resolve as resolve2 } from "node:path";
63040
+ import { dirname as dirname4, join as join4, resolve as resolve2 } from "node:path";
63021
63041
  import { fileURLToPath } from "node:url";
63022
63042
  var import_express10 = __toESM(require_express2(), 1);
63023
63043
 
@@ -77848,7 +77868,8 @@ var configSchema = external_exports.object({
77848
77868
  telegramUserId: external_exports.string().trim().min(1).nullable().default(null),
77849
77869
  supabaseUrl: external_exports.string().trim().min(1).nullable().default(null),
77850
77870
  supabaseAnonKey: external_exports.string().trim().min(1).nullable().default(null),
77851
- sessionResetThreshold: external_exports.coerce.number().int().positive().default(SESSION_RESET_THRESHOLD)
77871
+ sessionResetThreshold: external_exports.coerce.number().int().positive().default(SESSION_RESET_THRESHOLD),
77872
+ pricingRefreshHours: external_exports.coerce.number().positive().default(24)
77852
77873
  });
77853
77874
  function parseConfigFile() {
77854
77875
  if (!existsSync(CONFIG_PATH)) {
@@ -77901,6 +77922,9 @@ function readEnvOverrides() {
77901
77922
  if (process.env.IO_SESSION_RESET_THRESHOLD !== void 0) {
77902
77923
  overrides.sessionResetThreshold = process.env.IO_SESSION_RESET_THRESHOLD;
77903
77924
  }
77925
+ if (process.env.IO_PRICING_REFRESH_HOURS !== void 0) {
77926
+ overrides.pricingRefreshHours = process.env.IO_PRICING_REFRESH_HOURS;
77927
+ }
77904
77928
  return overrides;
77905
77929
  }
77906
77930
  function loadConfig() {
@@ -77922,7 +77946,8 @@ var APP_SETTING_KEYS = [
77922
77946
  "telegramUserId",
77923
77947
  "supabaseUrl",
77924
77948
  "supabaseAnonKey",
77925
- "sessionResetThreshold"
77949
+ "sessionResetThreshold",
77950
+ "pricingRefreshHours"
77926
77951
  ];
77927
77952
  router5.get("/api/settings", async (_req, res) => {
77928
77953
  try {
@@ -78034,11 +78059,16 @@ router6.get("/api/skills", async (_req, res) => {
78034
78059
  router6.post("/api/skills/install", async (req, res) => {
78035
78060
  try {
78036
78061
  const body = req.body;
78037
- if (!body?.url && !(body?.source && body?.slug)) {
78062
+ if (!body?.url && !(body?.source && (body?.slug || body?.skillId || body?.name))) {
78038
78063
  res.status(400).json({ error: "Provide url or source+slug to install a skill" });
78039
78064
  return;
78040
78065
  }
78041
- const result = await installSkill(body);
78066
+ const normalized = {
78067
+ url: body.url,
78068
+ source: body.source,
78069
+ slug: body.slug || body.skillId || body.name
78070
+ };
78071
+ const result = await installSkill(normalized);
78042
78072
  res.status(result.created ? 201 : 200).json(result.skill);
78043
78073
  } catch (error51) {
78044
78074
  const statusCode = error51 instanceof Error && /Invalid skill|fetch/i.test(error51.message) ? 400 : 500;
@@ -78063,8 +78093,25 @@ router6.delete("/api/skills/:id", async (req, res) => {
78063
78093
  });
78064
78094
  }
78065
78095
  });
78066
- router6.get("/api/skills/discover", async (_req, res) => {
78067
- res.status(200).json([]);
78096
+ router6.get("/api/skills/discover", async (req, res) => {
78097
+ try {
78098
+ const source = typeof req.query.source === "string" ? req.query.source : "";
78099
+ const query = typeof req.query.q === "string" ? req.query.q.trim() : "";
78100
+ if (source === "skillssh") {
78101
+ const skills = await discoverSkillsSh(query);
78102
+ res.status(200).json(skills);
78103
+ } else if (source === "awesome-copilot") {
78104
+ const skills = await discoverAwesomeCopilot(query);
78105
+ res.status(200).json(skills);
78106
+ } else {
78107
+ res.status(400).json({ error: `Unknown source: ${source}` });
78108
+ }
78109
+ } catch (error51) {
78110
+ res.status(500).json({
78111
+ error: "Failed to discover skills",
78112
+ details: error51 instanceof Error ? error51.message : "Unknown error"
78113
+ });
78114
+ }
78068
78115
  });
78069
78116
  async function installSkill(request) {
78070
78117
  await mkdir3(SKILLS_DIR, { recursive: true });
@@ -78076,13 +78123,20 @@ async function installSkill(request) {
78076
78123
  const directory = join2(SKILLS_DIR, directoryName);
78077
78124
  const existing = installedSkills.find((skill2) => skill2.id === id);
78078
78125
  let entryFile = existing?.entryFile ?? null;
78079
- if (request.url) {
78080
- const response = await fetch(request.url);
78126
+ let resolvedUrl = request.url;
78127
+ if (source === "skillssh" && resolvedUrl) {
78128
+ const fetchResult = await fetch(resolvedUrl);
78129
+ if (!fetchResult.ok) {
78130
+ resolvedUrl = await resolveSkillsShUrl(request) ?? resolvedUrl;
78131
+ }
78132
+ }
78133
+ if (resolvedUrl) {
78134
+ const response = await fetch(resolvedUrl);
78081
78135
  if (!response.ok) {
78082
- throw new Error(`Failed to fetch skill from ${request.url}`);
78136
+ throw new Error(`Failed to fetch skill from ${resolvedUrl} (${response.status})`);
78083
78137
  }
78084
78138
  const body = await response.text();
78085
- entryFile = chooseEntryFileName(request.url, response.headers.get("content-type"));
78139
+ entryFile = chooseEntryFileName(resolvedUrl, response.headers.get("content-type"));
78086
78140
  await mkdir3(directory, { recursive: true });
78087
78141
  await writeFile2(join2(directory, entryFile), body, "utf8");
78088
78142
  } else {
@@ -78169,6 +78223,115 @@ function chooseEntryFileName(url2, contentType) {
78169
78223
  function isMissingFileError2(error51) {
78170
78224
  return !!error51 && typeof error51 === "object" && "code" in error51 && error51.code === "ENOENT";
78171
78225
  }
78226
+ async function resolveSkillsShUrl(request) {
78227
+ const slug = request.slug?.trim();
78228
+ if (!slug) return null;
78229
+ try {
78230
+ const searchResponse = await fetch(
78231
+ `https://skills.sh/api/search?q=${encodeURIComponent(slug)}&limit=10`,
78232
+ { signal: AbortSignal.timeout(1e4) }
78233
+ );
78234
+ if (!searchResponse.ok) return null;
78235
+ const data = await searchResponse.json();
78236
+ const match = data.skills?.find(
78237
+ (s) => s.skillId === slug || s.name === slug || s.id.endsWith(`/${slug}`)
78238
+ );
78239
+ if (!match?.source) return null;
78240
+ const headers = {
78241
+ Accept: "application/vnd.github.v3+json",
78242
+ "User-Agent": "io-daemon"
78243
+ };
78244
+ if (process.env.GITHUB_TOKEN) {
78245
+ headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
78246
+ }
78247
+ const treeResponse = await fetch(
78248
+ `https://api.github.com/repos/${match.source}/git/trees/main?recursive=1`,
78249
+ { headers, signal: AbortSignal.timeout(15e3) }
78250
+ );
78251
+ if (!treeResponse.ok) return null;
78252
+ const treeData = await treeResponse.json();
78253
+ const skillFile = treeData.tree?.find(
78254
+ (entry) => entry.type === "blob" && entry.path.endsWith(`/${match.skillId}/SKILL.md`)
78255
+ );
78256
+ if (!skillFile) return null;
78257
+ return `https://raw.githubusercontent.com/${match.source}/main/${skillFile.path}`;
78258
+ } catch {
78259
+ return null;
78260
+ }
78261
+ }
78262
+ async function discoverSkillsSh(query) {
78263
+ if (!query) return [];
78264
+ const endpoint = `https://skills.sh/api/search?q=${encodeURIComponent(query)}&limit=50`;
78265
+ const response = await fetch(endpoint, { signal: AbortSignal.timeout(1e4) });
78266
+ if (!response.ok) {
78267
+ throw new Error(`skills.sh returned ${response.status}`);
78268
+ }
78269
+ const data = await response.json();
78270
+ const installedSkills = await readInstalledSkills();
78271
+ const installedIds = new Set(installedSkills.map((s) => s.id));
78272
+ return (data.skills ?? []).map((entry) => {
78273
+ const skillUrl = entry.source ? `https://raw.githubusercontent.com/${entry.source}/main/skills/${entry.skillId}/SKILL.md` : "";
78274
+ return {
78275
+ name: entry.name || entry.skillId,
78276
+ title: entry.name || entry.skillId,
78277
+ description: `${entry.source ?? ""}`,
78278
+ url: skillUrl,
78279
+ source: "skillssh",
78280
+ installed: installedIds.has(`skillssh:${normalizeSlug(entry.skillId || entry.name)}`),
78281
+ registrySource: entry.source ?? void 0,
78282
+ skillId: entry.skillId ?? void 0,
78283
+ installs: entry.installs ?? 0
78284
+ };
78285
+ });
78286
+ }
78287
+ var awesomeCopilotCache = null;
78288
+ var AWESOME_COPILOT_CACHE_TTL = 60 * 60 * 1e3;
78289
+ async function discoverAwesomeCopilot(query) {
78290
+ const now = Date.now();
78291
+ if (!awesomeCopilotCache || now - awesomeCopilotCache.fetchedAt > AWESOME_COPILOT_CACHE_TTL) {
78292
+ awesomeCopilotCache = { skills: await fetchAwesomeCopilotList(), fetchedAt: now };
78293
+ }
78294
+ const skills = awesomeCopilotCache.skills;
78295
+ if (!query) return skills;
78296
+ const needle = query.toLowerCase();
78297
+ return skills.filter(
78298
+ (s) => s.name.toLowerCase().includes(needle) || s.title.toLowerCase().includes(needle) || s.description.toLowerCase().includes(needle)
78299
+ );
78300
+ }
78301
+ async function fetchAwesomeCopilotList() {
78302
+ const treeUrl = "https://api.github.com/repos/github/awesome-copilot/git/trees/main?recursive=1";
78303
+ const headers = {
78304
+ Accept: "application/vnd.github.v3+json",
78305
+ "User-Agent": "io-daemon"
78306
+ };
78307
+ if (process.env.GITHUB_TOKEN) {
78308
+ headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
78309
+ }
78310
+ const response = await fetch(treeUrl, { headers, signal: AbortSignal.timeout(15e3) });
78311
+ if (!response.ok) {
78312
+ throw new Error(`GitHub API returned ${response.status}`);
78313
+ }
78314
+ const data = await response.json();
78315
+ const installedSkills = await readInstalledSkills();
78316
+ const installedIds = new Set(installedSkills.map((s) => s.id));
78317
+ const skillEntries = (data.tree ?? []).filter(
78318
+ (entry) => entry.type === "blob" && entry.path.startsWith("skills/") && entry.path.endsWith("/SKILL.md")
78319
+ );
78320
+ return skillEntries.map((entry) => {
78321
+ const parts = entry.path.split("/");
78322
+ const skillName = parts[1] ?? "unknown";
78323
+ const slug = normalizeSlug(skillName);
78324
+ const rawUrl = `https://raw.githubusercontent.com/github/awesome-copilot/main/${entry.path}`;
78325
+ return {
78326
+ name: skillName,
78327
+ title: skillName.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
78328
+ description: "From github/awesome-copilot",
78329
+ url: rawUrl,
78330
+ source: "awesome-copilot",
78331
+ installed: installedIds.has(`awesome-copilot:${slug}`)
78332
+ };
78333
+ });
78334
+ }
78172
78335
 
78173
78336
  // packages/daemon/src/api/routes/squads.ts
78174
78337
  var import_express7 = __toESM(require_express2(), 1);
@@ -79015,6 +79178,9 @@ function createApiServer(config2) {
79015
79178
  });
79016
79179
  if (existsSync2(webDirectory)) {
79017
79180
  app.use(import_express10.default.static(webDirectory));
79181
+ app.get("*", (_req, res) => {
79182
+ res.sendFile(join4(webDirectory, "index.html"));
79183
+ });
79018
79184
  }
79019
79185
  app.use((error51, _req, res, next) => {
79020
79186
  if (res.headersSent) {
@@ -79061,8 +79227,8 @@ function createApiServer(config2) {
79061
79227
 
79062
79228
  // packages/daemon/src/data-dir.ts
79063
79229
  import { mkdirSync } from "node:fs";
79064
- import { join as join4 } from "node:path";
79065
- var WIKI_PAGES_DIR = join4(WIKI_DIR, "pages");
79230
+ import { join as join5 } from "node:path";
79231
+ var WIKI_PAGES_DIR = join5(WIKI_DIR, "pages");
79066
79232
  function ensureDataDirectories() {
79067
79233
  for (const directory of [DATA_DIR, WIKI_DIR, WIKI_PAGES_DIR, SKILLS_DIR, LOGS_DIR]) {
79068
79234
  mkdirSync(directory, { recursive: true });
@@ -79070,10 +79236,10 @@ function ensureDataDirectories() {
79070
79236
  }
79071
79237
 
79072
79238
  // packages/daemon/src/logging/logger.ts
79073
- import { join as join5 } from "node:path";
79239
+ import { join as join6 } from "node:path";
79074
79240
  var import_pino = __toESM(require_pino(), 1);
79075
79241
  var import_pino_pretty = __toESM(require_pino_pretty(), 1);
79076
- var LOG_FILE_PATH = join5(LOGS_DIR, "io.log");
79242
+ var LOG_FILE_PATH = join6(LOGS_DIR, "io.log");
79077
79243
  var rootLogger = null;
79078
79244
  function shouldPrettyPrint(logLevel) {
79079
79245
  return process.env.NODE_ENV !== "production" || process.env.LOG_LEVEL === "debug" || logLevel === "debug" || logLevel === "trace";
@@ -79114,6 +79280,448 @@ function createLogger(name) {
79114
79280
  return getRootLogger().child({ subsystem: name });
79115
79281
  }
79116
79282
 
79283
+ // packages/daemon/src/models/catalog.ts
79284
+ import { execFileSync } from "node:child_process";
79285
+ var CATALOG_URL = "https://models.github.ai/catalog/models";
79286
+ function resolveGitHubToken() {
79287
+ const envToken = process.env.GITHUB_TOKEN?.trim();
79288
+ if (envToken && envToken.length > 0) {
79289
+ return envToken;
79290
+ }
79291
+ try {
79292
+ const token = execFileSync("gh", ["auth", "token"], {
79293
+ encoding: "utf8",
79294
+ stdio: ["ignore", "pipe", "pipe"]
79295
+ }).trim();
79296
+ return token.length > 0 ? token : void 0;
79297
+ } catch {
79298
+ return void 0;
79299
+ }
79300
+ }
79301
+ async function fetchModelCatalog() {
79302
+ const token = resolveGitHubToken();
79303
+ if (!token) {
79304
+ throw new Error("No GitHub token available for model catalog fetch");
79305
+ }
79306
+ const response = await fetch(CATALOG_URL, {
79307
+ headers: {
79308
+ Accept: "application/json",
79309
+ Authorization: `Bearer ${token}`,
79310
+ "X-GitHub-Api-Version": "2024-12-01"
79311
+ }
79312
+ });
79313
+ if (!response.ok) {
79314
+ throw new Error(`Model catalog fetch failed: ${response.status} ${response.statusText}`);
79315
+ }
79316
+ const data = await response.json();
79317
+ if (!Array.isArray(data)) {
79318
+ throw new Error("Model catalog response is not an array");
79319
+ }
79320
+ const models = [];
79321
+ for (const entry of data) {
79322
+ const id = entry.id ?? entry.name;
79323
+ const displayName = entry.friendly_name ?? entry.name ?? id;
79324
+ if (id && typeof id === "string") {
79325
+ models.push({ id, displayName: displayName ?? id });
79326
+ }
79327
+ }
79328
+ return models;
79329
+ }
79330
+
79331
+ // packages/daemon/src/models/pricing-scraper.ts
79332
+ var TOKEN_UNIT_COSTS_URL = "https://docs.github.com/en/billing/reference/costs-for-github-models";
79333
+ var PREMIUM_MULTIPLIERS_URL = "https://docs.github.com/en/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans";
79334
+ async function scrapeTokenUnitPricing() {
79335
+ const html = await fetchPage(TOKEN_UNIT_COSTS_URL);
79336
+ return parseTokenUnitTable(html);
79337
+ }
79338
+ async function scrapePremiumRequestPricing() {
79339
+ const html = await fetchPage(PREMIUM_MULTIPLIERS_URL);
79340
+ return parsePremiumMultiplierTable(html);
79341
+ }
79342
+ async function fetchPage(url2) {
79343
+ const response = await fetch(url2, {
79344
+ headers: {
79345
+ Accept: "text/html",
79346
+ "User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
79347
+ },
79348
+ redirect: "follow"
79349
+ });
79350
+ if (!response.ok) {
79351
+ throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
79352
+ }
79353
+ return response.text();
79354
+ }
79355
+ function parseTokenUnitTable(html) {
79356
+ const results = [];
79357
+ const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]*)<\/td>\s*<td[^>]*>([^<]+)<\/td>/gi;
79358
+ for (const match of html.matchAll(tableRowPattern)) {
79359
+ const modelName = cleanCellText(match[1]);
79360
+ const inputMultiplier = Number.parseFloat(match[2]);
79361
+ const cachedInput = match[3].trim().toLowerCase();
79362
+ const outputMultiplier = Number.parseFloat(match[4]);
79363
+ if (modelName && !Number.isNaN(inputMultiplier) && !Number.isNaN(outputMultiplier)) {
79364
+ results.push({
79365
+ modelName,
79366
+ inputMultiplier,
79367
+ cachedInputMultiplier: cachedInput === "n/a" || cachedInput === "" ? null : Number.parseFloat(cachedInput) || null,
79368
+ outputMultiplier
79369
+ });
79370
+ }
79371
+ }
79372
+ return results;
79373
+ }
79374
+ function parsePremiumMultiplierTable(html) {
79375
+ const results = [];
79376
+ const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<\/tr>/gi;
79377
+ for (const match of html.matchAll(tableRowPattern)) {
79378
+ const modelName = cleanCellText(match[1]);
79379
+ const multiplier = Number.parseFloat(match[2]);
79380
+ if (modelName && !Number.isNaN(multiplier) && multiplier > 0) {
79381
+ results.push({ modelName, multiplier });
79382
+ }
79383
+ }
79384
+ return results;
79385
+ }
79386
+ function cleanCellText(text) {
79387
+ return text.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " ").trim();
79388
+ }
79389
+
79390
+ // packages/daemon/src/models/seed.ts
79391
+ var SEED_MODELS = [
79392
+ {
79393
+ id: "gpt-4o-mini",
79394
+ displayName: "OpenAI GPT-4o mini",
79395
+ premiumMultiplier: 0.33,
79396
+ tokenInputMultiplier: 0.015,
79397
+ tokenOutputMultiplier: 0.06,
79398
+ cachedInputMultiplier: 75e-4,
79399
+ tier: "trivial",
79400
+ available: true
79401
+ },
79402
+ {
79403
+ id: "gpt-4o",
79404
+ displayName: "OpenAI GPT-4o",
79405
+ premiumMultiplier: 0.33,
79406
+ tokenInputMultiplier: 0.25,
79407
+ tokenOutputMultiplier: 1,
79408
+ cachedInputMultiplier: 0.125,
79409
+ tier: "trivial",
79410
+ available: true
79411
+ },
79412
+ {
79413
+ id: "gpt-5-mini",
79414
+ displayName: "GPT-5 mini",
79415
+ premiumMultiplier: 0.33,
79416
+ tokenInputMultiplier: null,
79417
+ tokenOutputMultiplier: null,
79418
+ cachedInputMultiplier: null,
79419
+ tier: "trivial",
79420
+ available: true
79421
+ },
79422
+ {
79423
+ id: "claude-sonnet-4",
79424
+ displayName: "Claude Sonnet 4",
79425
+ premiumMultiplier: 6,
79426
+ tokenInputMultiplier: 0.6,
79427
+ tokenOutputMultiplier: 2.4,
79428
+ cachedInputMultiplier: null,
79429
+ tier: "premium",
79430
+ available: true
79431
+ },
79432
+ {
79433
+ id: "claude-haiku-4.5",
79434
+ displayName: "Claude Haiku 4.5",
79435
+ premiumMultiplier: 0.33,
79436
+ tokenInputMultiplier: null,
79437
+ tokenOutputMultiplier: null,
79438
+ cachedInputMultiplier: null,
79439
+ tier: "trivial",
79440
+ available: true
79441
+ },
79442
+ {
79443
+ id: "gemini-2.5-pro",
79444
+ displayName: "Gemini 2.5 Pro",
79445
+ premiumMultiplier: 1,
79446
+ tokenInputMultiplier: null,
79447
+ tokenOutputMultiplier: null,
79448
+ cachedInputMultiplier: null,
79449
+ tier: "fast",
79450
+ available: true
79451
+ },
79452
+ {
79453
+ id: "gpt-5.1",
79454
+ displayName: "GPT-5.1",
79455
+ premiumMultiplier: 3,
79456
+ tokenInputMultiplier: null,
79457
+ tokenOutputMultiplier: null,
79458
+ cachedInputMultiplier: null,
79459
+ tier: "standard",
79460
+ available: true
79461
+ },
79462
+ {
79463
+ id: "gpt-5.2",
79464
+ displayName: "GPT-5.2",
79465
+ premiumMultiplier: 3,
79466
+ tokenInputMultiplier: null,
79467
+ tokenOutputMultiplier: null,
79468
+ cachedInputMultiplier: null,
79469
+ tier: "standard",
79470
+ available: true
79471
+ },
79472
+ {
79473
+ id: "gpt-5.4",
79474
+ displayName: "GPT-5.4",
79475
+ premiumMultiplier: 6,
79476
+ tokenInputMultiplier: null,
79477
+ tokenOutputMultiplier: null,
79478
+ cachedInputMultiplier: null,
79479
+ tier: "premium",
79480
+ available: true
79481
+ },
79482
+ {
79483
+ id: "claude-opus-4.5",
79484
+ displayName: "Claude Opus 4.5",
79485
+ premiumMultiplier: 15,
79486
+ tokenInputMultiplier: null,
79487
+ tokenOutputMultiplier: null,
79488
+ cachedInputMultiplier: null,
79489
+ tier: "premium",
79490
+ available: true
79491
+ },
79492
+ {
79493
+ id: "claude-opus-4.6",
79494
+ displayName: "Claude Opus 4.6",
79495
+ premiumMultiplier: 27,
79496
+ tokenInputMultiplier: null,
79497
+ tokenOutputMultiplier: null,
79498
+ cachedInputMultiplier: null,
79499
+ tier: "ultra",
79500
+ available: true
79501
+ },
79502
+ {
79503
+ id: "claude-opus-4.7",
79504
+ displayName: "Claude Opus 4.7",
79505
+ premiumMultiplier: 27,
79506
+ tokenInputMultiplier: null,
79507
+ tokenOutputMultiplier: null,
79508
+ cachedInputMultiplier: null,
79509
+ tier: "ultra",
79510
+ available: true
79511
+ },
79512
+ {
79513
+ id: "gpt-5.5",
79514
+ displayName: "GPT-5.5",
79515
+ premiumMultiplier: 57,
79516
+ tokenInputMultiplier: null,
79517
+ tokenOutputMultiplier: null,
79518
+ cachedInputMultiplier: null,
79519
+ tier: "ultra",
79520
+ available: true
79521
+ }
79522
+ ];
79523
+
79524
+ // packages/daemon/src/models/types.ts
79525
+ var TIER_RANGES = {
79526
+ trivial: { min: 0, max: 0.33 },
79527
+ fast: { min: 0.34, max: 1 },
79528
+ standard: { min: 1.1, max: 5 },
79529
+ premium: { min: 5.1, max: 15 },
79530
+ ultra: { min: 15.1, max: Number.POSITIVE_INFINITY }
79531
+ };
79532
+ function computeTierFromMultiplier(premiumMultiplier) {
79533
+ if (premiumMultiplier === null) {
79534
+ return "standard";
79535
+ }
79536
+ for (const [tier, range] of Object.entries(TIER_RANGES)) {
79537
+ if (premiumMultiplier >= range.min && premiumMultiplier <= range.max) {
79538
+ return tier;
79539
+ }
79540
+ }
79541
+ return "ultra";
79542
+ }
79543
+ var TOKEN_UNIT_PRICE = 1e-5;
79544
+
79545
+ // packages/daemon/src/models/registry.ts
79546
+ function normalizeModelName(name) {
79547
+ return name.toLowerCase().replace(/^openai\s+/i, "").replace(/\s+/g, "-").replace(/[^a-z0-9.\-]/g, "").trim();
79548
+ }
79549
+ async function fetchCatalogIntoMap(modelMap, result, logger) {
79550
+ try {
79551
+ const catalogModels = await fetchModelCatalog();
79552
+ result.catalogFetched = true;
79553
+ for (const m of catalogModels) {
79554
+ const key = normalizeModelName(m.id);
79555
+ modelMap.set(key, { id: m.id, displayName: m.displayName, available: true });
79556
+ }
79557
+ } catch (error51) {
79558
+ const msg = error51 instanceof Error ? error51.message : String(error51);
79559
+ result.errors.push(`Catalog fetch failed: ${msg}`);
79560
+ logger?.warn(`Model catalog fetch failed: ${msg}`);
79561
+ }
79562
+ }
79563
+ async function scrapeTokenPricingIntoMap(modelMap, result, logger) {
79564
+ try {
79565
+ const tokenPricing = await scrapeTokenUnitPricing();
79566
+ result.tokenPricingScraped = true;
79567
+ for (const tp of tokenPricing) {
79568
+ const key = normalizeModelName(tp.modelName);
79569
+ const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
79570
+ if (existing) {
79571
+ existing.tokenInputMultiplier = tp.inputMultiplier;
79572
+ existing.tokenOutputMultiplier = tp.outputMultiplier;
79573
+ existing.cachedInputMultiplier = tp.cachedInputMultiplier;
79574
+ } else {
79575
+ modelMap.set(key, {
79576
+ id: key,
79577
+ displayName: tp.modelName,
79578
+ tokenInputMultiplier: tp.inputMultiplier,
79579
+ tokenOutputMultiplier: tp.outputMultiplier,
79580
+ cachedInputMultiplier: tp.cachedInputMultiplier,
79581
+ available: true
79582
+ });
79583
+ }
79584
+ }
79585
+ } catch (error51) {
79586
+ const msg = error51 instanceof Error ? error51.message : String(error51);
79587
+ result.errors.push(`Token pricing scrape failed: ${msg}`);
79588
+ logger?.warn(`Token pricing scrape failed: ${msg}`);
79589
+ }
79590
+ }
79591
+ async function scrapePremiumPricingIntoMap(modelMap, result, logger) {
79592
+ try {
79593
+ const premiumPricing = await scrapePremiumRequestPricing();
79594
+ result.premiumPricingScraped = true;
79595
+ for (const pp of premiumPricing) {
79596
+ const key = normalizeModelName(pp.modelName);
79597
+ const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
79598
+ if (existing) {
79599
+ existing.premiumMultiplier = pp.multiplier;
79600
+ } else {
79601
+ modelMap.set(key, {
79602
+ id: key,
79603
+ displayName: pp.modelName,
79604
+ premiumMultiplier: pp.multiplier,
79605
+ available: true
79606
+ });
79607
+ }
79608
+ }
79609
+ } catch (error51) {
79610
+ const msg = error51 instanceof Error ? error51.message : String(error51);
79611
+ result.errors.push(`Premium pricing scrape failed: ${msg}`);
79612
+ logger?.warn(`Premium pricing scrape failed: ${msg}`);
79613
+ }
79614
+ }
79615
+ async function refreshModelPricing(logger) {
79616
+ const result = {
79617
+ modelsUpdated: 0,
79618
+ catalogFetched: false,
79619
+ tokenPricingScraped: false,
79620
+ premiumPricingScraped: false,
79621
+ errors: []
79622
+ };
79623
+ const modelMap = /* @__PURE__ */ new Map();
79624
+ await fetchCatalogIntoMap(modelMap, result, logger);
79625
+ await scrapeTokenPricingIntoMap(modelMap, result, logger);
79626
+ await scrapePremiumPricingIntoMap(modelMap, result, logger);
79627
+ if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped) {
79628
+ logger?.warn("All pricing sources failed, using seed data");
79629
+ await seedFromFallback();
79630
+ result.modelsUpdated = SEED_MODELS.length;
79631
+ return result;
79632
+ }
79633
+ const db = await getDatabase();
79634
+ const now = nowIso();
79635
+ for (const model of modelMap.values()) {
79636
+ const tier = computeTierFromMultiplier(model.premiumMultiplier ?? null);
79637
+ await upsertModel(db, {
79638
+ id: model.id,
79639
+ displayName: model.displayName,
79640
+ premiumMultiplier: model.premiumMultiplier ?? null,
79641
+ tokenInputMultiplier: model.tokenInputMultiplier ?? null,
79642
+ tokenOutputMultiplier: model.tokenOutputMultiplier ?? null,
79643
+ cachedInputMultiplier: model.cachedInputMultiplier ?? null,
79644
+ tier,
79645
+ available: model.available ?? true,
79646
+ updatedAt: now
79647
+ });
79648
+ result.modelsUpdated++;
79649
+ }
79650
+ return result;
79651
+ }
79652
+ async function seedFromFallback() {
79653
+ const db = await getDatabase();
79654
+ const now = nowIso();
79655
+ for (const model of SEED_MODELS) {
79656
+ await upsertModel(db, { ...model, updatedAt: now });
79657
+ }
79658
+ }
79659
+ async function getModelPricing(modelId) {
79660
+ const db = await getDatabase();
79661
+ const result = await db.execute({
79662
+ sql: "SELECT * FROM model_pricing WHERE id = ?",
79663
+ args: [modelId]
79664
+ });
79665
+ if (result.rows.length === 0) {
79666
+ return null;
79667
+ }
79668
+ return rowToModelPricing(result.rows[0]);
79669
+ }
79670
+ function calculateTokenUnitCost(inputTokens, outputTokens, inputMultiplier, outputMultiplier) {
79671
+ if (inputMultiplier === null || outputMultiplier === null) {
79672
+ return 0;
79673
+ }
79674
+ const tokenUnits = inputTokens * inputMultiplier + outputTokens * outputMultiplier;
79675
+ return tokenUnits * TOKEN_UNIT_PRICE;
79676
+ }
79677
+ async function upsertModel(db, model) {
79678
+ await db.execute({
79679
+ sql: `INSERT INTO model_pricing (id, display_name, premium_multiplier, token_input_multiplier, token_output_multiplier, cached_input_multiplier, tier, available, updated_at)
79680
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
79681
+ ON CONFLICT(id) DO UPDATE SET
79682
+ display_name = excluded.display_name,
79683
+ premium_multiplier = COALESCE(excluded.premium_multiplier, model_pricing.premium_multiplier),
79684
+ token_input_multiplier = COALESCE(excluded.token_input_multiplier, model_pricing.token_input_multiplier),
79685
+ token_output_multiplier = COALESCE(excluded.token_output_multiplier, model_pricing.token_output_multiplier),
79686
+ cached_input_multiplier = COALESCE(excluded.cached_input_multiplier, model_pricing.cached_input_multiplier),
79687
+ tier = excluded.tier,
79688
+ available = excluded.available,
79689
+ updated_at = excluded.updated_at`,
79690
+ args: [
79691
+ model.id,
79692
+ model.displayName,
79693
+ model.premiumMultiplier,
79694
+ model.tokenInputMultiplier,
79695
+ model.tokenOutputMultiplier,
79696
+ model.cachedInputMultiplier,
79697
+ model.tier,
79698
+ model.available ? 1 : 0,
79699
+ model.updatedAt
79700
+ ]
79701
+ });
79702
+ }
79703
+ function rowToModelPricing(row) {
79704
+ return {
79705
+ id: asString(row.id),
79706
+ displayName: asString(row.display_name),
79707
+ premiumMultiplier: asNullableNumber(row.premium_multiplier),
79708
+ tokenInputMultiplier: asNullableNumber(row.token_input_multiplier),
79709
+ tokenOutputMultiplier: asNullableNumber(row.token_output_multiplier),
79710
+ cachedInputMultiplier: asNullableNumber(row.cached_input_multiplier),
79711
+ tier: asString(row.tier),
79712
+ available: asNumber(row.available) === 1,
79713
+ updatedAt: asString(row.updated_at)
79714
+ };
79715
+ }
79716
+ function findClosestKey(map2, targetKey) {
79717
+ for (const [key, value] of map2) {
79718
+ if (key.includes(targetKey) || targetKey.includes(key)) {
79719
+ return value;
79720
+ }
79721
+ }
79722
+ return void 0;
79723
+ }
79724
+
79117
79725
  // packages/daemon/src/orchestrator/system-prompt.ts
79118
79726
  function formatSquadRoster(squads) {
79119
79727
  if (squads.length === 0) {
@@ -79206,151 +79814,18 @@ function buildFreshSessionContext(summary, wikiContext) {
79206
79814
  return sections.join("\n\n");
79207
79815
  }
79208
79816
 
79209
- // packages/daemon/src/copilot/router.ts
79210
- var FAST_KEYWORDS = [
79211
- "hi",
79212
- "hello",
79213
- "hey",
79214
- "thanks",
79215
- "thank you",
79216
- "status",
79217
- "list",
79218
- "show",
79219
- "ping",
79220
- "uptime",
79221
- "what time",
79222
- "ok",
79223
- "okay"
79224
- ];
79225
- var PREMIUM_KEYWORDS = [
79226
- "architecture",
79227
- "architect",
79228
- "design",
79229
- "tradeoff",
79230
- "trade-off",
79231
- "plan",
79232
- "planning",
79233
- "strategy",
79234
- "roadmap",
79235
- "migration",
79236
- "refactor",
79237
- "generate",
79238
- "implement",
79239
- "build",
79240
- "codebase",
79241
- "repository",
79242
- "system prompt",
79243
- "multi-step",
79244
- "complex",
79245
- "reason",
79246
- "compare"
79247
- ];
79248
- var SIMPLE_QUESTION_PREFIXES = [
79249
- "is",
79250
- "are",
79251
- "do",
79252
- "does",
79253
- "did",
79254
- "can",
79255
- "could",
79256
- "should",
79257
- "would",
79258
- "will"
79259
- ];
79260
- var MODEL_SWITCH_COOLDOWN_MESSAGES = 3;
79261
- var activeTier = null;
79262
- var messagesSinceLastSwitch = MODEL_SWITCH_COOLDOWN_MESSAGES;
79263
- function normalizeMessage(message2) {
79264
- return message2.trim().toLowerCase();
79265
- }
79266
- function getWordCount(message2) {
79267
- const normalized = message2.trim();
79268
- if (normalized.length === 0) {
79269
- return 0;
79270
- }
79271
- return normalized.split(/\s+/u).length;
79272
- }
79273
- function containsAny(message2, keywords) {
79274
- return keywords.some((keyword) => message2.includes(keyword));
79275
- }
79276
- function startsWithSimpleQuestion(message2) {
79277
- return SIMPLE_QUESTION_PREFIXES.some(
79278
- (prefix) => message2 === prefix || message2.startsWith(`${prefix} `)
79279
- );
79280
- }
79281
- function classifyMessage(message2) {
79282
- const normalized = normalizeMessage(message2);
79283
- const wordCount = getWordCount(normalized);
79284
- if (normalized.length === 0) {
79285
- return "fast";
79286
- }
79287
- if (containsAny(normalized, PREMIUM_KEYWORDS) || normalized.includes("```") || wordCount >= 80) {
79288
- return "premium";
79289
- }
79290
- if (wordCount <= 20 && (containsAny(normalized, FAST_KEYWORDS) || startsWithSimpleQuestion(normalized) || /^(yes|no|sure|okay|ok|thanks)[!.?]*$/u.test(normalized))) {
79291
- return "fast";
79292
- }
79293
- if (wordCount <= 8 && /^(what|when|where|who)\b/u.test(normalized)) {
79294
- return "fast";
79295
- }
79296
- return "standard";
79297
- }
79298
- function getModelForTier(tier) {
79299
- switch (tier) {
79300
- case "fast":
79301
- return FAST_MODEL;
79302
- case "premium":
79303
- return PREMIUM_MODEL;
79304
- default:
79305
- return STANDARD_MODEL;
79306
- }
79307
- }
79308
- function routeMessage(message2) {
79309
- const requestedTier = classifyMessage(message2);
79310
- if (activeTier === null) {
79311
- activeTier = requestedTier;
79312
- messagesSinceLastSwitch = 0;
79313
- return {
79314
- requestedTier,
79315
- effectiveTier: activeTier,
79316
- model: getModelForTier(activeTier),
79317
- switched: true,
79318
- cooldownRemaining: MODEL_SWITCH_COOLDOWN_MESSAGES
79319
- };
79320
- }
79321
- if (requestedTier !== activeTier && messagesSinceLastSwitch >= MODEL_SWITCH_COOLDOWN_MESSAGES) {
79322
- activeTier = requestedTier;
79323
- messagesSinceLastSwitch = 0;
79324
- return {
79325
- requestedTier,
79326
- effectiveTier: activeTier,
79327
- model: getModelForTier(activeTier),
79328
- switched: true,
79329
- cooldownRemaining: MODEL_SWITCH_COOLDOWN_MESSAGES
79330
- };
79331
- }
79332
- messagesSinceLastSwitch += 1;
79333
- return {
79334
- requestedTier,
79335
- effectiveTier: activeTier,
79336
- model: getModelForTier(activeTier),
79337
- switched: false,
79338
- cooldownRemaining: Math.max(MODEL_SWITCH_COOLDOWN_MESSAGES - messagesSinceLastSwitch, 0)
79339
- };
79340
- }
79341
-
79342
79817
  // packages/daemon/src/copilot/session.ts
79343
79818
  import {
79344
79819
  approveAll
79345
79820
  } from "@github/copilot-sdk";
79346
79821
 
79347
79822
  // packages/daemon/src/copilot/client.ts
79348
- import { execFileSync } from "node:child_process";
79823
+ import { execFileSync as execFileSync2 } from "node:child_process";
79349
79824
  import { mkdirSync as mkdirSync2 } from "node:fs";
79350
- import { join as join6 } from "node:path";
79825
+ import { join as join7 } from "node:path";
79351
79826
  import { CopilotClient } from "@github/copilot-sdk";
79352
79827
  var COPILOT_AUTH_ERROR_HINT = "Set GITHUB_TOKEN or authenticate with the GitHub CLI (`gh auth login`) so `gh auth token` returns a valid token.";
79353
- var COPILOT_BASE_DIRECTORY = join6(DATA_DIR, "copilot-sdk");
79828
+ var COPILOT_BASE_DIRECTORY = join7(DATA_DIR, "copilot-sdk");
79354
79829
  var copilotClientSingleton = null;
79355
79830
  var copilotClientInitPromise = null;
79356
79831
  function readTokenFromEnvironment() {
@@ -79359,7 +79834,7 @@ function readTokenFromEnvironment() {
79359
79834
  }
79360
79835
  function readTokenFromGhCli() {
79361
79836
  try {
79362
- const token = execFileSync("gh", ["auth", "token"], {
79837
+ const token = execFileSync2("gh", ["auth", "token"], {
79363
79838
  encoding: "utf8",
79364
79839
  stdio: ["ignore", "pipe", "pipe"]
79365
79840
  }).trim();
@@ -79368,7 +79843,7 @@ function readTokenFromGhCli() {
79368
79843
  return void 0;
79369
79844
  }
79370
79845
  }
79371
- function resolveGitHubToken() {
79846
+ function resolveGitHubToken2() {
79372
79847
  return readTokenFromEnvironment() ?? readTokenFromGhCli();
79373
79848
  }
79374
79849
  function buildClientOptions(token) {
@@ -79404,7 +79879,7 @@ async function initCopilotClient() {
79404
79879
  }
79405
79880
  copilotClientInitPromise = (async () => {
79406
79881
  try {
79407
- const token = resolveGitHubToken();
79882
+ const token = resolveGitHubToken2();
79408
79883
  if (token === void 0) {
79409
79884
  throw new Error(
79410
79885
  `Unable to find a GitHub token for Copilot SDK authentication. ${COPILOT_AUTH_ERROR_HINT}`
@@ -79577,7 +80052,7 @@ async function sendMessage(session, message2, onChunk) {
79577
80052
  // packages/daemon/src/skills/loader.ts
79578
80053
  var import_gray_matter2 = __toESM(require_gray_matter(), 1);
79579
80054
  import { readFile as readFile4, readdir as readdir2 } from "node:fs/promises";
79580
- import { basename as basename3, dirname as dirname5, join as join7 } from "node:path";
80055
+ import { basename as basename3, dirname as dirname5, join as join8 } from "node:path";
79581
80056
  async function scanSkills() {
79582
80057
  const skillFilePaths = await collectSkillFiles(SKILLS_DIR);
79583
80058
  const skills = await Promise.all(skillFilePaths.map((filePath) => readSkillFromFile(filePath)));
@@ -79588,7 +80063,7 @@ async function collectSkillFiles(directory) {
79588
80063
  const entries = await readdir2(directory, { withFileTypes: true });
79589
80064
  const skillFiles = [];
79590
80065
  for (const entry of entries) {
79591
- const entryPath = join7(directory, entry.name);
80066
+ const entryPath = join8(directory, entry.name);
79592
80067
  if (entry.isDirectory()) {
79593
80068
  skillFiles.push(...await collectSkillFiles(entryPath));
79594
80069
  continue;
@@ -79625,7 +80100,7 @@ function isMissingFileError4(error51) {
79625
80100
  // packages/daemon/src/skills/manager.ts
79626
80101
  var import_gray_matter3 = __toESM(require_gray_matter(), 1);
79627
80102
  import { mkdir as mkdir5, readFile as readFile5, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
79628
- import { basename as basename4, dirname as dirname6, join as join8 } from "node:path";
80103
+ import { basename as basename4, dirname as dirname6, join as join9 } from "node:path";
79629
80104
  async function installSkill2(url2) {
79630
80105
  const response = await fetch(url2);
79631
80106
  if (!response.ok) {
@@ -79635,8 +80110,8 @@ async function installSkill2(url2) {
79635
80110
  }
79636
80111
  const skillMarkdown = await response.text();
79637
80112
  const skillId = getSkillIdFromUrl(url2);
79638
- const skillDirectory = join8(SKILLS_DIR, skillId);
79639
- const skillPath = join8(skillDirectory, "SKILL.md");
80113
+ const skillDirectory = join9(SKILLS_DIR, skillId);
80114
+ const skillPath = join9(skillDirectory, "SKILL.md");
79640
80115
  const parsed = (0, import_gray_matter3.default)(skillMarkdown);
79641
80116
  const installedSkill = {
79642
80117
  id: skillId,
@@ -79650,7 +80125,7 @@ async function installSkill2(url2) {
79650
80125
  return installedSkill;
79651
80126
  }
79652
80127
  async function removeSkill2(id) {
79653
- await rm3(join8(SKILLS_DIR, id), { recursive: true, force: true });
80128
+ await rm3(join9(SKILLS_DIR, id), { recursive: true, force: true });
79654
80129
  const lockFile = await readSkillsLock();
79655
80130
  lockFile.skills = lockFile.skills.filter((skill) => skill.id !== id);
79656
80131
  await writeSkillsLock2(lockFile);
@@ -80139,7 +80614,7 @@ var executeSkillsToolCall = async (toolName, rawArgs) => {
80139
80614
  // packages/daemon/src/execution/runner.ts
80140
80615
  import { exec as exec7 } from "node:child_process";
80141
80616
  import { access as access2 } from "node:fs/promises";
80142
- import { basename as basename5, join as join13 } from "node:path";
80617
+ import { basename as basename5, join as join14 } from "node:path";
80143
80618
  import { promisify as promisify7 } from "node:util";
80144
80619
 
80145
80620
  // packages/daemon/src/squad/manager.ts
@@ -80171,7 +80646,7 @@ async function isSquadAvailable(squadId) {
80171
80646
  // packages/daemon/src/execution/agent.ts
80172
80647
  import { exec as exec2 } from "node:child_process";
80173
80648
  import { mkdir as mkdir7, readFile as readFile7, readdir as readdir4, stat as stat3, writeFile as writeFile6 } from "node:fs/promises";
80174
- import { dirname as dirname8, extname as extname3, isAbsolute, join as join9, relative as relative3, resolve as resolve4 } from "node:path";
80649
+ import { dirname as dirname8, extname as extname3, isAbsolute, join as join10, relative as relative3, resolve as resolve4 } from "node:path";
80175
80650
  import { promisify as promisify2 } from "node:util";
80176
80651
  import {
80177
80652
  CopilotClient as CopilotClient2,
@@ -80232,13 +80707,24 @@ function mergeUsage(target, usage) {
80232
80707
  }
80233
80708
  async function persistUsage(member, usageEvents) {
80234
80709
  for (const usage of usageEvents) {
80710
+ const model = usage.model;
80711
+ const pricing = await getModelPricing(model);
80712
+ const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
80713
+ const tokenUnitCost = pricing ? calculateTokenUnitCost(
80714
+ usage.inputTokens ?? 0,
80715
+ usage.outputTokens ?? 0,
80716
+ pricing.tokenInputMultiplier,
80717
+ pricing.tokenOutputMultiplier
80718
+ ) : 0;
80235
80719
  await recordUsage({
80236
80720
  squadId: member.squadId,
80237
80721
  agentId: member.id,
80238
- model: usage.model,
80722
+ model,
80239
80723
  inputTokens: usage.inputTokens ?? 0,
80240
80724
  outputTokens: usage.outputTokens ?? 0,
80241
- cost: usage.cost ?? 0
80725
+ cost: 0,
80726
+ premiumRequestCost,
80727
+ tokenUnitCost
80242
80728
  });
80243
80729
  }
80244
80730
  }
@@ -80256,7 +80742,7 @@ async function collectFiles(directory, recursive, output) {
80256
80742
  if (output.length >= MAX_LIST_RESULTS) {
80257
80743
  return;
80258
80744
  }
80259
- const fullPath = join9(directory, entry.name);
80745
+ const fullPath = join10(directory, entry.name);
80260
80746
  output.push(fullPath);
80261
80747
  if (recursive && entry.isDirectory()) {
80262
80748
  await collectFiles(fullPath, recursive, output);
@@ -80456,7 +80942,7 @@ async function executeAgentTask(member, task, worktreePath, options2) {
80456
80942
  client2 = new CopilotClient2({ workingDirectory: worktreePath });
80457
80943
  await client2.start();
80458
80944
  const session = await client2.createSession({
80459
- model: member.model ?? STANDARD_MODEL,
80945
+ model: member.model ?? DEFAULT_MODEL,
80460
80946
  workingDirectory: worktreePath,
80461
80947
  tools,
80462
80948
  availableTools: ["custom:*"],
@@ -80512,7 +80998,7 @@ Work only inside the current worktree. Summarize the concrete changes you made,
80512
80998
  // packages/daemon/src/execution/planning.ts
80513
80999
  import { exec as exec3 } from "node:child_process";
80514
81000
  import { access, readFile as readFile8 } from "node:fs/promises";
80515
- import { join as join10 } from "node:path";
81001
+ import { join as join11 } from "node:path";
80516
81002
  import { promisify as promisify3 } from "node:util";
80517
81003
  import { CopilotClient as CopilotClient3, approveAll as approveAll3 } from "@github/copilot-sdk";
80518
81004
 
@@ -80611,7 +81097,7 @@ async function fileExists(path) {
80611
81097
  }
80612
81098
  async function buildRepoContext(repoPath) {
80613
81099
  for (const candidate of README_CANDIDATES) {
80614
- const readmePath = join10(repoPath, candidate);
81100
+ const readmePath = join11(repoPath, candidate);
80615
81101
  if (await fileExists(readmePath)) {
80616
81102
  const content = await readFile8(readmePath, "utf8");
80617
81103
  return content.slice(0, MAX_REPO_CONTEXT_LENGTH);
@@ -80697,7 +81183,7 @@ Return strict JSON in this shape:
80697
81183
  client2 = new CopilotClient3({ workingDirectory: repoPath });
80698
81184
  await client2.start();
80699
81185
  const session = await client2.createSession({
80700
- model: PREMIUM_MODEL,
81186
+ model: DEFAULT_MODEL,
80701
81187
  workingDirectory: repoPath,
80702
81188
  onPermissionRequest: approveAll3,
80703
81189
  systemMessage: {
@@ -80734,13 +81220,13 @@ ${repoContext}`
80734
81220
  // packages/daemon/src/execution/pr.ts
80735
81221
  import { exec as exec5 } from "node:child_process";
80736
81222
  import { rm as rm5, writeFile as writeFile7 } from "node:fs/promises";
80737
- import { join as join12 } from "node:path";
81223
+ import { join as join13 } from "node:path";
80738
81224
  import { promisify as promisify5 } from "node:util";
80739
81225
 
80740
81226
  // packages/daemon/src/execution/worktree.ts
80741
81227
  import { exec as exec4 } from "node:child_process";
80742
81228
  import { mkdir as mkdir8, rm as rm4 } from "node:fs/promises";
80743
- import { join as join11 } from "node:path";
81229
+ import { join as join12 } from "node:path";
80744
81230
  import { promisify as promisify4 } from "node:util";
80745
81231
  var execAsync4 = promisify4(exec4);
80746
81232
  var DEFAULT_REMOTE = "origin";
@@ -80760,7 +81246,7 @@ async function branchExists(repoPath, branchName) {
80760
81246
  return output.length > 0;
80761
81247
  }
80762
81248
  function getWorktreePath(repoPath, branchName) {
80763
- return join11(repoPath, ".worktrees", sanitizeBranchName(branchName));
81249
+ return join12(repoPath, ".worktrees", sanitizeBranchName(branchName));
80764
81250
  }
80765
81251
  async function listWorktrees(repoPath) {
80766
81252
  const output = await runGit("git worktree list --porcelain", repoPath);
@@ -80810,7 +81296,7 @@ async function listWorktrees(repoPath) {
80810
81296
  }
80811
81297
  async function createWorktree(repoPath, branchName, baseBranch) {
80812
81298
  const worktreePath = getWorktreePath(repoPath, branchName);
80813
- await mkdir8(join11(repoPath, ".worktrees"), { recursive: true });
81299
+ await mkdir8(join12(repoPath, ".worktrees"), { recursive: true });
80814
81300
  const existing = (await listWorktrees(repoPath)).find(
80815
81301
  (worktree) => worktree.path === worktreePath
80816
81302
  );
@@ -80832,7 +81318,7 @@ async function createWorktree(repoPath, branchName, baseBranch) {
80832
81318
  }
80833
81319
  }
80834
81320
  async function cleanupWorktree(worktreePath) {
80835
- const parentRepoPath = join11(worktreePath, "..", "..");
81321
+ const parentRepoPath = join12(worktreePath, "..", "..");
80836
81322
  try {
80837
81323
  await runGit(`git worktree remove ${JSON.stringify(worktreePath)}`, parentRepoPath);
80838
81324
  } catch (error51) {
@@ -80874,7 +81360,7 @@ function extractUrl(output) {
80874
81360
  }
80875
81361
  function buildBodyFilePath(repoPath, branchName) {
80876
81362
  const safeBranchName = branchName.replace(/[^a-zA-Z0-9._-]+/g, "-");
80877
- return join12(repoPath, `.io-pr-body-${safeBranchName}.md`);
81363
+ return join13(repoPath, `.io-pr-body-${safeBranchName}.md`);
80878
81364
  }
80879
81365
  function buildPrBody(objective, plan, taskSummaries, qaOutcome) {
80880
81366
  const tasks = taskSummaries.map((summary) => `- ${summary}`).join("\n") || "- No task summaries recorded.";
@@ -80968,7 +81454,7 @@ Return strict JSON:
80968
81454
  client2 = new CopilotClient4({ workingDirectory: worktreePath });
80969
81455
  await client2.start();
80970
81456
  const session = await client2.createSession({
80971
- model: qaMember.model ?? STANDARD_MODEL,
81457
+ model: qaMember.model ?? DEFAULT_MODEL,
80972
81458
  workingDirectory: worktreePath,
80973
81459
  onPermissionRequest: approveAll4,
80974
81460
  systemMessage: {
@@ -81088,7 +81574,7 @@ Return strict JSON:
81088
81574
  client2 = new CopilotClient5();
81089
81575
  await client2.start();
81090
81576
  const session = await client2.createSession({
81091
- model: teamLead.model ?? PREMIUM_MODEL,
81577
+ model: teamLead.model ?? DEFAULT_MODEL,
81092
81578
  onPermissionRequest: approveAll5,
81093
81579
  systemMessage: {
81094
81580
  content: `${TEAM_LEAD_PROMPT}
@@ -81192,12 +81678,12 @@ async function runGit2(command, cwd) {
81192
81678
  async function resolveRepoPath(repoUrl, repoName) {
81193
81679
  const candidates = [
81194
81680
  process.cwd(),
81195
- join13(process.cwd(), repoName),
81196
- join13(process.cwd(), "repos", repoName),
81197
- join13(process.cwd(), "..", repoName)
81681
+ join14(process.cwd(), repoName),
81682
+ join14(process.cwd(), "repos", repoName),
81683
+ join14(process.cwd(), "..", repoName)
81198
81684
  ];
81199
81685
  for (const candidate of candidates) {
81200
- if (!await pathExists(join13(candidate, ".git"))) {
81686
+ if (!await pathExists(join14(candidate, ".git"))) {
81201
81687
  continue;
81202
81688
  }
81203
81689
  const remoteUrl = await runGit2("git remote get-url origin", candidate).catch(() => "");
@@ -81543,12 +82029,9 @@ function buildMandatoryRoles() {
81543
82029
  function modelForRole(role) {
81544
82030
  const normalized = slugifyRole(role);
81545
82031
  if (normalized === "team-lead") {
81546
- return PREMIUM_MODEL;
82032
+ return DEFAULT_MODEL;
81547
82033
  }
81548
- if (normalized === "qa") {
81549
- return STANDARD_MODEL;
81550
- }
81551
- return STANDARD_MODEL;
82034
+ return DEFAULT_MODEL;
81552
82035
  }
81553
82036
  function systemPromptForRole(role, repoContext) {
81554
82037
  const normalized = slugifyRole(role);
@@ -81580,7 +82063,7 @@ ${ROLE_GENERATION_PROMPT}`;
81580
82063
  client2 = new CopilotClient6();
81581
82064
  await client2.start();
81582
82065
  const session = await client2.createSession({
81583
- model: PREMIUM_MODEL,
82066
+ model: DEFAULT_MODEL,
81584
82067
  onPermissionRequest: approveAll6,
81585
82068
  systemMessage: {
81586
82069
  content: ROLE_GENERATION_PROMPT
@@ -82054,8 +82537,7 @@ var Orchestrator = class {
82054
82537
  skillsContext,
82055
82538
  conversationSummary: [this.latestResetSummary, conversationSummary].filter(Boolean).join("\n\n")
82056
82539
  });
82057
- const route = routeMessage(normalizedMessage);
82058
- await this.refreshSession(route.model, systemPrompt);
82540
+ await this.refreshSession(this.config.defaultModel, systemPrompt);
82059
82541
  const sendResult = await sendMessage(
82060
82542
  this.requireActiveSession(),
82061
82543
  normalizedMessage,
@@ -82156,12 +82638,25 @@ var Orchestrator = class {
82156
82638
  }
82157
82639
  async refreshSession(model, systemPrompt) {
82158
82640
  await this.disconnectSession();
82159
- this.activeSession = await createSession({
82160
- model,
82161
- systemPrompt,
82162
- tools: createBoundOrchestratorTools(this.config)
82163
- });
82164
- this.activeModel = model;
82641
+ try {
82642
+ this.activeSession = await createSession({
82643
+ model,
82644
+ systemPrompt,
82645
+ tools: createBoundOrchestratorTools(this.config)
82646
+ });
82647
+ this.activeModel = model;
82648
+ } catch (error51) {
82649
+ if (model !== DEFAULT_MODEL) {
82650
+ this.activeSession = await createSession({
82651
+ model: DEFAULT_MODEL,
82652
+ systemPrompt,
82653
+ tools: createBoundOrchestratorTools(this.config)
82654
+ });
82655
+ this.activeModel = DEFAULT_MODEL;
82656
+ } else {
82657
+ throw error51;
82658
+ }
82659
+ }
82165
82660
  }
82166
82661
  async disconnectSession() {
82167
82662
  if (!this.activeSession) {
@@ -82183,11 +82678,22 @@ var Orchestrator = class {
82183
82678
  if (usage.inputTokens === 0 && usage.outputTokens === 0) {
82184
82679
  return;
82185
82680
  }
82681
+ const model = usage.model || this.activeModel || this.config.defaultModel;
82682
+ const pricing = await getModelPricing(model);
82683
+ const premiumRequestCost = pricing?.premiumMultiplier ?? 0;
82684
+ const tokenUnitCost = pricing ? calculateTokenUnitCost(
82685
+ usage.inputTokens,
82686
+ usage.outputTokens,
82687
+ pricing.tokenInputMultiplier,
82688
+ pricing.tokenOutputMultiplier
82689
+ ) : 0;
82186
82690
  await recordUsage({
82187
- model: usage.model || this.activeModel || this.config.defaultModel,
82691
+ model,
82188
82692
  inputTokens: usage.inputTokens,
82189
82693
  outputTokens: usage.outputTokens,
82190
- cost: 0
82694
+ cost: 0,
82695
+ premiumRequestCost,
82696
+ tokenUnitCost
82191
82697
  });
82192
82698
  }
82193
82699
  };
@@ -82484,6 +82990,7 @@ function registerShutdownHandlers(logger, onShutdown) {
82484
82990
  }
82485
82991
  async function main() {
82486
82992
  let logger;
82993
+ let pricingRefreshTimer = null;
82487
82994
  try {
82488
82995
  ensureDataDirectories();
82489
82996
  const config2 = loadConfig();
@@ -82491,6 +82998,12 @@ async function main() {
82491
82998
  logger.info(`IO Daemon v${APP_VERSION} starting...`);
82492
82999
  await initDatabase();
82493
83000
  logger.info("Database initialized");
83001
+ const pricingResult = await refreshModelPricing(logger);
83002
+ if (pricingResult.modelsUpdated === 0) {
83003
+ logger.warn("Model pricing refresh returned 0 models, seeding with fallback");
83004
+ await seedFromFallback();
83005
+ }
83006
+ logger.info({ modelsUpdated: pricingResult.modelsUpdated }, "Model pricing initialized");
82494
83007
  await scanSkills();
82495
83008
  logger.info("Skills scanned");
82496
83009
  const orchestrator2 = createOrchestrator(config2, eventBus);
@@ -82499,12 +83012,21 @@ async function main() {
82499
83012
  const scheduler = createScheduler(orchestrator2, eventBus);
82500
83013
  scheduler.start();
82501
83014
  logger.info("Scheduler started");
83015
+ const refreshIntervalMs = config2.pricingRefreshHours * 60 * 60 * 1e3;
83016
+ pricingRefreshTimer = setInterval(() => {
83017
+ void refreshModelPricing(logger).catch((err) => {
83018
+ logger?.warn({ err }, "Periodic model pricing refresh failed");
83019
+ });
83020
+ }, refreshIntervalMs);
82502
83021
  setChatOrchestrator(orchestrator2);
82503
83022
  const apiServer = createApiServer(config2);
82504
83023
  const telegramBot = createTelegramBot(config2, orchestrator2);
82505
83024
  telegramBot?.start();
82506
83025
  createTelegramNotifier(telegramBot, config2, eventBus);
82507
83026
  registerShutdownHandlers(logger, async () => {
83027
+ if (pricingRefreshTimer) {
83028
+ clearInterval(pricingRefreshTimer);
83029
+ }
82508
83030
  scheduler.stop();
82509
83031
  telegramBot?.stop();
82510
83032
  apiServer.server.close();