bazilion 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/daemon.js CHANGED
@@ -9,7 +9,7 @@ var __export = (target, all) => {
9
9
  import { serve } from "@hono/node-server";
10
10
 
11
11
  // ../daemon/src/app.ts
12
- import { Hono as Hono10 } from "hono";
12
+ import { Hono as Hono11 } from "hono";
13
13
 
14
14
  // ../daemon/src/lib/middleware-auth.ts
15
15
  import { getCookie } from "hono/cookie";
@@ -725,7 +725,7 @@ function unarchiveAgent(db, id) {
725
725
  }
726
726
 
727
727
  // ../daemon/src/core/db/client.ts
728
- import { DatabaseSync } from "sqlite";
728
+ import { DatabaseSync } from "node:sqlite";
729
729
  function wrap(rawDb) {
730
730
  const cache = /* @__PURE__ */ new Map();
731
731
  function getStmt(sql) {
@@ -794,6 +794,9 @@ function openDb(path) {
794
794
  }
795
795
  };
796
796
  }
797
+ function inTx(db, fn) {
798
+ return db.raw.transaction(fn)();
799
+ }
797
800
 
798
801
  // ../daemon/src/core/db/migrate.ts
799
802
  import { readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
@@ -830,11 +833,11 @@ function runMigrations(db) {
830
833
  function deleteGroup(db, paths, id) {
831
834
  const g = get2(db, id, paths);
832
835
  if (!g) throw new Error(`group not found: ${id}`);
833
- const members = list(db, { includeArchived: true }).filter((a) => a.groupId === id);
834
- if (members.length > 0) {
835
- const names = members.map((a) => `${a.name} (${a.id.slice(0, 8)})`).join(", ");
836
+ const members2 = list(db, { includeArchived: true }).filter((a) => a.groupId === id);
837
+ if (members2.length > 0) {
838
+ const names = members2.map((a) => `${a.name} (${a.id.slice(0, 8)})`).join(", ");
836
839
  throw new Error(
837
- `cannot delete group "${id}": ${members.length} agent(s) still belong to it: ${names}. Move or archive them first.`
840
+ `cannot delete group "${id}": ${members2.length} agent(s) still belong to it: ${names}. Move or archive them first.`
838
841
  );
839
842
  }
840
843
  remove2(db, id);
@@ -871,6 +874,123 @@ function resolvePaths(home) {
871
874
 
872
875
  // ../daemon/src/core/profile/delete.ts
873
876
  import { existsSync as existsSync5, rmSync as rmSync2 } from "fs";
877
+
878
+ // ../daemon/src/core/repos/profileGroups.ts
879
+ var profileGroups_exports = {};
880
+ __export(profileGroups_exports, {
881
+ findReferencingProfile: () => findReferencingProfile,
882
+ get: () => get4,
883
+ insert: () => insert4,
884
+ list: () => list5,
885
+ members: () => members,
886
+ remove: () => remove5,
887
+ replaceMembers: () => replaceMembers,
888
+ update: () => update2
889
+ });
890
+ function toProfileGroup(r) {
891
+ return {
892
+ id: r.id,
893
+ name: r.name,
894
+ userMd: r.user_md,
895
+ createdAt: r.created_at,
896
+ updatedAt: r.updated_at
897
+ };
898
+ }
899
+ function toMember(r) {
900
+ return {
901
+ profileGroupId: r.profile_group_id,
902
+ position: r.position,
903
+ profileId: r.profile_id,
904
+ agentName: r.agent_name,
905
+ modelOverride: r.model_override,
906
+ reasoningLevel: r.reasoning_level
907
+ };
908
+ }
909
+ function insert4(db, p) {
910
+ const now = Date.now();
911
+ db.raw.run(
912
+ `INSERT INTO profile_groups (id, name, user_md, created_at, updated_at)
913
+ VALUES (?, ?, ?, ?, ?)`,
914
+ [p.id, p.name, p.userMd, now, now]
915
+ );
916
+ return { ...p, createdAt: now, updatedAt: now };
917
+ }
918
+ function get4(db, id) {
919
+ const row = db.raw.query("SELECT * FROM profile_groups WHERE id = ?").get(id);
920
+ return row ? toProfileGroup(row) : null;
921
+ }
922
+ function list5(db) {
923
+ return db.raw.query(
924
+ `SELECT pg.*, COALESCE(m.cnt, 0) AS member_count
925
+ FROM profile_groups pg
926
+ LEFT JOIN (
927
+ SELECT profile_group_id, COUNT(*) AS cnt
928
+ FROM profile_group_members
929
+ GROUP BY profile_group_id
930
+ ) m ON m.profile_group_id = pg.id
931
+ ORDER BY pg.created_at ASC`
932
+ ).all().map((r) => ({ ...toProfileGroup(r), memberCount: r.member_count }));
933
+ }
934
+ function update2(db, id, patch) {
935
+ const sets = [];
936
+ const args = [];
937
+ if (Object.hasOwn(patch, "name")) {
938
+ sets.push("name = ?");
939
+ args.push(patch.name);
940
+ }
941
+ if (Object.hasOwn(patch, "userMd")) {
942
+ sets.push("user_md = ?");
943
+ args.push(patch.userMd ?? null);
944
+ }
945
+ if (sets.length === 0) return;
946
+ sets.push("updated_at = ?");
947
+ args.push(Date.now());
948
+ args.push(id);
949
+ db.raw.run(`UPDATE profile_groups SET ${sets.join(", ")} WHERE id = ?`, args);
950
+ }
951
+ function remove5(db, id) {
952
+ db.raw.run("DELETE FROM profile_groups WHERE id = ?", [id]);
953
+ }
954
+ function members(db, profileGroupId) {
955
+ return db.raw.query(
956
+ `SELECT * FROM profile_group_members
957
+ WHERE profile_group_id = ?
958
+ ORDER BY position ASC`
959
+ ).all(profileGroupId).map(toMember);
960
+ }
961
+ function findReferencingProfile(db, profileId) {
962
+ return db.raw.query(
963
+ `SELECT DISTINCT pg.id AS id, pg.name AS name
964
+ FROM profile_groups pg
965
+ JOIN profile_group_members m ON m.profile_group_id = pg.id
966
+ WHERE m.profile_id = ?`
967
+ ).all(profileId);
968
+ }
969
+ function replaceMembers(db, profileGroupId, newMembers) {
970
+ const tx = db.raw.transaction(() => {
971
+ db.raw.run("DELETE FROM profile_group_members WHERE profile_group_id = ?", [profileGroupId]);
972
+ const stmt = db.raw.query(
973
+ `INSERT INTO profile_group_members
974
+ (profile_group_id, position, profile_id, agent_name, model_override, reasoning_level)
975
+ VALUES (?, ?, ?, ?, ?, ?)`
976
+ );
977
+ for (let i = 0; i < newMembers.length; i++) {
978
+ const m = newMembers[i];
979
+ if (!m) continue;
980
+ stmt.run(
981
+ profileGroupId,
982
+ i,
983
+ m.profileId,
984
+ m.agentName,
985
+ m.modelOverride ?? null,
986
+ m.reasoningLevel ?? null
987
+ );
988
+ }
989
+ });
990
+ tx();
991
+ }
992
+
993
+ // ../daemon/src/core/profile/delete.ts
874
994
  function deleteProfile(db, id) {
875
995
  const profile = get3(db, id);
876
996
  if (!profile) throw new Error(`profile not found: ${id}`);
@@ -881,6 +1001,13 @@ function deleteProfile(db, id) {
881
1001
  `cannot delete profile "${id}": ${agents.length} agent(s) still reference it: ${names}. Delete or re-profile them first.`
882
1002
  );
883
1003
  }
1004
+ const refGroups = findReferencingProfile(db, id);
1005
+ if (refGroups.length > 0) {
1006
+ const names = refGroups.map((g) => `${g.name} (${g.id})`).join(", ");
1007
+ throw new Error(
1008
+ `cannot delete profile "${id}": ${refGroups.length} profile group(s) still reference it: ${names}. Remove the member(s) first.`
1009
+ );
1010
+ }
884
1011
  remove3(db, id);
885
1012
  if (existsSync5(profile.dir)) {
886
1013
  rmSync2(profile.dir, { recursive: true, force: true });
@@ -920,6 +1047,135 @@ function updateProfile(db, paths, id, input) {
920
1047
  return updated;
921
1048
  }
922
1049
 
1050
+ // ../daemon/src/core/profile-group/spawn.ts
1051
+ import { readdirSync as readdirSync3, rmSync as rmSync4 } from "fs";
1052
+
1053
+ // ../daemon/src/core/profile-group/rm-with-retry.ts
1054
+ import { rmSync as rmSync3 } from "fs";
1055
+ var DEFAULT_RM_RETRY_DELAYS_MS = [100, 500, 2e3];
1056
+ async function rmWithRetry(target, opts = {}) {
1057
+ const rm = opts.rm ?? ((p) => rmSync3(p, { recursive: true, force: true }));
1058
+ const delays = opts.delays ?? DEFAULT_RM_RETRY_DELAYS_MS;
1059
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
1060
+ for (let attempt = 0; attempt <= delays.length; attempt++) {
1061
+ try {
1062
+ rm(target);
1063
+ return true;
1064
+ } catch {
1065
+ if (attempt === delays.length) return false;
1066
+ const delay = delays[attempt] ?? 0;
1067
+ await sleep(delay);
1068
+ }
1069
+ }
1070
+ return false;
1071
+ }
1072
+
1073
+ // ../daemon/src/core/profile-group/spawn.ts
1074
+ var SpawnProfileGroupError = class extends Error {
1075
+ name = "SpawnProfileGroupError";
1076
+ orphanAgentIds;
1077
+ cause;
1078
+ constructor(message, orphanAgentIds, cause) {
1079
+ super(message);
1080
+ this.orphanAgentIds = orphanAgentIds;
1081
+ this.cause = cause;
1082
+ }
1083
+ };
1084
+ function resolveMemberNames(existing, members2) {
1085
+ const taken = new Set(existing);
1086
+ const out = [];
1087
+ for (const m of members2) {
1088
+ let candidate = m.agentName;
1089
+ let n = 2;
1090
+ while (taken.has(candidate)) {
1091
+ candidate = `${m.agentName}-${n}`;
1092
+ n++;
1093
+ }
1094
+ taken.add(candidate);
1095
+ out.push(candidate);
1096
+ }
1097
+ return out;
1098
+ }
1099
+ async function spawnProfileGroup(db, paths, input) {
1100
+ const template = get4(db, input.profileGroupId);
1101
+ if (!template) {
1102
+ throw new Error(`profile group not found: ${input.profileGroupId}`);
1103
+ }
1104
+ const members2 = members(db, input.profileGroupId);
1105
+ const missing = [];
1106
+ for (const m of members2) {
1107
+ if (!get3(db, m.profileId)) missing.push(m.profileId);
1108
+ }
1109
+ if (missing.length > 0) {
1110
+ throw new Error(`profile group spawn: missing profiles: ${missing.join(", ")}`);
1111
+ }
1112
+ const targetSlug = input.groupSlug ?? DEFAULT_GROUP_ID;
1113
+ const targetGroupExists = !!get2(db, targetSlug, paths);
1114
+ const existingNames = new Set(
1115
+ targetGroupExists ? db.raw.query("SELECT name FROM agents WHERE group_id = ?").all(targetSlug).map((r) => r.name) : []
1116
+ );
1117
+ const resolvedNames = resolveMemberNames(existingNames, members2);
1118
+ const beforeAgentDirs = new Set(safeReaddir(paths.agentsDir));
1119
+ const beforeGroupDirs = new Set(safeReaddir(paths.groupsDir));
1120
+ let groupCreated = false;
1121
+ const created = [];
1122
+ try {
1123
+ inTx(db, () => {
1124
+ if (!targetGroupExists) {
1125
+ registerGroup(db, { id: targetSlug, name: targetSlug }, paths);
1126
+ groupCreated = true;
1127
+ }
1128
+ const seedUserMd = input.userMd ?? template.userMd ?? null;
1129
+ if (groupCreated && seedUserMd) {
1130
+ setUserMd(db, targetSlug, seedUserMd);
1131
+ }
1132
+ for (let i = 0; i < members2.length; i++) {
1133
+ const member = members2[i];
1134
+ const name = resolvedNames[i];
1135
+ if (!member || !name) continue;
1136
+ const agent = spawnAgent(db, paths, {
1137
+ profileId: member.profileId,
1138
+ name,
1139
+ modelOverride: member.modelOverride,
1140
+ reasoningLevel: member.reasoningLevel ?? "medium",
1141
+ groupId: targetSlug
1142
+ });
1143
+ created.push({ id: agent.id, name: agent.name });
1144
+ }
1145
+ });
1146
+ return { groupSlug: targetSlug, agents: created, orphanAgentIds: [] };
1147
+ } catch (err) {
1148
+ const newAgentDirs = safeReaddir(paths.agentsDir).filter((d) => !beforeAgentDirs.has(d));
1149
+ const orphans = [];
1150
+ for (const dir of newAgentDirs) {
1151
+ if (!await rmWithRetry(paths.agentDir(dir))) {
1152
+ orphans.push(dir);
1153
+ }
1154
+ }
1155
+ const newGroupDirs = safeReaddir(paths.groupsDir).filter((d) => !beforeGroupDirs.has(d));
1156
+ for (const slug of newGroupDirs) {
1157
+ try {
1158
+ rmSync4(paths.groupDir(slug), { recursive: true, force: true });
1159
+ } catch (cleanupErr) {
1160
+ console.error(`spawnProfileGroup: failed to clean up group dir ${slug}`, cleanupErr);
1161
+ }
1162
+ }
1163
+ for (const id of orphans) {
1164
+ console.error(`spawnProfileGroup: orphan agent dir left on disk: ${paths.agentDir(id)}`);
1165
+ }
1166
+ const original = err instanceof Error ? err.message : String(err);
1167
+ const message = orphans.length > 0 ? `${original} (orphan agent dirs: ${orphans.join(", ")})` : original;
1168
+ throw new SpawnProfileGroupError(message, orphans, err);
1169
+ }
1170
+ }
1171
+ function safeReaddir(dir) {
1172
+ try {
1173
+ return readdirSync3(dir);
1174
+ } catch {
1175
+ return [];
1176
+ }
1177
+ }
1178
+
923
1179
  // ../daemon/src/core/services.ts
924
1180
  var SERVICES = [
925
1181
  // --- LLM providers (configured via API keys / URLs) ---
@@ -1321,7 +1577,7 @@ var messages_exports = {};
1321
1577
  __export(messages_exports, {
1322
1578
  drainUnreadForAgent: () => drainUnreadForAgent,
1323
1579
  findReplies: () => findReplies,
1324
- get: () => get4,
1580
+ get: () => get5,
1325
1581
  listInbox: () => listInbox,
1326
1582
  listRecipientsWithUnread: () => listRecipientsWithUnread,
1327
1583
  markRead: () => markRead,
@@ -1357,7 +1613,7 @@ function send(db, input) {
1357
1613
  readAt: null
1358
1614
  };
1359
1615
  }
1360
- function get4(db, id) {
1616
+ function get5(db, id) {
1361
1617
  const row = db.raw.query("SELECT * FROM messages WHERE id = ?").get(id);
1362
1618
  return row ? toMessage(row) : null;
1363
1619
  }
@@ -1495,9 +1751,9 @@ function openSecrets(db, password) {
1495
1751
  // ../daemon/src/core/repos/skillMeta.ts
1496
1752
  var skillMeta_exports = {};
1497
1753
  __export(skillMeta_exports, {
1498
- get: () => get5,
1754
+ get: () => get6,
1499
1755
  listAll: () => listAll2,
1500
- remove: () => remove5,
1756
+ remove: () => remove6,
1501
1757
  upsert: () => upsert
1502
1758
  });
1503
1759
  function toMeta(r) {
@@ -1507,7 +1763,7 @@ function toMeta(r) {
1507
1763
  importedAt: r.imported_at
1508
1764
  };
1509
1765
  }
1510
- function get5(db, name) {
1766
+ function get6(db, name) {
1511
1767
  const row = db.raw.query("SELECT * FROM skill_meta WHERE name = ?").get(name);
1512
1768
  return row ? toMeta(row) : null;
1513
1769
  }
@@ -1515,7 +1771,7 @@ function listAll2(db) {
1515
1771
  return db.raw.query("SELECT * FROM skill_meta ORDER BY name ASC").all().map(toMeta);
1516
1772
  }
1517
1773
  function upsert(db, input) {
1518
- const existing = get5(db, input.name);
1774
+ const existing = get6(db, input.name);
1519
1775
  const source = input.source !== void 0 ? input.source : existing?.source ?? null;
1520
1776
  const importedAt = input.importedAt !== void 0 ? input.importedAt : existing?.importedAt ?? null;
1521
1777
  db.raw.run(
@@ -1526,19 +1782,19 @@ function upsert(db, input) {
1526
1782
  );
1527
1783
  return { name: input.name, source, importedAt };
1528
1784
  }
1529
- function remove5(db, name) {
1785
+ function remove6(db, name) {
1530
1786
  db.raw.run("DELETE FROM skill_meta WHERE name = ?", [name]);
1531
1787
  }
1532
1788
 
1533
1789
  // ../daemon/src/core/repos/triggers.ts
1534
1790
  var triggers_exports = {};
1535
1791
  __export(triggers_exports, {
1536
- get: () => get6,
1537
- insert: () => insert4,
1792
+ get: () => get7,
1793
+ insert: () => insert5,
1538
1794
  listEnabled: () => listEnabled2,
1539
1795
  listForAgent: () => listForAgent,
1540
1796
  markFired: () => markFired,
1541
- remove: () => remove6,
1797
+ remove: () => remove7,
1542
1798
  setEnabled: () => setEnabled2
1543
1799
  });
1544
1800
  import { randomUUID as randomUUID3 } from "crypto";
@@ -1555,7 +1811,7 @@ function toTrigger(r) {
1555
1811
  createdAt: r.created_at
1556
1812
  };
1557
1813
  }
1558
- function insert4(db, input) {
1814
+ function insert5(db, input) {
1559
1815
  const id = randomUUID3();
1560
1816
  const now = Date.now();
1561
1817
  const enabled = input.enabled === false ? 0 : 1;
@@ -1577,7 +1833,7 @@ function insert4(db, input) {
1577
1833
  createdAt: now
1578
1834
  };
1579
1835
  }
1580
- function get6(db, id) {
1836
+ function get7(db, id) {
1581
1837
  const row = db.raw.query("SELECT * FROM agent_triggers WHERE id = ?").get(id);
1582
1838
  return row ? toTrigger(row) : null;
1583
1839
  }
@@ -1600,7 +1856,7 @@ function setEnabled2(db, id, enabled) {
1600
1856
  function markFired(db, id, when = Date.now()) {
1601
1857
  db.raw.run("UPDATE agent_triggers SET last_fired_at = ? WHERE id = ?", [when, id]);
1602
1858
  }
1603
- function remove6(db, id) {
1859
+ function remove7(db, id) {
1604
1860
  db.raw.run("DELETE FROM agent_triggers WHERE id = ?", [id]);
1605
1861
  }
1606
1862
 
@@ -1609,9 +1865,9 @@ var webTokens_exports = {};
1609
1865
  __export(webTokens_exports, {
1610
1866
  create: () => create,
1611
1867
  findActiveByToken: () => findActiveByToken,
1612
- get: () => get7,
1868
+ get: () => get8,
1613
1869
  hashToken: () => hashToken,
1614
- list: () => list5,
1870
+ list: () => list6,
1615
1871
  markUsed: () => markUsed,
1616
1872
  revoke: () => revoke
1617
1873
  });
@@ -1643,11 +1899,11 @@ function create(db, label) {
1643
1899
  meta: { id, label, createdAt: now, lastUsedAt: null, revokedAt: null }
1644
1900
  };
1645
1901
  }
1646
- function list5(db, opts) {
1902
+ function list6(db, opts) {
1647
1903
  const sql = opts?.includeRevoked ? "SELECT * FROM web_tokens ORDER BY created_at ASC" : "SELECT * FROM web_tokens WHERE revoked_at IS NULL ORDER BY created_at ASC";
1648
1904
  return db.raw.query(sql).all().map(toToken);
1649
1905
  }
1650
- function get7(db, id) {
1906
+ function get8(db, id) {
1651
1907
  const row = db.raw.query("SELECT * FROM web_tokens WHERE id = ?").get(id);
1652
1908
  return row ? toToken(row) : null;
1653
1909
  }
@@ -1702,7 +1958,7 @@ function mergeSecretsIntoEnv(db, password, env = process.env) {
1702
1958
  }
1703
1959
 
1704
1960
  // ../daemon/src/core/skills/import.ts
1705
- import { cpSync, existsSync as existsSync7, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync3, statSync as statSync2 } from "fs";
1961
+ import { cpSync, existsSync as existsSync7, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync5, statSync as statSync2 } from "fs";
1706
1962
  import { tmpdir } from "os";
1707
1963
  import { basename, join as join8, resolve as resolve2, sep } from "path";
1708
1964
  import AdmZip from "adm-zip";
@@ -1758,11 +2014,11 @@ function extractZipSafely(zipPath) {
1758
2014
  }
1759
2015
  zip.extractAllTo(root, true);
1760
2016
  } catch (err) {
1761
- rmSync3(root, { recursive: true, force: true });
2017
+ rmSync5(root, { recursive: true, force: true });
1762
2018
  throw err;
1763
2019
  }
1764
2020
  let effectiveSource = root;
1765
- const topEntries = readdirSync3(root, { withFileTypes: true });
2021
+ const topEntries = readdirSync4(root, { withFileTypes: true });
1766
2022
  if (topEntries.length === 1 && topEntries[0]?.isDirectory()) {
1767
2023
  effectiveSource = join8(root, topEntries[0].name);
1768
2024
  }
@@ -1789,7 +2045,7 @@ function importSkills(paths, input) {
1789
2045
  try {
1790
2046
  return importSkillsFromDir(paths, source, input);
1791
2047
  } finally {
1792
- if (tempRoot) rmSync3(tempRoot, { recursive: true, force: true });
2048
+ if (tempRoot) rmSync5(tempRoot, { recursive: true, force: true });
1793
2049
  }
1794
2050
  }
1795
2051
  function importSkillsFromDir(paths, source, input) {
@@ -1797,7 +2053,7 @@ function importSkillsFromDir(paths, source, input) {
1797
2053
  if (existsSync7(join8(source, "SKILL.md"))) {
1798
2054
  candidates.push({ name: basename(source), dir: source });
1799
2055
  } else {
1800
- const entries = readdirSync3(source, { withFileTypes: true });
2056
+ const entries = readdirSync4(source, { withFileTypes: true });
1801
2057
  for (const e of entries) {
1802
2058
  if (!e.isDirectory()) continue;
1803
2059
  const skillDir = join8(source, e.name);
@@ -1959,9 +2215,9 @@ var DEFAULT_HEARTBEAT_EVERY_SEC = 30 * 60;
1959
2215
  import {
1960
2216
  existsSync as existsSync8,
1961
2217
  mkdirSync as mkdirSync4,
1962
- readdirSync as readdirSync4,
2218
+ readdirSync as readdirSync5,
1963
2219
  readFileSync as readFileSync6,
1964
- rmSync as rmSync4,
2220
+ rmSync as rmSync6,
1965
2221
  statSync as statSync3,
1966
2222
  writeFileSync as writeFileSync4
1967
2223
  } from "fs";
@@ -1971,9 +2227,9 @@ import { dirname as dirname2, join as join9 } from "path";
1971
2227
  import {
1972
2228
  existsSync as existsSync9,
1973
2229
  mkdirSync as mkdirSync5,
1974
- readdirSync as readdirSync5,
2230
+ readdirSync as readdirSync6,
1975
2231
  readFileSync as readFileSync7,
1976
- rmSync as rmSync5,
2232
+ rmSync as rmSync7,
1977
2233
  statSync as statSync4,
1978
2234
  writeFileSync as writeFileSync5
1979
2235
  } from "fs";
@@ -2006,7 +2262,7 @@ function safeKey(root, key) {
2006
2262
  }
2007
2263
  function walkMd(dir, prefix, out) {
2008
2264
  if (!existsSync9(dir)) return;
2009
- for (const e of readdirSync5(dir, { withFileTypes: true })) {
2265
+ for (const e of readdirSync6(dir, { withFileTypes: true })) {
2010
2266
  if (e.name.startsWith(".")) continue;
2011
2267
  const full = join10(dir, e.name);
2012
2268
  const key = prefix ? `${prefix}/${e.name}` : e.name;
@@ -2081,7 +2337,7 @@ function qmdBackend(root) {
2081
2337
  },
2082
2338
  async remove(key) {
2083
2339
  const path = safeKey(root, key);
2084
- if (existsSync9(path)) rmSync5(path);
2340
+ if (existsSync9(path)) rmSync7(path);
2085
2341
  const store = await getStore(root);
2086
2342
  await store.update();
2087
2343
  }
@@ -2149,7 +2405,7 @@ function piMessagesToProviderView(messages) {
2149
2405
  }
2150
2406
 
2151
2407
  // ../daemon/src/runtime/pi/session.ts
2152
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
2408
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync8, statSync as statSync6 } from "fs";
2153
2409
  import { basename as basename2, join as join14 } from "path";
2154
2410
  import {
2155
2411
  AuthStorage,
@@ -2853,7 +3109,7 @@ var PROVIDERS = {
2853
3109
  function createProviderRegistry(config, opts = {}) {
2854
3110
  const cache = /* @__PURE__ */ new Map();
2855
3111
  const enabledSet = opts.enabledSet;
2856
- function get8(name) {
3112
+ function get9(name) {
2857
3113
  const cached = cache.get(name);
2858
3114
  if (cached) return cached;
2859
3115
  const entry = PROVIDERS[name];
@@ -2885,7 +3141,7 @@ function createProviderRegistry(config, opts = {}) {
2885
3141
  }
2886
3142
  const providerName = modelString.slice(0, idx);
2887
3143
  const model = modelString.slice(idx + 1);
2888
- return { provider: get8(providerName), model };
3144
+ return { provider: get9(providerName), model };
2889
3145
  },
2890
3146
  list() {
2891
3147
  return Object.entries(PROVIDERS).filter(([name, entry]) => {
@@ -3005,7 +3261,7 @@ This group's USER.md is empty. As you learn STABLE facts about the human (prefer
3005
3261
  import { Type as Type2 } from "typebox";
3006
3262
 
3007
3263
  // ../daemon/src/runtime/tools/bootstrap.ts
3008
- import { existsSync as existsSync11, rmSync as rmSync6 } from "fs";
3264
+ import { existsSync as existsSync11, rmSync as rmSync8 } from "fs";
3009
3265
  import { join as join12 } from "path";
3010
3266
  function bootstrapTool(agentDir) {
3011
3267
  return {
@@ -3017,7 +3273,7 @@ function bootstrapTool(agentDir) {
3017
3273
  async invoke() {
3018
3274
  const path = join12(agentDir, "BOOTSTRAP.md");
3019
3275
  if (existsSync11(path)) {
3020
- rmSync6(path);
3276
+ rmSync8(path);
3021
3277
  return "BOOTSTRAP.md removed. Bootstrap is complete.";
3022
3278
  }
3023
3279
  return "BOOTSTRAP.md was already removed.";
@@ -3026,7 +3282,7 @@ function bootstrapTool(agentDir) {
3026
3282
  }
3027
3283
 
3028
3284
  // ../daemon/src/runtime/tools/home.ts
3029
- import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync5, writeFileSync as writeFileSync6 } from "fs";
3285
+ import { readdirSync as readdirSync7, readFileSync as readFileSync9, statSync as statSync5, writeFileSync as writeFileSync6 } from "fs";
3030
3286
  import { join as join13 } from "path";
3031
3287
  var HOME_FILES_READABLE = [
3032
3288
  "IDENTITY.md",
@@ -3118,7 +3374,7 @@ function homeTools(agentDir) {
3118
3374
  if (entries.length === 0) {
3119
3375
  const dirEntries = (() => {
3120
3376
  try {
3121
- return readdirSync6(agentDir);
3377
+ return readdirSync7(agentDir);
3122
3378
  } catch {
3123
3379
  return [];
3124
3380
  }
@@ -3903,13 +4159,7 @@ function webTools(opts) {
3903
4159
  extracted = { text: body };
3904
4160
  }
3905
4161
  if (isHtml && !firecrawlDisabled && extracted.text.length < FIRECRAWL_FALLBACK_THRESHOLD) {
3906
- const rescued = await firecrawlScrape(
3907
- result.finalUrl,
3908
- mode,
3909
- env,
3910
- fetchFn,
3911
- timeoutMs
3912
- );
4162
+ const rescued = await firecrawlScrape(result.finalUrl, mode, env, fetchFn, timeoutMs);
3913
4163
  if (rescued) {
3914
4164
  extracted = {
3915
4165
  ...rescued,
@@ -4199,7 +4449,7 @@ function loadSessionHead(agent, paths) {
4199
4449
  function findMostRecent(sessionDir) {
4200
4450
  if (!existsSync12(sessionDir)) return null;
4201
4451
  let newest = null;
4202
- for (const entry of readdirSync7(sessionDir)) {
4452
+ for (const entry of readdirSync8(sessionDir)) {
4203
4453
  if (!entry.endsWith(".jsonl")) continue;
4204
4454
  const path = join14(sessionDir, entry);
4205
4455
  try {
@@ -4446,10 +4696,9 @@ async function dispatch(req, messagingHost, userMdHost) {
4446
4696
  result = await requireMessagingHost(messagingHost, req.method).sendMessage(req.args);
4447
4697
  break;
4448
4698
  case "listInbox":
4449
- result = await requireMessagingHost(messagingHost, req.method).listInbox(
4450
- req.args.agentId,
4451
- { unreadOnly: req.args.unreadOnly }
4452
- );
4699
+ result = await requireMessagingHost(messagingHost, req.method).listInbox(req.args.agentId, {
4700
+ unreadOnly: req.args.unreadOnly
4701
+ });
4453
4702
  break;
4454
4703
  case "markRead":
4455
4704
  await requireMessagingHost(messagingHost, req.method).markRead(req.args.messageId);
@@ -4916,7 +5165,7 @@ async function authMiddleware(c, next) {
4916
5165
  }
4917
5166
 
4918
5167
  // ../daemon/src/routes/agents.ts
4919
- import { existsSync as existsSync15, readdirSync as readdirSync8, readFileSync as readFileSync10, rmSync as rmSync7 } from "fs";
5168
+ import { existsSync as existsSync15, readdirSync as readdirSync9, readFileSync as readFileSync10, rmSync as rmSync9 } from "fs";
4920
5169
  import { join as join15 } from "path";
4921
5170
 
4922
5171
  // ../../packages/api-types/src/entities.ts
@@ -5463,10 +5712,10 @@ agentsRouter.post("/:id/chat/reset", (c) => {
5463
5712
  const sessionsDir = join15(paths.agentDir(agent.id), "sessions");
5464
5713
  let deleted = 0;
5465
5714
  if (existsSync15(sessionsDir)) {
5466
- for (const file of readdirSync8(sessionsDir)) {
5715
+ for (const file of readdirSync9(sessionsDir)) {
5467
5716
  if (!file.endsWith(".jsonl")) continue;
5468
5717
  try {
5469
- rmSync7(join15(sessionsDir, file));
5718
+ rmSync9(join15(sessionsDir, file));
5470
5719
  deleted++;
5471
5720
  } catch {
5472
5721
  }
@@ -6158,11 +6407,147 @@ miscRouter.delete("/tokens/:id", (c) => {
6158
6407
  return c.body(null, 204);
6159
6408
  });
6160
6409
 
6410
+ // ../daemon/src/routes/profile-groups.ts
6411
+ import { Hono as Hono7 } from "hono";
6412
+ var profileGroupsRouter = new Hono7();
6413
+ profileGroupsRouter.get("/", (c) => {
6414
+ const { db } = getCtx();
6415
+ return c.json(profileGroups_exports.list(db));
6416
+ });
6417
+ profileGroupsRouter.get("/:id", (c) => {
6418
+ const { db } = getCtx();
6419
+ const id = c.req.param("id");
6420
+ const group = profileGroups_exports.get(db, id);
6421
+ if (!group) return c.json({ error: `profile group not found: ${id}` }, 404);
6422
+ const body = {
6423
+ group,
6424
+ members: profileGroups_exports.members(db, id)
6425
+ };
6426
+ return c.json(body);
6427
+ });
6428
+ profileGroupsRouter.post("/", async (c) => {
6429
+ const raw = await c.req.json().catch(() => null);
6430
+ if (!raw) return c.json({ error: "invalid JSON body" }, 400);
6431
+ const id = typeof raw.id === "string" ? raw.id : "";
6432
+ if (!id) return c.json({ error: "id is required" }, 400);
6433
+ try {
6434
+ validateSlug(id);
6435
+ } catch (err) {
6436
+ return c.json({ error: err.message }, 400);
6437
+ }
6438
+ const { db } = getCtx();
6439
+ if (profileGroups_exports.get(db, id)) {
6440
+ return c.json({ error: `profile group already exists: ${id}` }, 409);
6441
+ }
6442
+ const name = typeof raw.name === "string" && raw.name.length > 0 ? raw.name : id;
6443
+ const userMd = typeof raw.userMd === "string" ? raw.userMd : null;
6444
+ try {
6445
+ const inserted = profileGroups_exports.insert(db, { id, name, userMd });
6446
+ return c.json(inserted, 201);
6447
+ } catch (err) {
6448
+ return c.json({ error: err.message }, 400);
6449
+ }
6450
+ });
6451
+ profileGroupsRouter.patch("/:id", async (c) => {
6452
+ const raw = await c.req.json().catch(() => null);
6453
+ if (!raw) return c.json({ error: "invalid JSON body" }, 400);
6454
+ const { db } = getCtx();
6455
+ const id = c.req.param("id");
6456
+ if (!profileGroups_exports.get(db, id)) {
6457
+ return c.json({ error: `profile group not found: ${id}` }, 404);
6458
+ }
6459
+ const patch = {};
6460
+ if (Object.hasOwn(raw, "name") && typeof raw.name === "string") {
6461
+ patch.name = raw.name;
6462
+ }
6463
+ if (Object.hasOwn(raw, "userMd")) {
6464
+ patch.userMd = raw.userMd === null ? null : typeof raw.userMd === "string" ? raw.userMd : null;
6465
+ }
6466
+ profileGroups_exports.update(db, id, patch);
6467
+ return c.json(profileGroups_exports.get(db, id));
6468
+ });
6469
+ profileGroupsRouter.put("/:id/members", async (c) => {
6470
+ const raw = await c.req.json().catch(() => null);
6471
+ if (!raw || !Array.isArray(raw.members)) {
6472
+ return c.json({ error: "members array is required" }, 400);
6473
+ }
6474
+ const { db } = getCtx();
6475
+ const id = c.req.param("id");
6476
+ if (!profileGroups_exports.get(db, id)) {
6477
+ return c.json({ error: `profile group not found: ${id}` }, 404);
6478
+ }
6479
+ const cleaned = [];
6480
+ const missingProfiles = [];
6481
+ for (let i = 0; i < raw.members.length; i++) {
6482
+ const m = raw.members[i];
6483
+ if (!m || typeof m.profileId !== "string" || typeof m.agentName !== "string") {
6484
+ return c.json({ error: `member ${i}: profileId and agentName are required strings` }, 400);
6485
+ }
6486
+ if (!profiles_exports.get(db, m.profileId)) {
6487
+ missingProfiles.push(m.profileId);
6488
+ continue;
6489
+ }
6490
+ const modelOverride = m.modelOverride === null ? null : typeof m.modelOverride === "string" ? m.modelOverride : null;
6491
+ const reasoningLevel = m.reasoningLevel === null ? null : typeof m.reasoningLevel === "string" && REASONING_LEVELS.includes(m.reasoningLevel) ? m.reasoningLevel : null;
6492
+ cleaned.push({
6493
+ profileId: m.profileId,
6494
+ agentName: m.agentName,
6495
+ modelOverride,
6496
+ reasoningLevel
6497
+ });
6498
+ }
6499
+ if (missingProfiles.length > 0) {
6500
+ return c.json({ error: `missing profiles: ${[...new Set(missingProfiles)].join(", ")}` }, 400);
6501
+ }
6502
+ profileGroups_exports.replaceMembers(db, id, cleaned);
6503
+ return c.json({ members: profileGroups_exports.members(db, id) });
6504
+ });
6505
+ profileGroupsRouter.delete("/:id", (c) => {
6506
+ const { db } = getCtx();
6507
+ const id = c.req.param("id");
6508
+ if (!profileGroups_exports.get(db, id)) {
6509
+ return c.json({ error: `profile group not found: ${id}` }, 404);
6510
+ }
6511
+ profileGroups_exports.remove(db, id);
6512
+ return c.body(null, 204);
6513
+ });
6514
+ profileGroupsRouter.post("/:id/spawn", async (c) => {
6515
+ const raw = await c.req.json().catch(() => ({}));
6516
+ const body = raw ?? {};
6517
+ const groupSlug = typeof body.groupSlug === "string" ? body.groupSlug : void 0;
6518
+ const userMd = typeof body.userMd === "string" ? body.userMd : void 0;
6519
+ const { db, paths } = getCtx();
6520
+ const id = c.req.param("id");
6521
+ try {
6522
+ const result = await spawnProfileGroup(db, paths, {
6523
+ profileGroupId: id,
6524
+ groupSlug,
6525
+ userMd
6526
+ });
6527
+ const response = {
6528
+ groupSlug: result.groupSlug,
6529
+ agents: result.agents
6530
+ };
6531
+ if (result.orphanAgentIds.length > 0) response.orphanAgentIds = result.orphanAgentIds;
6532
+ return c.json(response);
6533
+ } catch (err) {
6534
+ if (err instanceof SpawnProfileGroupError) {
6535
+ return c.json({ error: err.message, orphanAgentIds: err.orphanAgentIds }, 500);
6536
+ }
6537
+ const msg = err.message;
6538
+ if (msg.startsWith("profile group not found")) return c.json({ error: msg }, 404);
6539
+ if (msg.startsWith("profile group spawn: missing profiles")) {
6540
+ return c.json({ error: msg }, 400);
6541
+ }
6542
+ return c.json({ error: msg }, 500);
6543
+ }
6544
+ });
6545
+
6161
6546
  // ../daemon/src/routes/profiles.ts
6162
6547
  import { existsSync as existsSync17, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
6163
6548
  import { join as join18 } from "path";
6164
- import { Hono as Hono7 } from "hono";
6165
- var profilesRouter = new Hono7();
6549
+ import { Hono as Hono8 } from "hono";
6550
+ var profilesRouter = new Hono8();
6166
6551
  profilesRouter.get("/", (c) => {
6167
6552
  const { db } = getCtx();
6168
6553
  const profiles = profiles_exports.list(db);
@@ -6293,12 +6678,12 @@ function toSkillsMode(v) {
6293
6678
  }
6294
6679
 
6295
6680
  // ../daemon/src/routes/skills.ts
6296
- import { existsSync as existsSync18, mkdtempSync as mkdtempSync2, rmSync as rmSync8, writeFileSync as writeFileSync9 } from "fs";
6681
+ import { existsSync as existsSync18, mkdtempSync as mkdtempSync2, rmSync as rmSync10, writeFileSync as writeFileSync9 } from "fs";
6297
6682
  import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
6298
6683
  import { join as join19 } from "path";
6299
- import { Hono as Hono8 } from "hono";
6684
+ import { Hono as Hono9 } from "hono";
6300
6685
  var MAX_ZIP_BYTES = 50 * 1024 * 1024;
6301
- var skillsRouter = new Hono8();
6686
+ var skillsRouter = new Hono9();
6302
6687
  skillsRouter.get("/", (c) => {
6303
6688
  const { db, paths } = getCtx();
6304
6689
  const out = [];
@@ -6325,7 +6710,7 @@ skillsRouter.delete("/:name", (c) => {
6325
6710
  const name = c.req.param("name");
6326
6711
  const dir = paths.skillDir(name);
6327
6712
  if (!existsSync18(dir)) return c.json({ error: `skill not found: ${name}` }, 404);
6328
- rmSync8(dir, { recursive: true, force: true });
6713
+ rmSync10(dir, { recursive: true, force: true });
6329
6714
  skillMeta_exports.remove(db, name);
6330
6715
  return c.body(null, 204);
6331
6716
  });
@@ -6348,7 +6733,7 @@ skillsRouter.post("/import", async (c) => {
6348
6733
  } catch (err) {
6349
6734
  return c.json({ error: err.message }, 400);
6350
6735
  } finally {
6351
- if (input.tempZipPath) rmSync8(input.tempZipPath, { recursive: true, force: true });
6736
+ if (input.tempZipPath) rmSync10(input.tempZipPath, { recursive: true, force: true });
6352
6737
  }
6353
6738
  });
6354
6739
  async function parseImportInput(request) {
@@ -6392,8 +6777,8 @@ async function parseImportInput(request) {
6392
6777
  }
6393
6778
 
6394
6779
  // ../daemon/src/routes/triggers.ts
6395
- import { Hono as Hono9 } from "hono";
6396
- var triggersRouter = new Hono9();
6780
+ import { Hono as Hono10 } from "hono";
6781
+ var triggersRouter = new Hono10();
6397
6782
  triggersRouter.delete("/:id", (c) => {
6398
6783
  const { db } = getCtx();
6399
6784
  const id = c.req.param("id");
@@ -6415,10 +6800,11 @@ triggersRouter.patch("/:id", async (c) => {
6415
6800
 
6416
6801
  // ../daemon/src/app.ts
6417
6802
  function createApp() {
6418
- const app2 = new Hono10();
6803
+ const app2 = new Hono11();
6419
6804
  app2.use("*", authMiddleware);
6420
6805
  app2.route("/api/agents", agentsRouter);
6421
6806
  app2.route("/api/groups", groupsRouter);
6807
+ app2.route("/api/profile-groups", profileGroupsRouter);
6422
6808
  app2.route("/api/profiles", profilesRouter);
6423
6809
  app2.route("/api/skills", skillsRouter);
6424
6810
  app2.route("/api/triggers", triggersRouter);