@askexenow/exe-os 0.9.37 → 0.9.39

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.
Files changed (73) hide show
  1. package/deploy/stack-manifests/v0.9.json +55 -0
  2. package/dist/bin/backfill-conversations.js +36 -9
  3. package/dist/bin/backfill-responses.js +36 -9
  4. package/dist/bin/backfill-vectors.js +36 -9
  5. package/dist/bin/cleanup-stale-review-tasks.js +37 -10
  6. package/dist/bin/cli.js +624 -204
  7. package/dist/bin/exe-agent.js +13 -5
  8. package/dist/bin/exe-assign.js +36 -9
  9. package/dist/bin/exe-boot.js +50 -20
  10. package/dist/bin/exe-call.js +134 -342
  11. package/dist/bin/exe-dispatch.js +36 -9
  12. package/dist/bin/exe-doctor.js +39 -12
  13. package/dist/bin/exe-export-behaviors.js +38 -11
  14. package/dist/bin/exe-forget.js +36 -9
  15. package/dist/bin/exe-gateway.js +64 -15
  16. package/dist/bin/exe-heartbeat.js +37 -10
  17. package/dist/bin/exe-kill.js +36 -9
  18. package/dist/bin/exe-launch-agent.js +287 -1081
  19. package/dist/bin/exe-new-employee.js +100 -14
  20. package/dist/bin/exe-pending-messages.js +36 -9
  21. package/dist/bin/exe-pending-notifications.js +36 -9
  22. package/dist/bin/exe-pending-reviews.js +36 -9
  23. package/dist/bin/exe-rename.js +1780 -204
  24. package/dist/bin/exe-review.js +36 -9
  25. package/dist/bin/exe-search.js +38 -11
  26. package/dist/bin/exe-session-cleanup.js +38 -11
  27. package/dist/bin/exe-start-codex.js +38 -11
  28. package/dist/bin/exe-start-opencode.js +38 -11
  29. package/dist/bin/exe-status.js +37 -10
  30. package/dist/bin/exe-team.js +36 -9
  31. package/dist/bin/git-sweep.js +36 -9
  32. package/dist/bin/graph-backfill.js +36 -9
  33. package/dist/bin/graph-export.js +36 -9
  34. package/dist/bin/install.js +70 -3
  35. package/dist/bin/intercom-check.js +38 -11
  36. package/dist/bin/scan-tasks.js +36 -9
  37. package/dist/bin/setup.js +20 -19
  38. package/dist/bin/shard-migrate.js +36 -9
  39. package/dist/bin/stack-update.js +308 -0
  40. package/dist/gateway/index.js +62 -13
  41. package/dist/hooks/bug-report-worker.js +40 -12
  42. package/dist/hooks/codex-stop-task-finalizer.js +38 -11
  43. package/dist/hooks/commit-complete.js +36 -9
  44. package/dist/hooks/error-recall.js +38 -11
  45. package/dist/hooks/ingest.js +38 -10
  46. package/dist/hooks/instructions-loaded.js +44 -12
  47. package/dist/hooks/notification.js +36 -9
  48. package/dist/hooks/post-compact.js +36 -9
  49. package/dist/hooks/post-tool-combined.js +39 -12
  50. package/dist/hooks/pre-compact.js +37 -10
  51. package/dist/hooks/pre-tool-use.js +38 -10
  52. package/dist/hooks/prompt-submit.js +43 -15
  53. package/dist/hooks/session-end.js +37 -10
  54. package/dist/hooks/session-start.js +49 -16
  55. package/dist/hooks/stop.js +37 -10
  56. package/dist/hooks/subagent-stop.js +36 -9
  57. package/dist/hooks/summary-worker.js +45 -18
  58. package/dist/index.js +60 -11
  59. package/dist/lib/consolidation.js +2 -1
  60. package/dist/lib/employee-templates.js +4 -3
  61. package/dist/lib/employees.js +2 -1
  62. package/dist/lib/exe-daemon.js +11229 -10537
  63. package/dist/lib/hybrid-search.js +38 -11
  64. package/dist/lib/identity.js +8 -3
  65. package/dist/lib/schedules.js +36 -9
  66. package/dist/lib/store.js +36 -9
  67. package/dist/mcp/server.js +6873 -6249
  68. package/dist/mcp/tools/create-task.js +10 -4
  69. package/dist/runtime/index.js +36 -9
  70. package/dist/tui/App.js +42 -13
  71. package/package.json +4 -1
  72. package/stack.release.json +31 -0
  73. package/stack.release.schema.json +31 -0
@@ -302,7 +302,8 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
302
302
  }
303
303
  }
304
304
  function addEmployee(employees, employee) {
305
- const normalized = { ...employee, name: employee.name.toLowerCase() };
305
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
306
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
306
307
  if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
307
308
  throw new Error(`Employee '${normalized.name}' already exists`);
308
309
  }
@@ -1034,6 +1035,9 @@ function ensureDir() {
1034
1035
  function identityPath(agentId) {
1035
1036
  return path8.join(IDENTITY_DIR2, `${agentId}.md`);
1036
1037
  }
1038
+ function sanitizeIdentityBody(body) {
1039
+ return body.replace(/<!--[\s\S]*?-->/g, "").trim();
1040
+ }
1037
1041
  function parseFrontmatter(raw) {
1038
1042
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
1039
1043
  if (!match) {
@@ -1046,11 +1050,11 @@ function parseFrontmatter(raw) {
1046
1050
  created_by: "system",
1047
1051
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
1048
1052
  },
1049
- body: raw
1053
+ body: sanitizeIdentityBody(raw)
1050
1054
  };
1051
1055
  }
1052
1056
  const yamlStr = match[1];
1053
- const body = match[2].trim();
1057
+ const body = sanitizeIdentityBody(match[2]);
1054
1058
  const fm = {};
1055
1059
  for (const line of yamlStr.split("\n")) {
1056
1060
  const kv = line.match(/^(\w+):\s*(.+)$/);
@@ -1115,7 +1119,9 @@ function listIdentities() {
1115
1119
  const summary = lines[0]?.trim().slice(0, 120) ?? identity.frontmatter.title;
1116
1120
  results.push({
1117
1121
  agentId,
1118
- title: `${identity.frontmatter.title} (${identity.frontmatter.role.toUpperCase()})`,
1122
+ // User-facing/team-facing title only. `frontmatter.role` is internal
1123
+ // routing metadata and must not leak as an external title.
1124
+ title: identity.frontmatter.title,
1119
1125
  summary
1120
1126
  });
1121
1127
  }
@@ -1256,6 +1262,7 @@ var installer_exports = {};
1256
1262
  __export(installer_exports, {
1257
1263
  cleanOldShellFunctions: () => cleanOldShellFunctions,
1258
1264
  copySlashCommands: () => copySlashCommands,
1265
+ detectMcpNameCollisions: () => detectMcpNameCollisions,
1259
1266
  installStatusLine: () => installStatusLine,
1260
1267
  mergeHooks: () => mergeHooks,
1261
1268
  registerMcpServer: () => registerMcpServer,
@@ -1341,6 +1348,60 @@ name: ${skillName}
1341
1348
  await writeFile3(destPath, content);
1342
1349
  return true;
1343
1350
  }
1351
+ function readJsonFile(filePath) {
1352
+ try {
1353
+ return JSON.parse(readFileSync9(filePath, "utf-8"));
1354
+ } catch {
1355
+ return null;
1356
+ }
1357
+ }
1358
+ function findAncestorMcpJsons(startDir, homeDir) {
1359
+ const files = [];
1360
+ let dir = path11.resolve(startDir);
1361
+ const root = path11.parse(dir).root;
1362
+ const stop = path11.resolve(homeDir);
1363
+ while (dir !== root) {
1364
+ const candidate = path11.join(dir, ".mcp.json");
1365
+ if (existsSync11(candidate)) files.push(candidate);
1366
+ if (dir === stop) break;
1367
+ dir = path11.dirname(dir);
1368
+ }
1369
+ return files;
1370
+ }
1371
+ function pathApplies(projectPath, cwd) {
1372
+ const project = path11.resolve(projectPath);
1373
+ const current = path11.resolve(cwd);
1374
+ return current === project || current.startsWith(project + path11.sep);
1375
+ }
1376
+ function detectMcpNameCollisions(homeDir = os7.homedir(), cwd = process.cwd()) {
1377
+ const claudeJsonPath = path11.join(homeDir, ".claude.json");
1378
+ if (!existsSync11(claudeJsonPath)) return [];
1379
+ const claudeJson = readJsonFile(claudeJsonPath);
1380
+ if (!claudeJson?.projects) return [];
1381
+ const collisions = [];
1382
+ const mcpJsons = findAncestorMcpJsons(cwd, homeDir);
1383
+ if (mcpJsons.length === 0) return [];
1384
+ for (const [projectPath, projectConfig] of Object.entries(claudeJson.projects)) {
1385
+ if (!pathApplies(projectPath, cwd)) continue;
1386
+ const projectServerNames = new Set(Object.keys(projectConfig.mcpServers ?? {}));
1387
+ if (projectServerNames.size === 0) continue;
1388
+ const enabled = new Set(projectConfig.enabledMcpjsonServers ?? []);
1389
+ for (const mcpJsonPath of mcpJsons) {
1390
+ const mcpJson = readJsonFile(mcpJsonPath);
1391
+ const mcpServerNames = Object.keys(mcpJson?.mcpServers ?? {});
1392
+ for (const serverName of mcpServerNames) {
1393
+ if (!projectServerNames.has(serverName)) continue;
1394
+ collisions.push({
1395
+ mcpJsonPath,
1396
+ projectPath,
1397
+ serverName,
1398
+ disabledInMcpJson: !enabled.has(serverName)
1399
+ });
1400
+ }
1401
+ }
1402
+ }
1403
+ return collisions;
1404
+ }
1344
1405
  async function registerMcpServer(packageRoot, homeDir = os7.homedir()) {
1345
1406
  const claudeJsonPath = path11.join(homeDir, ".claude.json");
1346
1407
  let claudeJson = {};
@@ -1369,12 +1430,26 @@ async function registerMcpServer(packageRoot, homeDir = os7.homedir()) {
1369
1430
  if (osMatches) {
1370
1431
  await cleanSettingsJsonMcp(path11.join(homeDir, ".claude", "settings.json"));
1371
1432
  await migratePermissionsToExeOs(path11.join(homeDir, ".claude", "settings.json"));
1433
+ const collisions2 = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
1434
+ for (const c of collisions2) {
1435
+ process.stderr.write(
1436
+ `exe-os: WARNING Claude Code MCP name collision: ${c.serverName} exists in ${c.mcpJsonPath} and ~/.claude.json project ${c.projectPath}. Remove or rename the .mcp.json entry if tools do not surface.
1437
+ `
1438
+ );
1439
+ }
1372
1440
  return false;
1373
1441
  }
1374
1442
  claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
1375
1443
  await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
1376
1444
  await cleanSettingsJsonMcp(path11.join(homeDir, ".claude", "settings.json"));
1377
1445
  await migratePermissionsToExeOs(path11.join(homeDir, ".claude", "settings.json"));
1446
+ const collisions = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
1447
+ for (const c of collisions) {
1448
+ process.stderr.write(
1449
+ `exe-os: WARNING Claude Code MCP name collision: ${c.serverName} exists in ${c.mcpJsonPath} and ~/.claude.json project ${c.projectPath}. Remove or rename the .mcp.json entry if tools do not surface.
1450
+ `
1451
+ );
1452
+ }
1378
1453
  return true;
1379
1454
  }
1380
1455
  async function cleanSettingsJsonMcp(settingsPath) {
@@ -1676,8 +1751,6 @@ async function mergeHooks(packageRoot, homeDir = os7.homedir()) {
1676
1751
  "purge_document",
1677
1752
  "rerank_documents",
1678
1753
  "set_document_importance",
1679
- "create_wiki_page",
1680
- "update_wiki_page",
1681
1754
  "get_wiki_page",
1682
1755
  "list_wiki_pages",
1683
1756
  // System
@@ -2382,7 +2455,7 @@ var PLATFORM_PROCEDURES = [
2382
2455
  title: "MCP tools \u2014 wiki, documents, and content",
2383
2456
  domain: "tool-use",
2384
2457
  priority: "p1",
2385
- content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
2458
+ content: "wiki: read/list wiki pages only. Direct wiki write tools are removed; wiki updates flow through raw-data ingestion/projection into the curated wiki store. Legacy aliases: list_wiki_pages/get_wiki_page. crm: read/list/get CRM records from exe-db. raw_data: read capped raw landing-pad events from exe-db with payload opt-in. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
2386
2459
  },
2387
2460
  {
2388
2461
  title: "MCP tools \u2014 system, operations, and admin",
@@ -2400,7 +2473,7 @@ var PLATFORM_PROCEDURES = [
2400
2473
  title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
2401
2474
  domain: "tool-use",
2402
2475
  priority: "p1",
2403
- content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
2476
+ content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage customer-owned company procedures (Layer 0; actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
2404
2477
  }
2405
2478
  ];
2406
2479
  var PLATFORM_PROCEDURE_TITLES = new Set(
@@ -3112,11 +3185,12 @@ async function main() {
3112
3185
  );
3113
3186
  process.exit(1);
3114
3187
  }
3188
+ const rolePrompt = template ? personalizePrompt(template.systemPrompt, template.name, name) : buildCustomEmployeePrompt(name, "specialist");
3115
3189
  if (template) {
3190
+ const { systemPrompt: _templatePrompt, ...templateMeta } = template;
3116
3191
  newEmployee = {
3117
- ...template,
3192
+ ...templateMeta,
3118
3193
  name,
3119
- systemPrompt: personalizePrompt(template.systemPrompt, template.name, name),
3120
3194
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3121
3195
  templateName: effectiveTemplate,
3122
3196
  templateVersion: TEMPLATE_VERSION
@@ -3125,7 +3199,6 @@ async function main() {
3125
3199
  newEmployee = {
3126
3200
  name,
3127
3201
  role: "specialist",
3128
- systemPrompt: buildCustomEmployeePrompt(name, "specialist"),
3129
3202
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3130
3203
  templateName: "custom",
3131
3204
  templateVersion: TEMPLATE_VERSION
@@ -3160,13 +3233,26 @@ async function main() {
3160
3233
  };
3161
3234
  const templateKey = roleMap[effectiveTemplate] ?? null;
3162
3235
  const identityTemplate = templateKey ? getIdentityTemplate(templateKey) : null;
3236
+ const idPath = identityPath2(name);
3237
+ const dir = path12.dirname(idPath);
3238
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
3163
3239
  if (identityTemplate) {
3164
- const idPath = identityPath2(name);
3165
- const dir = path12.dirname(idPath);
3166
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
3167
3240
  const content = identityTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`);
3168
3241
  fs.writeFileSync(idPath, content, "utf-8");
3169
3242
  console.log(`Identity doc written: ~/.exe-os/identity/${name}.md`);
3243
+ } else if (!fs.existsSync(idPath)) {
3244
+ const content = `---
3245
+ role: specialist
3246
+ title: Specialist
3247
+ agent_id: ${name}
3248
+ org_level: specialist
3249
+ created_by: exe-new-employee
3250
+ updated_at: ${(/* @__PURE__ */ new Date()).toISOString()}
3251
+ ---
3252
+
3253
+ ${rolePrompt}`;
3254
+ fs.writeFileSync(idPath, content, "utf-8");
3255
+ console.log(`Identity doc written: ~/.exe-os/identity/${name}.md`);
3170
3256
  }
3171
3257
  } catch {
3172
3258
  }
@@ -3669,7 +3669,7 @@ __export(shard_manager_exports, {
3669
3669
  shardExists: () => shardExists
3670
3670
  });
3671
3671
  import path13 from "path";
3672
- import { existsSync as existsSync12, mkdirSync as mkdirSync5, readdirSync as readdirSync2 } from "fs";
3672
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5, readdirSync as readdirSync2, renameSync as renameSync4, statSync as statSync2 } from "fs";
3673
3673
  import { createClient as createClient2 } from "@libsql/client";
3674
3674
  function initShardManager(encryptionKey) {
3675
3675
  _encryptionKey = encryptionKey;
@@ -3691,7 +3691,7 @@ function getShardClient(projectName) {
3691
3691
  if (!_encryptionKey) {
3692
3692
  throw new Error("Shard manager not initialized. Call initShardManager() first.");
3693
3693
  }
3694
- const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3694
+ const safeName = safeShardName(projectName);
3695
3695
  if (!safeName || safeName === "unknown") {
3696
3696
  throw new Error(`Invalid project name for shard: "${projectName}" (resolved to "${safeName}")`);
3697
3697
  }
@@ -3713,9 +3713,12 @@ function getShardClient(projectName) {
3713
3713
  return client;
3714
3714
  }
3715
3715
  function shardExists(projectName) {
3716
- const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3716
+ const safeName = safeShardName(projectName);
3717
3717
  return existsSync12(path13.join(SHARDS_DIR, `${safeName}.db`));
3718
3718
  }
3719
+ function safeShardName(projectName) {
3720
+ return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3721
+ }
3719
3722
  function listShards() {
3720
3723
  if (!existsSync12(SHARDS_DIR)) return [];
3721
3724
  return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
@@ -3809,7 +3812,8 @@ async function ensureShardSchema(client) {
3809
3812
  "ALTER TABLE memories ADD COLUMN token_cost REAL",
3810
3813
  "ALTER TABLE memories ADD COLUMN audience TEXT",
3811
3814
  "ALTER TABLE memories ADD COLUMN language_type TEXT",
3812
- "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
3815
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
3816
+ "ALTER TABLE memories ADD COLUMN deleted_at TEXT"
3813
3817
  ]) {
3814
3818
  try {
3815
3819
  await client.execute(col);
@@ -3905,9 +3909,32 @@ async function ensureShardSchema(client) {
3905
3909
  }
3906
3910
  }
3907
3911
  async function getReadyShardClient(projectName) {
3908
- const client = getShardClient(projectName);
3909
- await ensureShardSchema(client);
3910
- return client;
3912
+ const safeName = safeShardName(projectName);
3913
+ let client = getShardClient(projectName);
3914
+ try {
3915
+ await ensureShardSchema(client);
3916
+ return client;
3917
+ } catch (err) {
3918
+ const message = err instanceof Error ? err.message : String(err);
3919
+ if (!/SQLITE_NOTADB|file is not a database/i.test(message)) throw err;
3920
+ client.close();
3921
+ _shards.delete(safeName);
3922
+ _shardLastAccess.delete(safeName);
3923
+ const dbPath = path13.join(SHARDS_DIR, `${safeName}.db`);
3924
+ if (existsSync12(dbPath)) {
3925
+ const stat = statSync2(dbPath);
3926
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3927
+ const archivedPath = path13.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
3928
+ renameSync4(dbPath, archivedPath);
3929
+ process.stderr.write(
3930
+ `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
3931
+ `
3932
+ );
3933
+ }
3934
+ client = getShardClient(projectName);
3935
+ await ensureShardSchema(client);
3936
+ return client;
3937
+ }
3911
3938
  }
3912
3939
  function evictLRU() {
3913
3940
  let oldest = null;
@@ -4128,7 +4155,7 @@ var init_platform_procedures = __esm({
4128
4155
  title: "MCP tools \u2014 wiki, documents, and content",
4129
4156
  domain: "tool-use",
4130
4157
  priority: "p1",
4131
- content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
4158
+ content: "wiki: read/list wiki pages only. Direct wiki write tools are removed; wiki updates flow through raw-data ingestion/projection into the curated wiki store. Legacy aliases: list_wiki_pages/get_wiki_page. crm: read/list/get CRM records from exe-db. raw_data: read capped raw landing-pad events from exe-db with payload opt-in. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
4132
4159
  },
4133
4160
  {
4134
4161
  title: "MCP tools \u2014 system, operations, and admin",
@@ -4146,7 +4173,7 @@ var init_platform_procedures = __esm({
4146
4173
  title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
4147
4174
  domain: "tool-use",
4148
4175
  priority: "p1",
4149
- content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
4176
+ content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage customer-owned company procedures (Layer 0; actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
4150
4177
  }
4151
4178
  ];
4152
4179
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -3735,7 +3735,7 @@ __export(shard_manager_exports, {
3735
3735
  shardExists: () => shardExists
3736
3736
  });
3737
3737
  import path14 from "path";
3738
- import { existsSync as existsSync13, mkdirSync as mkdirSync5, readdirSync as readdirSync3 } from "fs";
3738
+ import { existsSync as existsSync13, mkdirSync as mkdirSync5, readdirSync as readdirSync3, renameSync as renameSync4, statSync as statSync2 } from "fs";
3739
3739
  import { createClient as createClient2 } from "@libsql/client";
3740
3740
  function initShardManager(encryptionKey) {
3741
3741
  _encryptionKey = encryptionKey;
@@ -3757,7 +3757,7 @@ function getShardClient(projectName) {
3757
3757
  if (!_encryptionKey) {
3758
3758
  throw new Error("Shard manager not initialized. Call initShardManager() first.");
3759
3759
  }
3760
- const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3760
+ const safeName = safeShardName(projectName);
3761
3761
  if (!safeName || safeName === "unknown") {
3762
3762
  throw new Error(`Invalid project name for shard: "${projectName}" (resolved to "${safeName}")`);
3763
3763
  }
@@ -3779,9 +3779,12 @@ function getShardClient(projectName) {
3779
3779
  return client;
3780
3780
  }
3781
3781
  function shardExists(projectName) {
3782
- const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3782
+ const safeName = safeShardName(projectName);
3783
3783
  return existsSync13(path14.join(SHARDS_DIR, `${safeName}.db`));
3784
3784
  }
3785
+ function safeShardName(projectName) {
3786
+ return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3787
+ }
3785
3788
  function listShards() {
3786
3789
  if (!existsSync13(SHARDS_DIR)) return [];
3787
3790
  return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
@@ -3875,7 +3878,8 @@ async function ensureShardSchema(client) {
3875
3878
  "ALTER TABLE memories ADD COLUMN token_cost REAL",
3876
3879
  "ALTER TABLE memories ADD COLUMN audience TEXT",
3877
3880
  "ALTER TABLE memories ADD COLUMN language_type TEXT",
3878
- "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
3881
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
3882
+ "ALTER TABLE memories ADD COLUMN deleted_at TEXT"
3879
3883
  ]) {
3880
3884
  try {
3881
3885
  await client.execute(col);
@@ -3971,9 +3975,32 @@ async function ensureShardSchema(client) {
3971
3975
  }
3972
3976
  }
3973
3977
  async function getReadyShardClient(projectName) {
3974
- const client = getShardClient(projectName);
3975
- await ensureShardSchema(client);
3976
- return client;
3978
+ const safeName = safeShardName(projectName);
3979
+ let client = getShardClient(projectName);
3980
+ try {
3981
+ await ensureShardSchema(client);
3982
+ return client;
3983
+ } catch (err) {
3984
+ const message = err instanceof Error ? err.message : String(err);
3985
+ if (!/SQLITE_NOTADB|file is not a database/i.test(message)) throw err;
3986
+ client.close();
3987
+ _shards.delete(safeName);
3988
+ _shardLastAccess.delete(safeName);
3989
+ const dbPath = path14.join(SHARDS_DIR, `${safeName}.db`);
3990
+ if (existsSync13(dbPath)) {
3991
+ const stat = statSync2(dbPath);
3992
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3993
+ const archivedPath = path14.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
3994
+ renameSync4(dbPath, archivedPath);
3995
+ process.stderr.write(
3996
+ `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
3997
+ `
3998
+ );
3999
+ }
4000
+ client = getShardClient(projectName);
4001
+ await ensureShardSchema(client);
4002
+ return client;
4003
+ }
3977
4004
  }
3978
4005
  function evictLRU() {
3979
4006
  let oldest = null;
@@ -4194,7 +4221,7 @@ var init_platform_procedures = __esm({
4194
4221
  title: "MCP tools \u2014 wiki, documents, and content",
4195
4222
  domain: "tool-use",
4196
4223
  priority: "p1",
4197
- content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
4224
+ content: "wiki: read/list wiki pages only. Direct wiki write tools are removed; wiki updates flow through raw-data ingestion/projection into the curated wiki store. Legacy aliases: list_wiki_pages/get_wiki_page. crm: read/list/get CRM records from exe-db. raw_data: read capped raw landing-pad events from exe-db with payload opt-in. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
4198
4225
  },
4199
4226
  {
4200
4227
  title: "MCP tools \u2014 system, operations, and admin",
@@ -4212,7 +4239,7 @@ var init_platform_procedures = __esm({
4212
4239
  title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
4213
4240
  domain: "tool-use",
4214
4241
  priority: "p1",
4215
- content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
4242
+ content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage customer-owned company procedures (Layer 0; actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
4216
4243
  }
4217
4244
  ];
4218
4245
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -3774,7 +3774,7 @@ __export(shard_manager_exports, {
3774
3774
  shardExists: () => shardExists
3775
3775
  });
3776
3776
  import path15 from "path";
3777
- import { existsSync as existsSync14, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
3777
+ import { existsSync as existsSync14, mkdirSync as mkdirSync5, readdirSync as readdirSync4, renameSync as renameSync4, statSync as statSync2 } from "fs";
3778
3778
  import { createClient as createClient2 } from "@libsql/client";
3779
3779
  function initShardManager(encryptionKey) {
3780
3780
  _encryptionKey = encryptionKey;
@@ -3796,7 +3796,7 @@ function getShardClient(projectName) {
3796
3796
  if (!_encryptionKey) {
3797
3797
  throw new Error("Shard manager not initialized. Call initShardManager() first.");
3798
3798
  }
3799
- const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3799
+ const safeName = safeShardName(projectName);
3800
3800
  if (!safeName || safeName === "unknown") {
3801
3801
  throw new Error(`Invalid project name for shard: "${projectName}" (resolved to "${safeName}")`);
3802
3802
  }
@@ -3818,9 +3818,12 @@ function getShardClient(projectName) {
3818
3818
  return client;
3819
3819
  }
3820
3820
  function shardExists(projectName) {
3821
- const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3821
+ const safeName = safeShardName(projectName);
3822
3822
  return existsSync14(path15.join(SHARDS_DIR, `${safeName}.db`));
3823
3823
  }
3824
+ function safeShardName(projectName) {
3825
+ return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3826
+ }
3824
3827
  function listShards() {
3825
3828
  if (!existsSync14(SHARDS_DIR)) return [];
3826
3829
  return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
@@ -3914,7 +3917,8 @@ async function ensureShardSchema(client) {
3914
3917
  "ALTER TABLE memories ADD COLUMN token_cost REAL",
3915
3918
  "ALTER TABLE memories ADD COLUMN audience TEXT",
3916
3919
  "ALTER TABLE memories ADD COLUMN language_type TEXT",
3917
- "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
3920
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
3921
+ "ALTER TABLE memories ADD COLUMN deleted_at TEXT"
3918
3922
  ]) {
3919
3923
  try {
3920
3924
  await client.execute(col);
@@ -4010,9 +4014,32 @@ async function ensureShardSchema(client) {
4010
4014
  }
4011
4015
  }
4012
4016
  async function getReadyShardClient(projectName) {
4013
- const client = getShardClient(projectName);
4014
- await ensureShardSchema(client);
4015
- return client;
4017
+ const safeName = safeShardName(projectName);
4018
+ let client = getShardClient(projectName);
4019
+ try {
4020
+ await ensureShardSchema(client);
4021
+ return client;
4022
+ } catch (err) {
4023
+ const message = err instanceof Error ? err.message : String(err);
4024
+ if (!/SQLITE_NOTADB|file is not a database/i.test(message)) throw err;
4025
+ client.close();
4026
+ _shards.delete(safeName);
4027
+ _shardLastAccess.delete(safeName);
4028
+ const dbPath = path15.join(SHARDS_DIR, `${safeName}.db`);
4029
+ if (existsSync14(dbPath)) {
4030
+ const stat = statSync2(dbPath);
4031
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4032
+ const archivedPath = path15.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4033
+ renameSync4(dbPath, archivedPath);
4034
+ process.stderr.write(
4035
+ `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
4036
+ `
4037
+ );
4038
+ }
4039
+ client = getShardClient(projectName);
4040
+ await ensureShardSchema(client);
4041
+ return client;
4042
+ }
4016
4043
  }
4017
4044
  function evictLRU() {
4018
4045
  let oldest = null;
@@ -4233,7 +4260,7 @@ var init_platform_procedures = __esm({
4233
4260
  title: "MCP tools \u2014 wiki, documents, and content",
4234
4261
  domain: "tool-use",
4235
4262
  priority: "p1",
4236
- content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
4263
+ content: "wiki: read/list wiki pages only. Direct wiki write tools are removed; wiki updates flow through raw-data ingestion/projection into the curated wiki store. Legacy aliases: list_wiki_pages/get_wiki_page. crm: read/list/get CRM records from exe-db. raw_data: read capped raw landing-pad events from exe-db with payload opt-in. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
4237
4264
  },
4238
4265
  {
4239
4266
  title: "MCP tools \u2014 system, operations, and admin",
@@ -4251,7 +4278,7 @@ var init_platform_procedures = __esm({
4251
4278
  title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
4252
4279
  domain: "tool-use",
4253
4280
  priority: "p1",
4254
- content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
4281
+ content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage customer-owned company procedures (Layer 0; actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
4255
4282
  }
4256
4283
  ];
4257
4284
  PLATFORM_PROCEDURE_TITLES = new Set(