@askexenow/exe-os 0.9.54 → 0.9.56

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 (66) hide show
  1. package/dist/bin/backfill-conversations.js +65 -0
  2. package/dist/bin/backfill-responses.js +65 -0
  3. package/dist/bin/backfill-vectors.js +65 -0
  4. package/dist/bin/cleanup-stale-review-tasks.js +91 -8
  5. package/dist/bin/cli.js +79 -1
  6. package/dist/bin/exe-assign.js +65 -0
  7. package/dist/bin/exe-boot.js +332 -190
  8. package/dist/bin/exe-dispatch.js +245 -103
  9. package/dist/bin/exe-doctor.js +114 -3
  10. package/dist/bin/exe-export-behaviors.js +72 -0
  11. package/dist/bin/exe-forget.js +65 -0
  12. package/dist/bin/exe-gateway.js +242 -100
  13. package/dist/bin/exe-heartbeat.js +103 -20
  14. package/dist/bin/exe-kill.js +65 -0
  15. package/dist/bin/exe-launch-agent.js +72 -0
  16. package/dist/bin/exe-pending-messages.js +113 -30
  17. package/dist/bin/exe-pending-notifications.js +116 -33
  18. package/dist/bin/exe-pending-reviews.js +118 -35
  19. package/dist/bin/exe-rename.js +65 -0
  20. package/dist/bin/exe-review.js +65 -0
  21. package/dist/bin/exe-search.js +65 -0
  22. package/dist/bin/exe-session-cleanup.js +233 -91
  23. package/dist/bin/exe-start-codex.js +72 -0
  24. package/dist/bin/exe-start-opencode.js +72 -0
  25. package/dist/bin/exe-status.js +91 -8
  26. package/dist/bin/exe-team.js +65 -0
  27. package/dist/bin/git-sweep.js +247 -105
  28. package/dist/bin/graph-backfill.js +65 -0
  29. package/dist/bin/graph-export.js +65 -0
  30. package/dist/bin/intercom-check.js +235 -93
  31. package/dist/bin/scan-tasks.js +253 -111
  32. package/dist/bin/shard-migrate.js +65 -0
  33. package/dist/gateway/index.js +231 -89
  34. package/dist/hooks/bug-report-worker.js +223 -81
  35. package/dist/hooks/codex-stop-task-finalizer.js +122 -39
  36. package/dist/hooks/commit-complete.js +245 -103
  37. package/dist/hooks/error-recall.js +65 -0
  38. package/dist/hooks/ingest.js +65 -0
  39. package/dist/hooks/instructions-loaded.js +65 -0
  40. package/dist/hooks/notification.js +65 -0
  41. package/dist/hooks/post-compact.js +113 -30
  42. package/dist/hooks/post-tool-combined.js +65 -0
  43. package/dist/hooks/pre-compact.js +248 -106
  44. package/dist/hooks/pre-tool-use.js +134 -51
  45. package/dist/hooks/prompt-submit.js +241 -92
  46. package/dist/hooks/session-end.js +256 -114
  47. package/dist/hooks/session-start.js +118 -35
  48. package/dist/hooks/stop.js +125 -42
  49. package/dist/hooks/subagent-stop.js +113 -30
  50. package/dist/hooks/summary-worker.js +174 -91
  51. package/dist/index.js +268 -119
  52. package/dist/lib/exe-daemon.js +493 -307
  53. package/dist/lib/hybrid-search.js +65 -0
  54. package/dist/lib/messaging.js +32 -14
  55. package/dist/lib/schedules.js +65 -0
  56. package/dist/lib/store.js +65 -0
  57. package/dist/lib/tasks.js +180 -103
  58. package/dist/lib/tmux-routing.js +180 -103
  59. package/dist/mcp/server.js +468 -282
  60. package/dist/mcp/tools/create-task.js +194 -117
  61. package/dist/mcp/tools/list-tasks.js +45 -27
  62. package/dist/mcp/tools/send-message.js +36 -18
  63. package/dist/mcp/tools/update-task.js +189 -112
  64. package/dist/runtime/index.js +254 -105
  65. package/dist/tui/App.js +291 -149
  66. package/package.json +1 -1
@@ -2729,6 +2729,7 @@ var init_database = __esm({
2729
2729
  // src/lib/shard-manager.ts
2730
2730
  var shard_manager_exports = {};
2731
2731
  __export(shard_manager_exports, {
2732
+ auditShardHealth: () => auditShardHealth,
2732
2733
  disposeShards: () => disposeShards,
2733
2734
  ensureShardSchema: () => ensureShardSchema,
2734
2735
  getOpenShardCount: () => getOpenShardCount,
@@ -2795,6 +2796,70 @@ function listShards() {
2795
2796
  if (!existsSync7(SHARDS_DIR)) return [];
2796
2797
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2797
2798
  }
2799
+ async function auditShardHealth(options = {}) {
2800
+ if (!_encryptionKey) {
2801
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
2802
+ }
2803
+ const repair = options.repair === true;
2804
+ const dryRun = options.dryRun === true;
2805
+ const names = listShards();
2806
+ const shards = [];
2807
+ for (const name of names) {
2808
+ const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
2809
+ const stat2 = statSync2(dbPath);
2810
+ const item = {
2811
+ name,
2812
+ path: dbPath,
2813
+ ok: false,
2814
+ unreadable: false,
2815
+ error: null,
2816
+ size: stat2.size,
2817
+ mtime: stat2.mtime.toISOString(),
2818
+ memoryCount: null
2819
+ };
2820
+ const client = createClient2({
2821
+ url: `file:${dbPath}`,
2822
+ encryptionKey: _encryptionKey
2823
+ });
2824
+ try {
2825
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
2826
+ const hasMemories = await client.execute(
2827
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
2828
+ );
2829
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
2830
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
2831
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
2832
+ }
2833
+ item.ok = true;
2834
+ } catch (err) {
2835
+ const message = err instanceof Error ? err.message : String(err);
2836
+ item.error = message;
2837
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
2838
+ if (item.unreadable && repair && !dryRun) {
2839
+ client.close();
2840
+ _shards.delete(name);
2841
+ _shardLastAccess.delete(name);
2842
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2843
+ const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
2844
+ renameSync3(dbPath, archivedPath);
2845
+ item.archivedPath = archivedPath;
2846
+ }
2847
+ } finally {
2848
+ try {
2849
+ client.close();
2850
+ } catch {
2851
+ }
2852
+ }
2853
+ shards.push(item);
2854
+ }
2855
+ return {
2856
+ total: shards.length,
2857
+ ok: shards.filter((s) => s.ok).length,
2858
+ unreadable: shards.filter((s) => s.unreadable).length,
2859
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
2860
+ shards
2861
+ };
2862
+ }
2798
2863
  async function ensureShardSchema(client) {
2799
2864
  await client.execute("PRAGMA journal_mode = WAL");
2800
2865
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -2729,6 +2729,7 @@ var init_database = __esm({
2729
2729
  // src/lib/shard-manager.ts
2730
2730
  var shard_manager_exports = {};
2731
2731
  __export(shard_manager_exports, {
2732
+ auditShardHealth: () => auditShardHealth,
2732
2733
  disposeShards: () => disposeShards,
2733
2734
  ensureShardSchema: () => ensureShardSchema,
2734
2735
  getOpenShardCount: () => getOpenShardCount,
@@ -2795,6 +2796,70 @@ function listShards() {
2795
2796
  if (!existsSync7(SHARDS_DIR)) return [];
2796
2797
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2797
2798
  }
2799
+ async function auditShardHealth(options = {}) {
2800
+ if (!_encryptionKey) {
2801
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
2802
+ }
2803
+ const repair = options.repair === true;
2804
+ const dryRun = options.dryRun === true;
2805
+ const names = listShards();
2806
+ const shards = [];
2807
+ for (const name of names) {
2808
+ const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
2809
+ const stat2 = statSync2(dbPath);
2810
+ const item = {
2811
+ name,
2812
+ path: dbPath,
2813
+ ok: false,
2814
+ unreadable: false,
2815
+ error: null,
2816
+ size: stat2.size,
2817
+ mtime: stat2.mtime.toISOString(),
2818
+ memoryCount: null
2819
+ };
2820
+ const client = createClient2({
2821
+ url: `file:${dbPath}`,
2822
+ encryptionKey: _encryptionKey
2823
+ });
2824
+ try {
2825
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
2826
+ const hasMemories = await client.execute(
2827
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
2828
+ );
2829
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
2830
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
2831
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
2832
+ }
2833
+ item.ok = true;
2834
+ } catch (err) {
2835
+ const message = err instanceof Error ? err.message : String(err);
2836
+ item.error = message;
2837
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
2838
+ if (item.unreadable && repair && !dryRun) {
2839
+ client.close();
2840
+ _shards.delete(name);
2841
+ _shardLastAccess.delete(name);
2842
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2843
+ const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
2844
+ renameSync3(dbPath, archivedPath);
2845
+ item.archivedPath = archivedPath;
2846
+ }
2847
+ } finally {
2848
+ try {
2849
+ client.close();
2850
+ } catch {
2851
+ }
2852
+ }
2853
+ shards.push(item);
2854
+ }
2855
+ return {
2856
+ total: shards.length,
2857
+ ok: shards.filter((s) => s.ok).length,
2858
+ unreadable: shards.filter((s) => s.unreadable).length,
2859
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
2860
+ shards
2861
+ };
2862
+ }
2798
2863
  async function ensureShardSchema(client) {
2799
2864
  await client.execute("PRAGMA journal_mode = WAL");
2800
2865
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -2725,6 +2725,7 @@ var init_database = __esm({
2725
2725
  // src/lib/shard-manager.ts
2726
2726
  var shard_manager_exports = {};
2727
2727
  __export(shard_manager_exports, {
2728
+ auditShardHealth: () => auditShardHealth,
2728
2729
  disposeShards: () => disposeShards,
2729
2730
  ensureShardSchema: () => ensureShardSchema,
2730
2731
  getOpenShardCount: () => getOpenShardCount,
@@ -2791,6 +2792,70 @@ function listShards() {
2791
2792
  if (!existsSync7(SHARDS_DIR)) return [];
2792
2793
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2793
2794
  }
2795
+ async function auditShardHealth(options = {}) {
2796
+ if (!_encryptionKey) {
2797
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
2798
+ }
2799
+ const repair = options.repair === true;
2800
+ const dryRun = options.dryRun === true;
2801
+ const names = listShards();
2802
+ const shards = [];
2803
+ for (const name of names) {
2804
+ const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
2805
+ const stat = statSync2(dbPath);
2806
+ const item = {
2807
+ name,
2808
+ path: dbPath,
2809
+ ok: false,
2810
+ unreadable: false,
2811
+ error: null,
2812
+ size: stat.size,
2813
+ mtime: stat.mtime.toISOString(),
2814
+ memoryCount: null
2815
+ };
2816
+ const client = createClient2({
2817
+ url: `file:${dbPath}`,
2818
+ encryptionKey: _encryptionKey
2819
+ });
2820
+ try {
2821
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
2822
+ const hasMemories = await client.execute(
2823
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
2824
+ );
2825
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
2826
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
2827
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
2828
+ }
2829
+ item.ok = true;
2830
+ } catch (err) {
2831
+ const message = err instanceof Error ? err.message : String(err);
2832
+ item.error = message;
2833
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
2834
+ if (item.unreadable && repair && !dryRun) {
2835
+ client.close();
2836
+ _shards.delete(name);
2837
+ _shardLastAccess.delete(name);
2838
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2839
+ const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
2840
+ renameSync3(dbPath, archivedPath);
2841
+ item.archivedPath = archivedPath;
2842
+ }
2843
+ } finally {
2844
+ try {
2845
+ client.close();
2846
+ } catch {
2847
+ }
2848
+ }
2849
+ shards.push(item);
2850
+ }
2851
+ return {
2852
+ total: shards.length,
2853
+ ok: shards.filter((s) => s.ok).length,
2854
+ unreadable: shards.filter((s) => s.unreadable).length,
2855
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
2856
+ shards
2857
+ };
2858
+ }
2794
2859
  async function ensureShardSchema(client) {
2795
2860
  await client.execute("PRAGMA journal_mode = WAL");
2796
2861
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -3287,6 +3287,7 @@ var init_memory_write_governor = __esm({
3287
3287
  // src/lib/shard-manager.ts
3288
3288
  var shard_manager_exports = {};
3289
3289
  __export(shard_manager_exports, {
3290
+ auditShardHealth: () => auditShardHealth,
3290
3291
  disposeShards: () => disposeShards,
3291
3292
  ensureShardSchema: () => ensureShardSchema,
3292
3293
  getOpenShardCount: () => getOpenShardCount,
@@ -3353,6 +3354,70 @@ function listShards() {
3353
3354
  if (!existsSync7(SHARDS_DIR)) return [];
3354
3355
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3355
3356
  }
3357
+ async function auditShardHealth(options = {}) {
3358
+ if (!_encryptionKey) {
3359
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
3360
+ }
3361
+ const repair = options.repair === true;
3362
+ const dryRun = options.dryRun === true;
3363
+ const names = listShards();
3364
+ const shards = [];
3365
+ for (const name of names) {
3366
+ const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
3367
+ const stat = statSync2(dbPath);
3368
+ const item = {
3369
+ name,
3370
+ path: dbPath,
3371
+ ok: false,
3372
+ unreadable: false,
3373
+ error: null,
3374
+ size: stat.size,
3375
+ mtime: stat.mtime.toISOString(),
3376
+ memoryCount: null
3377
+ };
3378
+ const client = createClient2({
3379
+ url: `file:${dbPath}`,
3380
+ encryptionKey: _encryptionKey
3381
+ });
3382
+ try {
3383
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
3384
+ const hasMemories = await client.execute(
3385
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
3386
+ );
3387
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
3388
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
3389
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
3390
+ }
3391
+ item.ok = true;
3392
+ } catch (err) {
3393
+ const message = err instanceof Error ? err.message : String(err);
3394
+ item.error = message;
3395
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
3396
+ if (item.unreadable && repair && !dryRun) {
3397
+ client.close();
3398
+ _shards.delete(name);
3399
+ _shardLastAccess.delete(name);
3400
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3401
+ const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3402
+ renameSync3(dbPath, archivedPath);
3403
+ item.archivedPath = archivedPath;
3404
+ }
3405
+ } finally {
3406
+ try {
3407
+ client.close();
3408
+ } catch {
3409
+ }
3410
+ }
3411
+ shards.push(item);
3412
+ }
3413
+ return {
3414
+ total: shards.length,
3415
+ ok: shards.filter((s) => s.ok).length,
3416
+ unreadable: shards.filter((s) => s.unreadable).length,
3417
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
3418
+ shards
3419
+ };
3420
+ }
3356
3421
  async function ensureShardSchema(client) {
3357
3422
  await client.execute("PRAGMA journal_mode = WAL");
3358
3423
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -4820,10 +4885,27 @@ var init_plan_limits = __esm({
4820
4885
  }
4821
4886
  });
4822
4887
 
4823
- // src/lib/tmux-routing.ts
4824
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync2 } from "fs";
4825
- import path13 from "path";
4888
+ // src/lib/agent-symlinks.ts
4826
4889
  import os9 from "os";
4890
+ import path13 from "path";
4891
+ import {
4892
+ existsSync as existsSync12,
4893
+ lstatSync,
4894
+ mkdirSync as mkdirSync5,
4895
+ readlinkSync as readlinkSync2,
4896
+ symlinkSync as symlinkSync2
4897
+ } from "fs";
4898
+ var init_agent_symlinks = __esm({
4899
+ "src/lib/agent-symlinks.ts"() {
4900
+ "use strict";
4901
+ init_employees();
4902
+ }
4903
+ });
4904
+
4905
+ // src/lib/tmux-routing.ts
4906
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync13, appendFileSync, readdirSync as readdirSync2 } from "fs";
4907
+ import path14 from "path";
4908
+ import os10 from "os";
4827
4909
  import { fileURLToPath as fileURLToPath2 } from "url";
4828
4910
  function getMySession() {
4829
4911
  return getTransport().getMySession();
@@ -4836,7 +4918,7 @@ function extractRootExe(name) {
4836
4918
  }
4837
4919
  function getParentExe(sessionKey) {
4838
4920
  try {
4839
- const data = JSON.parse(readFileSync9(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4921
+ const data = JSON.parse(readFileSync9(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4840
4922
  return data.parentExe || null;
4841
4923
  } catch {
4842
4924
  return null;
@@ -4879,10 +4961,11 @@ var init_tmux_routing = __esm({
4879
4961
  init_intercom_queue();
4880
4962
  init_plan_limits();
4881
4963
  init_employees();
4882
- SPAWN_LOCK_DIR = path13.join(os9.homedir(), ".exe-os", "spawn-locks");
4883
- SESSION_CACHE = path13.join(os9.homedir(), ".exe-os", "session-cache");
4884
- INTERCOM_LOG2 = path13.join(os9.homedir(), ".exe-os", "intercom.log");
4885
- DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
4964
+ init_agent_symlinks();
4965
+ SPAWN_LOCK_DIR = path14.join(os10.homedir(), ".exe-os", "spawn-locks");
4966
+ SESSION_CACHE = path14.join(os10.homedir(), ".exe-os", "session-cache");
4967
+ INTERCOM_LOG2 = path14.join(os10.homedir(), ".exe-os", "intercom.log");
4968
+ DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
4886
4969
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4887
4970
  }
4888
4971
  });
package/dist/bin/cli.js CHANGED
@@ -7157,6 +7157,7 @@ var init_memory_write_governor = __esm({
7157
7157
  // src/lib/shard-manager.ts
7158
7158
  var shard_manager_exports = {};
7159
7159
  __export(shard_manager_exports, {
7160
+ auditShardHealth: () => auditShardHealth,
7160
7161
  disposeShards: () => disposeShards,
7161
7162
  ensureShardSchema: () => ensureShardSchema,
7162
7163
  getOpenShardCount: () => getOpenShardCount,
@@ -7223,6 +7224,70 @@ function listShards() {
7223
7224
  if (!existsSync15(SHARDS_DIR)) return [];
7224
7225
  return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
7225
7226
  }
7227
+ async function auditShardHealth(options = {}) {
7228
+ if (!_encryptionKey) {
7229
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
7230
+ }
7231
+ const repair = options.repair === true;
7232
+ const dryRun = options.dryRun === true;
7233
+ const names = listShards();
7234
+ const shards = [];
7235
+ for (const name of names) {
7236
+ const dbPath = path15.join(SHARDS_DIR, `${name}.db`);
7237
+ const stat2 = statSync4(dbPath);
7238
+ const item = {
7239
+ name,
7240
+ path: dbPath,
7241
+ ok: false,
7242
+ unreadable: false,
7243
+ error: null,
7244
+ size: stat2.size,
7245
+ mtime: stat2.mtime.toISOString(),
7246
+ memoryCount: null
7247
+ };
7248
+ const client = createClient2({
7249
+ url: `file:${dbPath}`,
7250
+ encryptionKey: _encryptionKey
7251
+ });
7252
+ try {
7253
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
7254
+ const hasMemories = await client.execute(
7255
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
7256
+ );
7257
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
7258
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
7259
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
7260
+ }
7261
+ item.ok = true;
7262
+ } catch (err) {
7263
+ const message = err instanceof Error ? err.message : String(err);
7264
+ item.error = message;
7265
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
7266
+ if (item.unreadable && repair && !dryRun) {
7267
+ client.close();
7268
+ _shards.delete(name);
7269
+ _shardLastAccess.delete(name);
7270
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
7271
+ const archivedPath = path15.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
7272
+ renameSync3(dbPath, archivedPath);
7273
+ item.archivedPath = archivedPath;
7274
+ }
7275
+ } finally {
7276
+ try {
7277
+ client.close();
7278
+ } catch {
7279
+ }
7280
+ }
7281
+ shards.push(item);
7282
+ }
7283
+ return {
7284
+ total: shards.length,
7285
+ ok: shards.filter((s) => s.ok).length,
7286
+ unreadable: shards.filter((s) => s.unreadable).length,
7287
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
7288
+ shards
7289
+ };
7290
+ }
7226
7291
  async function ensureShardSchema(client) {
7227
7292
  await client.execute("PRAGMA journal_mode = WAL");
7228
7293
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -13296,7 +13361,19 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
13296
13361
  _resetCcAgentSupportCache();
13297
13362
  const hasAgentFlag = claudeSupportsAgentFlag();
13298
13363
  if (hasAgentFlag) {
13299
- identityFlag = ` --agent ${employeeName}`;
13364
+ const symlink = ensureAgentSymlink(employeeName);
13365
+ if (symlink.action === "created" || symlink.action === "already_correct") {
13366
+ identityFlag = ` --agent ${employeeName}`;
13367
+ } else if (existsSync24(identityPath2)) {
13368
+ identityFlag = ` --append-system-prompt-file ${identityPath2}`;
13369
+ legacyFallbackWarned = true;
13370
+ process.stderr.write(
13371
+ `[tmux-routing] WARN: ~/.claude/agents/${employeeName}.md is not the exe-os identity symlink (${symlink.conflict}). Using authoritative ${identityPath2} via --append-system-prompt-file.
13372
+ `
13373
+ );
13374
+ } else {
13375
+ identityFlag = ` --agent ${employeeName}`;
13376
+ }
13300
13377
  } else if (existsSync24(identityPath2)) {
13301
13378
  identityFlag = ` --append-system-prompt-file ${identityPath2}`;
13302
13379
  legacyFallbackWarned = true;
@@ -13485,6 +13562,7 @@ var init_tmux_routing = __esm({
13485
13562
  init_intercom_queue();
13486
13563
  init_plan_limits();
13487
13564
  init_employees();
13565
+ init_agent_symlinks();
13488
13566
  SPAWN_LOCK_DIR = path29.join(os15.homedir(), ".exe-os", "spawn-locks");
13489
13567
  SESSION_CACHE = path29.join(os15.homedir(), ".exe-os", "session-cache");
13490
13568
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
@@ -2739,6 +2739,7 @@ var init_database = __esm({
2739
2739
  // src/lib/shard-manager.ts
2740
2740
  var shard_manager_exports = {};
2741
2741
  __export(shard_manager_exports, {
2742
+ auditShardHealth: () => auditShardHealth,
2742
2743
  disposeShards: () => disposeShards,
2743
2744
  ensureShardSchema: () => ensureShardSchema,
2744
2745
  getOpenShardCount: () => getOpenShardCount,
@@ -2805,6 +2806,70 @@ function listShards() {
2805
2806
  if (!existsSync7(SHARDS_DIR)) return [];
2806
2807
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2807
2808
  }
2809
+ async function auditShardHealth(options = {}) {
2810
+ if (!_encryptionKey) {
2811
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
2812
+ }
2813
+ const repair = options.repair === true;
2814
+ const dryRun = options.dryRun === true;
2815
+ const names = listShards();
2816
+ const shards = [];
2817
+ for (const name of names) {
2818
+ const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
2819
+ const stat = statSync2(dbPath);
2820
+ const item = {
2821
+ name,
2822
+ path: dbPath,
2823
+ ok: false,
2824
+ unreadable: false,
2825
+ error: null,
2826
+ size: stat.size,
2827
+ mtime: stat.mtime.toISOString(),
2828
+ memoryCount: null
2829
+ };
2830
+ const client = createClient2({
2831
+ url: `file:${dbPath}`,
2832
+ encryptionKey: _encryptionKey
2833
+ });
2834
+ try {
2835
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
2836
+ const hasMemories = await client.execute(
2837
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
2838
+ );
2839
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
2840
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
2841
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
2842
+ }
2843
+ item.ok = true;
2844
+ } catch (err) {
2845
+ const message = err instanceof Error ? err.message : String(err);
2846
+ item.error = message;
2847
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
2848
+ if (item.unreadable && repair && !dryRun) {
2849
+ client.close();
2850
+ _shards.delete(name);
2851
+ _shardLastAccess.delete(name);
2852
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2853
+ const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
2854
+ renameSync3(dbPath, archivedPath);
2855
+ item.archivedPath = archivedPath;
2856
+ }
2857
+ } finally {
2858
+ try {
2859
+ client.close();
2860
+ } catch {
2861
+ }
2862
+ }
2863
+ shards.push(item);
2864
+ }
2865
+ return {
2866
+ total: shards.length,
2867
+ ok: shards.filter((s) => s.ok).length,
2868
+ unreadable: shards.filter((s) => s.unreadable).length,
2869
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
2870
+ shards
2871
+ };
2872
+ }
2808
2873
  async function ensureShardSchema(client) {
2809
2874
  await client.execute("PRAGMA journal_mode = WAL");
2810
2875
  await client.execute("PRAGMA busy_timeout = 30000");