heyio 4.2.4 → 4.2.7

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.
@@ -79,7 +79,7 @@ var init_constants = __esm({
79
79
  "packages/shared/dist/constants.js"() {
80
80
  "use strict";
81
81
  APP_NAME = "io";
82
- APP_VERSION = "4.2.4";
82
+ APP_VERSION = "4.2.7";
83
83
  API_PORT = 7777;
84
84
  API_HOST = "0.0.0.0";
85
85
  DEFAULT_MODEL = "gpt-4o";
@@ -77130,6 +77130,11 @@ var MIGRATIONS = [
77130
77130
  agent_name = (SELECT sm.name FROM squad_members sm WHERE sm.id = token_usage.agent_id)
77131
77131
  WHERE squad_id IS NOT NULL OR agent_id IS NOT NULL`
77132
77132
  ]
77133
+ },
77134
+ {
77135
+ version: 5,
77136
+ name: "add-squad-soft-delete",
77137
+ statements: ["ALTER TABLE squads ADD COLUMN deleted_at TEXT"]
77133
77138
  }
77134
77139
  ];
77135
77140
  var client = null;
@@ -77277,7 +77282,8 @@ async function createSquad(data, db) {
77277
77282
  status: data.status ?? "active",
77278
77283
  config: data.config,
77279
77284
  createdAt: timestamp,
77280
- updatedAt: timestamp
77285
+ updatedAt: timestamp,
77286
+ deletedAt: null
77281
77287
  };
77282
77288
  await database.execute({
77283
77289
  sql: `INSERT INTO squads (id, name, repo_url, repo_owner, repo_name, status, config, created_at, updated_at)
@@ -77299,7 +77305,7 @@ async function createSquad(data, db) {
77299
77305
  async function getSquad(id, db) {
77300
77306
  const database = db ?? await getDatabase();
77301
77307
  const squadResult = await database.execute({
77302
- sql: "SELECT * FROM squads WHERE id = ? LIMIT 1",
77308
+ sql: "SELECT * FROM squads WHERE id = ? AND deleted_at IS NULL LIMIT 1",
77303
77309
  args: [id]
77304
77310
  });
77305
77311
  const row = squadResult.rows[0];
@@ -77315,7 +77321,7 @@ async function getSquad(id, db) {
77315
77321
  async function getSquadByName(name, db) {
77316
77322
  const database = db ?? await getDatabase();
77317
77323
  const result = await database.execute({
77318
- sql: "SELECT * FROM squads WHERE name = ? LIMIT 1",
77324
+ sql: "SELECT * FROM squads WHERE name = ? AND deleted_at IS NULL LIMIT 1",
77319
77325
  args: [name]
77320
77326
  });
77321
77327
  const row = result.rows[0];
@@ -77328,7 +77334,9 @@ async function getSquadByName(name, db) {
77328
77334
  }
77329
77335
  async function listSquads(db) {
77330
77336
  const database = db ?? await getDatabase();
77331
- const result = await database.execute("SELECT * FROM squads ORDER BY created_at DESC, id DESC");
77337
+ const result = await database.execute(
77338
+ "SELECT * FROM squads WHERE deleted_at IS NULL ORDER BY created_at DESC, id DESC"
77339
+ );
77332
77340
  return result.rows.map((row) => mapSquad(row));
77333
77341
  }
77334
77342
  async function updateSquad(id, data, db) {
@@ -77347,7 +77355,8 @@ async function updateSquad(id, data, db) {
77347
77355
  status: data.status ?? existing.status,
77348
77356
  config: data.config ?? existing.config,
77349
77357
  createdAt: existing.createdAt,
77350
- updatedAt
77358
+ updatedAt,
77359
+ deletedAt: existing.deletedAt
77351
77360
  };
77352
77361
  await database.execute({
77353
77362
  sql: `UPDATE squads
@@ -77368,16 +77377,34 @@ async function updateSquad(id, data, db) {
77368
77377
  }
77369
77378
  async function deleteSquad(id, db) {
77370
77379
  const database = db ?? await getDatabase();
77380
+ const now = nowIso();
77371
77381
  const result = await database.execute({
77372
- sql: "DELETE FROM squads WHERE id = ?",
77373
- args: [id]
77382
+ sql: "UPDATE squads SET status = 'deleted', deleted_at = ?, updated_at = ? WHERE id = ? AND deleted_at IS NULL",
77383
+ args: [now, now, id]
77374
77384
  });
77375
77385
  return result.rowsAffected > 0;
77376
77386
  }
77387
+ async function restoreSquad(id, db) {
77388
+ const database = db ?? await getDatabase();
77389
+ const now = nowIso();
77390
+ const result = await database.execute({
77391
+ sql: "UPDATE squads SET status = 'active', deleted_at = NULL, updated_at = ? WHERE id = ? AND deleted_at IS NOT NULL",
77392
+ args: [now, id]
77393
+ });
77394
+ if (result.rowsAffected === 0) return null;
77395
+ return getSquadRow(id, database);
77396
+ }
77397
+ async function listDeletedSquads(db) {
77398
+ const database = db ?? await getDatabase();
77399
+ const result = await database.execute(
77400
+ "SELECT * FROM squads WHERE deleted_at IS NOT NULL ORDER BY deleted_at DESC, id DESC"
77401
+ );
77402
+ return result.rows.map((row) => mapSquad(row));
77403
+ }
77377
77404
  async function getSquadByRepo(repoOwner, repoName, db) {
77378
77405
  const database = db ?? await getDatabase();
77379
77406
  const result = await database.execute({
77380
- sql: "SELECT * FROM squads WHERE repo_owner = ? AND repo_name = ? LIMIT 1",
77407
+ sql: "SELECT * FROM squads WHERE repo_owner = ? AND repo_name = ? AND deleted_at IS NULL LIMIT 1",
77381
77408
  args: [repoOwner, repoName]
77382
77409
  });
77383
77410
  const row = result.rows[0];
@@ -77481,7 +77508,8 @@ function mapSquad(row) {
77481
77508
  status: asString(row.status),
77482
77509
  config: parseJson(asString(row.config)),
77483
77510
  createdAt: asString(row.created_at),
77484
- updatedAt: asString(row.updated_at)
77511
+ updatedAt: asString(row.updated_at),
77512
+ deletedAt: asNullableString(row.deleted_at)
77485
77513
  };
77486
77514
  }
77487
77515
  function mapMember(row) {
@@ -79724,6 +79752,39 @@ router7.delete("/api/squads/:id", async (req, res) => {
79724
79752
  });
79725
79753
  }
79726
79754
  });
79755
+ router7.get("/api/squads/deleted", async (_req, res) => {
79756
+ try {
79757
+ const squads = await listDeletedSquads();
79758
+ res.status(200).json({ squads });
79759
+ } catch (error51) {
79760
+ res.status(500).json({
79761
+ error: "Failed to list deleted squads",
79762
+ details: error51 instanceof Error ? error51.message : "Unknown error"
79763
+ });
79764
+ }
79765
+ });
79766
+ router7.post("/api/squads/:id/restore", async (req, res) => {
79767
+ try {
79768
+ const restored = await restoreSquad(req.params.id);
79769
+ if (!restored) {
79770
+ res.status(404).json({ error: "Squad not found or not deleted" });
79771
+ return;
79772
+ }
79773
+ await logActivity({
79774
+ squadId: restored.id,
79775
+ event: EVENT_NAMES.SQUAD_UPDATED,
79776
+ description: `Restored squad ${restored.name}`,
79777
+ metadata: { repoUrl: restored.repoUrl }
79778
+ });
79779
+ eventBus.emit(EVENT_NAMES.SQUAD_UPDATED, { squad: restored });
79780
+ res.status(200).json({ squad: restored });
79781
+ } catch (error51) {
79782
+ res.status(500).json({
79783
+ error: "Failed to restore squad",
79784
+ details: error51 instanceof Error ? error51.message : "Unknown error"
79785
+ });
79786
+ }
79787
+ });
79727
79788
  router7.get("/api/squads/:id/members", async (req, res) => {
79728
79789
  try {
79729
79790
  const squad = await resolveSquad(req.params.id);
@@ -80828,10 +80889,16 @@ async function fetchModelCatalog() {
80828
80889
  // packages/daemon/src/models/pricing-scraper.ts
80829
80890
  var TOKEN_UNIT_COSTS_URL = "https://docs.github.com/en/billing/reference/costs-for-github-models";
80830
80891
  var PREMIUM_MULTIPLIERS_URL = "https://docs.github.com/en/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans";
80892
+ var COPILOT_PRICING_URL = "https://docs.github.com/api/article/body?pathname=/en/copilot/reference/copilot-billing/models-and-pricing";
80893
+ var PRICE_TO_MULTIPLIER = 0.1;
80831
80894
  async function scrapeTokenUnitPricing() {
80832
80895
  const html = await fetchPage(TOKEN_UNIT_COSTS_URL);
80833
80896
  return parseTokenUnitTable(html);
80834
80897
  }
80898
+ async function scrapeCopilotPricing() {
80899
+ const markdown = await fetchMarkdown(COPILOT_PRICING_URL);
80900
+ return parseCopilotPricingMarkdown(markdown);
80901
+ }
80835
80902
  async function scrapePremiumRequestPricing() {
80836
80903
  const html = await fetchPage(PREMIUM_MULTIPLIERS_URL);
80837
80904
  return parsePremiumMultiplierTable(html);
@@ -80849,6 +80916,19 @@ async function fetchPage(url2) {
80849
80916
  }
80850
80917
  return response.text();
80851
80918
  }
80919
+ async function fetchMarkdown(url2) {
80920
+ const response = await fetch(url2, {
80921
+ headers: {
80922
+ Accept: "text/markdown, text/plain, */*",
80923
+ "User-Agent": "IO-Daemon/4.0 (pricing-refresh)"
80924
+ },
80925
+ redirect: "follow"
80926
+ });
80927
+ if (!response.ok) {
80928
+ throw new Error(`Failed to fetch ${url2}: ${response.status} ${response.statusText}`);
80929
+ }
80930
+ return response.text();
80931
+ }
80852
80932
  function parseTokenUnitTable(html) {
80853
80933
  const results = [];
80854
80934
  const tableRowPattern = /<tr[^>]*>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]+)<\/td>\s*<td[^>]*>([^<]*)<\/td>\s*<td[^>]*>([^<]+)<\/td>/gi;
@@ -80883,140 +80963,74 @@ function parsePremiumMultiplierTable(html) {
80883
80963
  function cleanCellText(text) {
80884
80964
  return text.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " ").trim();
80885
80965
  }
80886
-
80887
- // packages/daemon/src/models/seed.ts
80888
- var SEED_MODELS = [
80889
- {
80890
- id: "gpt-4o-mini",
80891
- displayName: "OpenAI GPT-4o mini",
80892
- premiumMultiplier: 0.33,
80893
- tokenInputMultiplier: 0.015,
80894
- tokenOutputMultiplier: 0.06,
80895
- cachedInputMultiplier: 75e-4,
80896
- tier: "trivial",
80897
- available: true
80898
- },
80899
- {
80900
- id: "gpt-4o",
80901
- displayName: "OpenAI GPT-4o",
80902
- premiumMultiplier: 0.33,
80903
- tokenInputMultiplier: 0.25,
80904
- tokenOutputMultiplier: 1,
80905
- cachedInputMultiplier: 0.125,
80906
- tier: "trivial",
80907
- available: true
80908
- },
80909
- {
80910
- id: "gpt-5-mini",
80911
- displayName: "GPT-5 mini",
80912
- premiumMultiplier: 0.33,
80913
- tokenInputMultiplier: null,
80914
- tokenOutputMultiplier: null,
80915
- cachedInputMultiplier: null,
80916
- tier: "trivial",
80917
- available: true
80918
- },
80919
- {
80920
- id: "claude-sonnet-4",
80921
- displayName: "Claude Sonnet 4",
80922
- premiumMultiplier: 6,
80923
- tokenInputMultiplier: 0.6,
80924
- tokenOutputMultiplier: 2.4,
80925
- cachedInputMultiplier: null,
80926
- tier: "premium",
80927
- available: true
80928
- },
80929
- {
80930
- id: "claude-haiku-4.5",
80931
- displayName: "Claude Haiku 4.5",
80932
- premiumMultiplier: 0.33,
80933
- tokenInputMultiplier: null,
80934
- tokenOutputMultiplier: null,
80935
- cachedInputMultiplier: null,
80936
- tier: "trivial",
80937
- available: true
80938
- },
80939
- {
80940
- id: "gemini-2.5-pro",
80941
- displayName: "Gemini 2.5 Pro",
80942
- premiumMultiplier: 1,
80943
- tokenInputMultiplier: null,
80944
- tokenOutputMultiplier: null,
80945
- cachedInputMultiplier: null,
80946
- tier: "fast",
80947
- available: true
80948
- },
80949
- {
80950
- id: "gpt-5.1",
80951
- displayName: "GPT-5.1",
80952
- premiumMultiplier: 3,
80953
- tokenInputMultiplier: null,
80954
- tokenOutputMultiplier: null,
80955
- cachedInputMultiplier: null,
80956
- tier: "standard",
80957
- available: true
80958
- },
80959
- {
80960
- id: "gpt-5.2",
80961
- displayName: "GPT-5.2",
80962
- premiumMultiplier: 3,
80963
- tokenInputMultiplier: null,
80964
- tokenOutputMultiplier: null,
80965
- cachedInputMultiplier: null,
80966
- tier: "standard",
80967
- available: true
80968
- },
80969
- {
80970
- id: "gpt-5.4",
80971
- displayName: "GPT-5.4",
80972
- premiumMultiplier: 6,
80973
- tokenInputMultiplier: null,
80974
- tokenOutputMultiplier: null,
80975
- cachedInputMultiplier: null,
80976
- tier: "premium",
80977
- available: true
80978
- },
80979
- {
80980
- id: "claude-opus-4.5",
80981
- displayName: "Claude Opus 4.5",
80982
- premiumMultiplier: 15,
80983
- tokenInputMultiplier: null,
80984
- tokenOutputMultiplier: null,
80985
- cachedInputMultiplier: null,
80986
- tier: "premium",
80987
- available: true
80988
- },
80989
- {
80990
- id: "claude-opus-4.6",
80991
- displayName: "Claude Opus 4.6",
80992
- premiumMultiplier: 27,
80993
- tokenInputMultiplier: null,
80994
- tokenOutputMultiplier: null,
80995
- cachedInputMultiplier: null,
80996
- tier: "ultra",
80997
- available: true
80998
- },
80999
- {
81000
- id: "claude-opus-4.7",
81001
- displayName: "Claude Opus 4.7",
81002
- premiumMultiplier: 27,
81003
- tokenInputMultiplier: null,
81004
- tokenOutputMultiplier: null,
81005
- cachedInputMultiplier: null,
81006
- tier: "ultra",
81007
- available: true
81008
- },
81009
- {
81010
- id: "gpt-5.5",
81011
- displayName: "GPT-5.5",
81012
- premiumMultiplier: 57,
81013
- tokenInputMultiplier: null,
81014
- tokenOutputMultiplier: null,
81015
- cachedInputMultiplier: null,
81016
- tier: "ultra",
81017
- available: true
80966
+ function parseCopilotPricingMarkdown(markdown) {
80967
+ const results = [];
80968
+ const seenModels = /* @__PURE__ */ new Set();
80969
+ const lines = markdown.split("\n");
80970
+ let columnIndices = null;
80971
+ for (const line of lines) {
80972
+ const trimmed = line.trim();
80973
+ if (!trimmed.startsWith("|")) continue;
80974
+ if (/\|\s*Model\s/i.test(trimmed)) {
80975
+ columnIndices = parseHeaderColumns(trimmed);
80976
+ continue;
80977
+ }
80978
+ if (/^[\s|:-]+$/.test(trimmed) || !columnIndices) continue;
80979
+ const entry = parseDataRow(trimmed, columnIndices);
80980
+ if (entry && !seenModels.has(entry.modelName.toLowerCase())) {
80981
+ seenModels.add(entry.modelName.toLowerCase());
80982
+ results.push(entry);
80983
+ }
81018
80984
  }
81019
- ];
80985
+ return results;
80986
+ }
80987
+ function parseDataRow(line, columnIndices) {
80988
+ const cells = splitMarkdownRow(line);
80989
+ if (cells.length <= columnIndices.output) return null;
80990
+ const modelName = cells[columnIndices.model].trim();
80991
+ const inputPrice = parseDollarValue(cells[columnIndices.input]);
80992
+ const cachedPrice = columnIndices.cached >= 0 ? parseDollarValue(cells[columnIndices.cached]) : null;
80993
+ const outputPrice = parseDollarValue(cells[columnIndices.output]);
80994
+ if (!modelName || inputPrice === null || outputPrice === null) return null;
80995
+ return {
80996
+ modelName,
80997
+ inputMultiplier: inputPrice * PRICE_TO_MULTIPLIER,
80998
+ cachedInputMultiplier: cachedPrice !== null ? cachedPrice * PRICE_TO_MULTIPLIER : null,
80999
+ outputMultiplier: outputPrice * PRICE_TO_MULTIPLIER
81000
+ };
81001
+ }
81002
+ function parseHeaderColumns(headerLine) {
81003
+ const cells = splitMarkdownRow(headerLine).map((c) => c.trim().toLowerCase());
81004
+ const model = cells.findIndex((c) => c === "model" || c === "model name");
81005
+ const input = cells.findIndex(
81006
+ (c) => (c === "input" || c === "input multiplier") && !c.includes("cached")
81007
+ );
81008
+ const cached2 = cells.findIndex(
81009
+ (c) => c.includes("cached input") || c === "cached input multiplier"
81010
+ );
81011
+ const output = cells.findIndex(
81012
+ (c) => (c === "output" || c === "output multiplier") && !c.includes("cached")
81013
+ );
81014
+ if (model < 0 || input < 0 || output < 0) {
81015
+ return null;
81016
+ }
81017
+ return { model, input, cached: cached2 >= 0 ? cached2 : -1, output };
81018
+ }
81019
+ function splitMarkdownRow(line) {
81020
+ const parts = line.split("|");
81021
+ if (parts[0].trim() === "") parts.shift();
81022
+ if (parts[parts.length - 1]?.trim() === "") parts.pop();
81023
+ return parts;
81024
+ }
81025
+ function parseDollarValue(cell) {
81026
+ if (!cell) return null;
81027
+ const cleaned = cell.trim().replace(/[$,]/g, "");
81028
+ if (cleaned.toLowerCase() === "n/a" || cleaned === "" || cleaned === "-") {
81029
+ return null;
81030
+ }
81031
+ const value = Number.parseFloat(cleaned);
81032
+ return Number.isNaN(value) ? null : value;
81033
+ }
81020
81034
 
81021
81035
  // packages/daemon/src/models/types.ts
81022
81036
  var TIER_RANGES = {
@@ -81109,22 +81123,52 @@ async function scrapePremiumPricingIntoMap(modelMap, result, logger2) {
81109
81123
  logger2?.warn(`Premium pricing scrape failed: ${msg}`);
81110
81124
  }
81111
81125
  }
81126
+ async function scrapeCopilotPricingIntoMap(modelMap, result, logger2) {
81127
+ try {
81128
+ const copilotPricing = await scrapeCopilotPricing();
81129
+ result.copilotPricingScraped = true;
81130
+ for (const cp of copilotPricing) {
81131
+ const key = normalizeModelName(cp.modelName);
81132
+ const existing = modelMap.get(key) ?? findClosestKey(modelMap, key);
81133
+ if (existing) {
81134
+ existing.tokenInputMultiplier = cp.inputMultiplier;
81135
+ existing.tokenOutputMultiplier = cp.outputMultiplier;
81136
+ if (cp.cachedInputMultiplier !== null) {
81137
+ existing.cachedInputMultiplier = cp.cachedInputMultiplier;
81138
+ }
81139
+ } else {
81140
+ modelMap.set(key, {
81141
+ id: key,
81142
+ displayName: cp.modelName,
81143
+ tokenInputMultiplier: cp.inputMultiplier,
81144
+ tokenOutputMultiplier: cp.outputMultiplier,
81145
+ cachedInputMultiplier: cp.cachedInputMultiplier,
81146
+ available: true
81147
+ });
81148
+ }
81149
+ }
81150
+ } catch (error51) {
81151
+ const msg = error51 instanceof Error ? error51.message : String(error51);
81152
+ result.errors.push(`Copilot pricing scrape failed: ${msg}`);
81153
+ logger2?.warn(`Copilot pricing scrape failed: ${msg}`);
81154
+ }
81155
+ }
81112
81156
  async function refreshModelPricing(logger2) {
81113
81157
  const result = {
81114
81158
  modelsUpdated: 0,
81115
81159
  catalogFetched: false,
81116
81160
  tokenPricingScraped: false,
81117
81161
  premiumPricingScraped: false,
81162
+ copilotPricingScraped: false,
81118
81163
  errors: []
81119
81164
  };
81120
81165
  const modelMap = /* @__PURE__ */ new Map();
81121
81166
  await fetchCatalogIntoMap(modelMap, result, logger2);
81122
81167
  await scrapeTokenPricingIntoMap(modelMap, result, logger2);
81168
+ await scrapeCopilotPricingIntoMap(modelMap, result, logger2);
81123
81169
  await scrapePremiumPricingIntoMap(modelMap, result, logger2);
81124
- if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped) {
81125
- logger2?.warn("All pricing sources failed, using seed data");
81126
- await seedFromFallback();
81127
- result.modelsUpdated = SEED_MODELS.length;
81170
+ if (!result.catalogFetched && !result.tokenPricingScraped && !result.premiumPricingScraped && !result.copilotPricingScraped) {
81171
+ logger2?.warn("All pricing sources failed, no models available");
81128
81172
  return result;
81129
81173
  }
81130
81174
  const db = await getDatabase();
@@ -81146,13 +81190,6 @@ async function refreshModelPricing(logger2) {
81146
81190
  }
81147
81191
  return result;
81148
81192
  }
81149
- async function seedFromFallback() {
81150
- const db = await getDatabase();
81151
- const now = nowIso();
81152
- for (const model of SEED_MODELS) {
81153
- await upsertModel(db, { ...model, updatedAt: now });
81154
- }
81155
- }
81156
81193
  async function getModelPricing(modelId) {
81157
81194
  const db = await getDatabase();
81158
81195
  const result = await db.execute({
@@ -83374,7 +83411,9 @@ var addSquadMemberSchema = external_exports.object({
83374
83411
  squadId: external_exports.string().trim().min(1),
83375
83412
  role: external_exports.string().trim().min(1).describe("Slug-style role identifier (e.g. 'senior-frontend-engineer', 'team-lead')"),
83376
83413
  name: external_exports.string().trim().min(1).describe("Display name for the member (e.g. 'Senior Frontend Engineer')"),
83377
- systemPrompt: external_exports.string().min(1).describe("The system prompt defining this member's expertise, responsibilities, and behavior."),
83414
+ systemPrompt: external_exports.string().min(1).describe(
83415
+ "The system prompt defining this member's expertise, responsibilities, and behavior."
83416
+ ),
83378
83417
  model: external_exports.string().optional().describe("Model override for this member. Uses squad default if not provided.")
83379
83418
  });
83380
83419
  var removeSquadMemberSchema = external_exports.object({
@@ -83446,10 +83485,22 @@ var squadToolDefinitions = [
83446
83485
  },
83447
83486
  {
83448
83487
  name: "fire_squad",
83449
- description: "Deactivate or delete a squad.",
83488
+ description: "Soft-delete a squad. The squad can be restored later with restore_squad.",
83450
83489
  parameters: squadIdSchema,
83451
83490
  skipPermission: true
83452
83491
  },
83492
+ {
83493
+ name: "restore_squad",
83494
+ description: "Restore a previously deleted squad and all its members.",
83495
+ parameters: squadIdSchema,
83496
+ skipPermission: true
83497
+ },
83498
+ {
83499
+ name: "list_deleted_squads",
83500
+ description: "List all soft-deleted squads that can be restored.",
83501
+ parameters: external_exports.object({}),
83502
+ skipPermission: true
83503
+ },
83453
83504
  {
83454
83505
  name: "list_squads",
83455
83506
  description: "List all squads and their status.",
@@ -83649,7 +83700,7 @@ async function processQueue2(squadId) {
83649
83700
  const next = await getNextQueued(squadId);
83650
83701
  if (next) {
83651
83702
  const freshSquad = await getSquad(squadId);
83652
- if (freshSquad) {
83703
+ if (freshSquad && next.objectiveId) {
83653
83704
  void startAndExecuteInstance(next.id, freshSquad, next.objectiveId);
83654
83705
  }
83655
83706
  }
@@ -83784,13 +83835,28 @@ async function handleFireSquad(rawArgs) {
83784
83835
  if (activeObjectives.length > 0) {
83785
83836
  const updated = await updateSquad(squadId, { status: "inactive" });
83786
83837
  return {
83787
- message: `Squad ${squadId} was deactivated because it still has active objectives.`,
83838
+ message: `Squad ${squadId} was deactivated because it still has active objectives. Use fire_squad again after objectives complete to delete it.`,
83788
83839
  squad: updated,
83789
83840
  activeObjectives
83790
83841
  };
83791
83842
  }
83792
83843
  await deleteSquad(squadId);
83793
- return { message: `Squad ${squadId} was deleted.`, squadId };
83844
+ return {
83845
+ message: `Squad "${squad.name}" has been deleted. It can be restored with restore_squad if needed.`,
83846
+ squadId
83847
+ };
83848
+ }
83849
+ async function handleRestoreSquad(rawArgs) {
83850
+ const { squadId } = squadIdSchema.parse(rawArgs);
83851
+ const restored = await restoreSquad(squadId);
83852
+ if (!restored) {
83853
+ throw new Error(`Squad ${squadId} was not found or is not deleted.`);
83854
+ }
83855
+ eventBus.emit(EVENT_NAMES.SQUAD_UPDATED, { squad: restored });
83856
+ return {
83857
+ message: `Squad "${restored.name}" has been restored.`,
83858
+ squad: restored
83859
+ };
83794
83860
  }
83795
83861
  async function handleDelegateToSquad(rawArgs) {
83796
83862
  const { squadId, objective } = delegateToSquadSchema.parse(rawArgs);
@@ -83840,7 +83906,7 @@ async function handleUpdateSquadMember(rawArgs) {
83840
83906
  member: updated
83841
83907
  };
83842
83908
  }
83843
- function createSquadToolExecutor(config2) {
83909
+ function createSquadToolExecutor(_config) {
83844
83910
  return async (toolName, rawArgs) => {
83845
83911
  switch (toolName) {
83846
83912
  case "create_squad":
@@ -83855,11 +83921,26 @@ function createSquadToolExecutor(config2) {
83855
83921
  return handleAnalyzeRepo(rawArgs);
83856
83922
  case "fire_squad":
83857
83923
  return handleFireSquad(rawArgs);
83924
+ case "restore_squad":
83925
+ return handleRestoreSquad(rawArgs);
83926
+ case "list_deleted_squads": {
83927
+ const deletedSquads = await listDeletedSquads();
83928
+ if (deletedSquads.length === 0) {
83929
+ return { message: "No deleted squads.", squads: [] };
83930
+ }
83931
+ const list = deletedSquads.map((s) => `${s.id}: ${s.name} (${s.repoOwner}/${s.repoName}) [deleted ${s.deletedAt}]`).join("\n");
83932
+ return { message: list, squads: deletedSquads };
83933
+ }
83858
83934
  case "list_squads": {
83859
83935
  const squads = await listSquads();
83936
+ const deletedCount = (await listDeletedSquads()).length;
83937
+ const suffix = deletedCount > 0 ? `
83938
+
83939
+ ${deletedCount} deleted squad(s) available for restore (use list_deleted_squads to view).` : "";
83860
83940
  return {
83861
- message: formatSquadList(squads),
83862
- squads
83941
+ message: formatSquadList(squads) + suffix,
83942
+ squads,
83943
+ deletedCount
83863
83944
  };
83864
83945
  }
83865
83946
  case "get_squad_status": {
@@ -84606,8 +84687,7 @@ async function main() {
84606
84687
  logger2.info("Database initialized");
84607
84688
  const pricingResult = await refreshModelPricing(logger2);
84608
84689
  if (pricingResult.modelsUpdated === 0) {
84609
- logger2.warn("Model pricing refresh returned 0 models, seeding with fallback");
84610
- await seedFromFallback();
84690
+ logger2.warn("Model pricing refresh returned 0 models");
84611
84691
  }
84612
84692
  logger2.info({ modelsUpdated: pricingResult.modelsUpdated }, "Model pricing initialized");
84613
84693
  await scanSkills();