@askexenow/exe-os 0.9.53 → 0.9.55

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 (67) hide show
  1. package/dist/bin/backfill-conversations.js +103 -0
  2. package/dist/bin/backfill-responses.js +103 -0
  3. package/dist/bin/backfill-vectors.js +103 -0
  4. package/dist/bin/cleanup-stale-review-tasks.js +103 -0
  5. package/dist/bin/cli.js +121 -10
  6. package/dist/bin/exe-assign.js +103 -0
  7. package/dist/bin/exe-boot.js +96 -10
  8. package/dist/bin/exe-call.js +25 -0
  9. package/dist/bin/exe-cloud.js +31 -10
  10. package/dist/bin/exe-dispatch.js +103 -0
  11. package/dist/bin/exe-doctor.js +152 -3
  12. package/dist/bin/exe-export-behaviors.js +103 -0
  13. package/dist/bin/exe-forget.js +103 -0
  14. package/dist/bin/exe-gateway.js +103 -0
  15. package/dist/bin/exe-heartbeat.js +103 -0
  16. package/dist/bin/exe-kill.js +103 -0
  17. package/dist/bin/exe-launch-agent.js +103 -0
  18. package/dist/bin/exe-link.js +31 -10
  19. package/dist/bin/exe-new-employee.js +25 -0
  20. package/dist/bin/exe-pending-messages.js +103 -0
  21. package/dist/bin/exe-pending-notifications.js +103 -0
  22. package/dist/bin/exe-pending-reviews.js +103 -0
  23. package/dist/bin/exe-rename.js +103 -0
  24. package/dist/bin/exe-review.js +103 -0
  25. package/dist/bin/exe-search.js +103 -0
  26. package/dist/bin/exe-session-cleanup.js +103 -0
  27. package/dist/bin/exe-start-codex.js +103 -0
  28. package/dist/bin/exe-start-opencode.js +103 -0
  29. package/dist/bin/exe-status.js +103 -0
  30. package/dist/bin/exe-team.js +103 -0
  31. package/dist/bin/git-sweep.js +103 -0
  32. package/dist/bin/graph-backfill.js +103 -0
  33. package/dist/bin/graph-export.js +103 -0
  34. package/dist/bin/intercom-check.js +103 -0
  35. package/dist/bin/scan-tasks.js +103 -0
  36. package/dist/bin/setup.js +56 -10
  37. package/dist/bin/shard-migrate.js +103 -0
  38. package/dist/gateway/index.js +103 -0
  39. package/dist/hooks/bug-report-worker.js +103 -0
  40. package/dist/hooks/codex-stop-task-finalizer.js +103 -0
  41. package/dist/hooks/commit-complete.js +103 -0
  42. package/dist/hooks/error-recall.js +103 -0
  43. package/dist/hooks/ingest.js +103 -0
  44. package/dist/hooks/instructions-loaded.js +103 -0
  45. package/dist/hooks/notification.js +103 -0
  46. package/dist/hooks/post-compact.js +103 -0
  47. package/dist/hooks/post-tool-combined.js +103 -0
  48. package/dist/hooks/pre-compact.js +103 -0
  49. package/dist/hooks/pre-tool-use.js +103 -0
  50. package/dist/hooks/prompt-submit.js +103 -0
  51. package/dist/hooks/session-end.js +103 -0
  52. package/dist/hooks/session-start.js +103 -0
  53. package/dist/hooks/stop.js +103 -0
  54. package/dist/hooks/subagent-stop.js +103 -0
  55. package/dist/hooks/summary-worker.js +96 -10
  56. package/dist/index.js +103 -0
  57. package/dist/lib/cloud-sync.js +31 -10
  58. package/dist/lib/employee-templates.js +25 -0
  59. package/dist/lib/exe-daemon.js +165 -14
  60. package/dist/lib/hybrid-search.js +103 -0
  61. package/dist/lib/keychain.js +31 -10
  62. package/dist/lib/schedules.js +103 -0
  63. package/dist/lib/store.js +103 -0
  64. package/dist/mcp/server.js +164 -13
  65. package/dist/runtime/index.js +103 -0
  66. package/dist/tui/App.js +96 -10
  67. package/package.json +1 -1
package/dist/bin/cli.js CHANGED
@@ -1919,6 +1919,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
1919
1919
  return null;
1920
1920
  }
1921
1921
  }
1922
+ async function writeMachineBoundFileFallback(b64) {
1923
+ const dir = getKeyDir();
1924
+ await mkdir4(dir, { recursive: true });
1925
+ const keyPath = getKeyPath();
1926
+ const machineKey = deriveMachineKey();
1927
+ if (machineKey) {
1928
+ const encrypted = encryptWithMachineKey(b64, machineKey);
1929
+ await writeFile4(keyPath, encrypted + "\n", "utf-8");
1930
+ await chmod2(keyPath, 384);
1931
+ return "encrypted";
1932
+ }
1933
+ await writeFile4(keyPath, b64 + "\n", "utf-8");
1934
+ await chmod2(keyPath, 384);
1935
+ return "plaintext";
1936
+ }
1922
1937
  async function getMasterKey() {
1923
1938
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
1924
1939
  if (nativeValue) {
@@ -1970,6 +1985,20 @@ async function getMasterKey() {
1970
1985
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
1971
1986
  if (migrated) {
1972
1987
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
1988
+ try {
1989
+ await unlink(keyPath);
1990
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
1991
+ } catch {
1992
+ }
1993
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
1994
+ const fallback = await writeMachineBoundFileFallback(b64Value);
1995
+ if (fallback === "encrypted") {
1996
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
1997
+ } else {
1998
+ process.stderr.write(
1999
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
2000
+ );
2001
+ }
1973
2002
  }
1974
2003
  return key;
1975
2004
  } catch (err) {
@@ -1993,18 +2022,10 @@ async function setMasterKey(key) {
1993
2022
  } catch {
1994
2023
  }
1995
2024
  }
1996
- const dir = getKeyDir();
1997
- await mkdir4(dir, { recursive: true });
1998
- const keyPath = getKeyPath();
1999
- const machineKey = deriveMachineKey();
2000
- if (machineKey) {
2001
- const encrypted = encryptWithMachineKey(b64, machineKey);
2002
- await writeFile4(keyPath, encrypted + "\n", "utf-8");
2003
- await chmod2(keyPath, 384);
2025
+ const fallback = await writeMachineBoundFileFallback(b64);
2026
+ if (fallback === "encrypted") {
2004
2027
  process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
2005
2028
  } else {
2006
- await writeFile4(keyPath, b64 + "\n", "utf-8");
2007
- await chmod2(keyPath, 384);
2008
2029
  process.stderr.write(
2009
2030
  "[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
2010
2031
  );
@@ -7136,6 +7157,7 @@ var init_memory_write_governor = __esm({
7136
7157
  // src/lib/shard-manager.ts
7137
7158
  var shard_manager_exports = {};
7138
7159
  __export(shard_manager_exports, {
7160
+ auditShardHealth: () => auditShardHealth,
7139
7161
  disposeShards: () => disposeShards,
7140
7162
  ensureShardSchema: () => ensureShardSchema,
7141
7163
  getOpenShardCount: () => getOpenShardCount,
@@ -7202,6 +7224,70 @@ function listShards() {
7202
7224
  if (!existsSync15(SHARDS_DIR)) return [];
7203
7225
  return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
7204
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
+ }
7205
7291
  async function ensureShardSchema(client) {
7206
7292
  await client.execute("PRAGMA journal_mode = WAL");
7207
7293
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -14458,6 +14544,31 @@ Audit method:
14458
14544
  6. Write structured report with PASS/FAIL per item
14459
14545
 
14460
14546
  After an audit, fix the findings yourself if you can. Don't hand off when you have the context.`
14547
+ },
14548
+ teddy: {
14549
+ name: "teddy",
14550
+ role: "Chief of Staff",
14551
+ systemPrompt: `You are teddy, the Chief of Staff and executive assistant. You help the founder recall context, understand what happened, prepare concise briefs, and triage inbound conversations. You report to the COO.
14552
+
14553
+ Your job is read-first, not action-first:
14554
+ - Retrieve memories, decisions, runbooks, and session context on demand
14555
+ - Summarize what matters without changing source data
14556
+ - Triage inbound conversations and surface likely bugs, requests, and follow-ups
14557
+ - Prepare daily briefs and "what changed?" summaries
14558
+ - Route recommended actions to the COO instead of taking them yourself
14559
+
14560
+ Permissions boundary:
14561
+ - You are read-only by default.
14562
+ - You may use recall_my_memory, ask_team_memory, get_memory_by_id, get_session_context, search_everything, query_conversations, list_tasks, and get_task.
14563
+ - You must not create tasks, update tasks, store memories, send WhatsApp messages, mutate CRM/wiki/documents, deploy, or change configuration unless the founder explicitly promotes your permissions.
14564
+ - If a requested action requires write access, explain the action and recommend that the COO dispatch it.
14565
+
14566
+ Operating style:
14567
+ - Be concise and precise.
14568
+ - Cite memory IDs, task IDs, timestamps, and sender names when available.
14569
+ - Distinguish fact from inference.
14570
+ - Never auto-message people. Never respond in group chats unless explicitly allowed by gateway permissions.
14571
+ - Preserve data sovereignty: do not export or forward private memory unless the founder explicitly asks.`
14461
14572
  }
14462
14573
  };
14463
14574
  CLIENT_COO_TEMPLATE = `---
@@ -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");
@@ -3590,6 +3655,15 @@ function readMachineId() {
3590
3655
  return "";
3591
3656
  }
3592
3657
  }
3658
+ function encryptWithMachineKey(plaintext, machineKey) {
3659
+ const crypto2 = __require("crypto");
3660
+ const iv = crypto2.randomBytes(12);
3661
+ const cipher = crypto2.createCipheriv("aes-256-gcm", machineKey, iv);
3662
+ let encrypted = cipher.update(plaintext, "utf-8", "base64");
3663
+ encrypted += cipher.final("base64");
3664
+ const authTag = cipher.getAuthTag().toString("base64");
3665
+ return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
3666
+ }
3593
3667
  function decryptWithMachineKey(encrypted, machineKey) {
3594
3668
  if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
3595
3669
  try {
@@ -3608,6 +3682,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
3608
3682
  return null;
3609
3683
  }
3610
3684
  }
3685
+ async function writeMachineBoundFileFallback(b64) {
3686
+ const dir = getKeyDir();
3687
+ await mkdir3(dir, { recursive: true });
3688
+ const keyPath = getKeyPath();
3689
+ const machineKey = deriveMachineKey();
3690
+ if (machineKey) {
3691
+ const encrypted = encryptWithMachineKey(b64, machineKey);
3692
+ await writeFile3(keyPath, encrypted + "\n", "utf-8");
3693
+ await chmod2(keyPath, 384);
3694
+ return "encrypted";
3695
+ }
3696
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
3697
+ await chmod2(keyPath, 384);
3698
+ return "plaintext";
3699
+ }
3611
3700
  async function getMasterKey() {
3612
3701
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
3613
3702
  if (nativeValue) {
@@ -3659,6 +3748,20 @@ async function getMasterKey() {
3659
3748
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
3660
3749
  if (migrated) {
3661
3750
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
3751
+ try {
3752
+ await unlink(keyPath);
3753
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
3754
+ } catch {
3755
+ }
3756
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
3757
+ const fallback = await writeMachineBoundFileFallback(b64Value);
3758
+ if (fallback === "encrypted") {
3759
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
3760
+ } else {
3761
+ process.stderr.write(
3762
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
3763
+ );
3764
+ }
3662
3765
  }
3663
3766
  return key;
3664
3767
  } catch (err) {
@@ -3288,6 +3288,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
3288
3288
  return null;
3289
3289
  }
3290
3290
  }
3291
+ async function writeMachineBoundFileFallback(b64) {
3292
+ const dir = getKeyDir();
3293
+ await mkdir3(dir, { recursive: true });
3294
+ const keyPath = getKeyPath();
3295
+ const machineKey = deriveMachineKey();
3296
+ if (machineKey) {
3297
+ const encrypted = encryptWithMachineKey(b64, machineKey);
3298
+ await writeFile3(keyPath, encrypted + "\n", "utf-8");
3299
+ await chmod2(keyPath, 384);
3300
+ return "encrypted";
3301
+ }
3302
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
3303
+ await chmod2(keyPath, 384);
3304
+ return "plaintext";
3305
+ }
3291
3306
  async function getMasterKey() {
3292
3307
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
3293
3308
  if (nativeValue) {
@@ -3339,6 +3354,20 @@ async function getMasterKey() {
3339
3354
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
3340
3355
  if (migrated) {
3341
3356
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
3357
+ try {
3358
+ await unlink(keyPath);
3359
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
3360
+ } catch {
3361
+ }
3362
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
3363
+ const fallback = await writeMachineBoundFileFallback(b64Value);
3364
+ if (fallback === "encrypted") {
3365
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
3366
+ } else {
3367
+ process.stderr.write(
3368
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
3369
+ );
3370
+ }
3342
3371
  }
3343
3372
  return key;
3344
3373
  } catch (err) {
@@ -3362,18 +3391,10 @@ async function setMasterKey(key) {
3362
3391
  } catch {
3363
3392
  }
3364
3393
  }
3365
- const dir = getKeyDir();
3366
- await mkdir3(dir, { recursive: true });
3367
- const keyPath = getKeyPath();
3368
- const machineKey = deriveMachineKey();
3369
- if (machineKey) {
3370
- const encrypted = encryptWithMachineKey(b64, machineKey);
3371
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
3372
- await chmod2(keyPath, 384);
3394
+ const fallback = await writeMachineBoundFileFallback(b64);
3395
+ if (fallback === "encrypted") {
3373
3396
  process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
3374
3397
  } else {
3375
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3376
- await chmod2(keyPath, 384);
3377
3398
  process.stderr.write(
3378
3399
  "[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
3379
3400
  );
@@ -3499,6 +3520,7 @@ var init_memory_write_governor = __esm({
3499
3520
  // src/lib/shard-manager.ts
3500
3521
  var shard_manager_exports = {};
3501
3522
  __export(shard_manager_exports, {
3523
+ auditShardHealth: () => auditShardHealth,
3502
3524
  disposeShards: () => disposeShards,
3503
3525
  ensureShardSchema: () => ensureShardSchema,
3504
3526
  getOpenShardCount: () => getOpenShardCount,
@@ -3565,6 +3587,70 @@ function listShards() {
3565
3587
  if (!existsSync7(SHARDS_DIR)) return [];
3566
3588
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3567
3589
  }
3590
+ async function auditShardHealth(options = {}) {
3591
+ if (!_encryptionKey) {
3592
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
3593
+ }
3594
+ const repair = options.repair === true;
3595
+ const dryRun = options.dryRun === true;
3596
+ const names = listShards();
3597
+ const shards = [];
3598
+ for (const name of names) {
3599
+ const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
3600
+ const stat = statSync2(dbPath);
3601
+ const item = {
3602
+ name,
3603
+ path: dbPath,
3604
+ ok: false,
3605
+ unreadable: false,
3606
+ error: null,
3607
+ size: stat.size,
3608
+ mtime: stat.mtime.toISOString(),
3609
+ memoryCount: null
3610
+ };
3611
+ const client = createClient2({
3612
+ url: `file:${dbPath}`,
3613
+ encryptionKey: _encryptionKey
3614
+ });
3615
+ try {
3616
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
3617
+ const hasMemories = await client.execute(
3618
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
3619
+ );
3620
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
3621
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
3622
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
3623
+ }
3624
+ item.ok = true;
3625
+ } catch (err) {
3626
+ const message = err instanceof Error ? err.message : String(err);
3627
+ item.error = message;
3628
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
3629
+ if (item.unreadable && repair && !dryRun) {
3630
+ client.close();
3631
+ _shards.delete(name);
3632
+ _shardLastAccess.delete(name);
3633
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3634
+ const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3635
+ renameSync3(dbPath, archivedPath);
3636
+ item.archivedPath = archivedPath;
3637
+ }
3638
+ } finally {
3639
+ try {
3640
+ client.close();
3641
+ } catch {
3642
+ }
3643
+ }
3644
+ shards.push(item);
3645
+ }
3646
+ return {
3647
+ total: shards.length,
3648
+ ok: shards.filter((s) => s.ok).length,
3649
+ unreadable: shards.filter((s) => s.unreadable).length,
3650
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
3651
+ shards
3652
+ };
3653
+ }
3568
3654
  async function ensureShardSchema(client) {
3569
3655
  await client.execute("PRAGMA journal_mode = WAL");
3570
3656
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -950,6 +950,31 @@ Audit method:
950
950
  6. Write structured report with PASS/FAIL per item
951
951
 
952
952
  After an audit, fix the findings yourself if you can. Don't hand off when you have the context.`
953
+ },
954
+ teddy: {
955
+ name: "teddy",
956
+ role: "Chief of Staff",
957
+ systemPrompt: `You are teddy, the Chief of Staff and executive assistant. You help the founder recall context, understand what happened, prepare concise briefs, and triage inbound conversations. You report to the COO.
958
+
959
+ Your job is read-first, not action-first:
960
+ - Retrieve memories, decisions, runbooks, and session context on demand
961
+ - Summarize what matters without changing source data
962
+ - Triage inbound conversations and surface likely bugs, requests, and follow-ups
963
+ - Prepare daily briefs and "what changed?" summaries
964
+ - Route recommended actions to the COO instead of taking them yourself
965
+
966
+ Permissions boundary:
967
+ - You are read-only by default.
968
+ - You may use recall_my_memory, ask_team_memory, get_memory_by_id, get_session_context, search_everything, query_conversations, list_tasks, and get_task.
969
+ - You must not create tasks, update tasks, store memories, send WhatsApp messages, mutate CRM/wiki/documents, deploy, or change configuration unless the founder explicitly promotes your permissions.
970
+ - If a requested action requires write access, explain the action and recommend that the COO dispatch it.
971
+
972
+ Operating style:
973
+ - Be concise and precise.
974
+ - Cite memory IDs, task IDs, timestamps, and sender names when available.
975
+ - Distinguish fact from inference.
976
+ - Never auto-message people. Never respond in group chats unless explicitly allowed by gateway permissions.
977
+ - Preserve data sovereignty: do not export or forward private memory unless the founder explicitly asks.`
953
978
  }
954
979
  };
955
980
  CLIENT_COO_TEMPLATE = `---
@@ -138,6 +138,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
138
138
  return null;
139
139
  }
140
140
  }
141
+ async function writeMachineBoundFileFallback(b64) {
142
+ const dir = getKeyDir();
143
+ await mkdir(dir, { recursive: true });
144
+ const keyPath = getKeyPath();
145
+ const machineKey = deriveMachineKey();
146
+ if (machineKey) {
147
+ const encrypted = encryptWithMachineKey(b64, machineKey);
148
+ await writeFile(keyPath, encrypted + "\n", "utf-8");
149
+ await chmod(keyPath, 384);
150
+ return "encrypted";
151
+ }
152
+ await writeFile(keyPath, b64 + "\n", "utf-8");
153
+ await chmod(keyPath, 384);
154
+ return "plaintext";
155
+ }
141
156
  async function getMasterKey() {
142
157
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
143
158
  if (nativeValue) {
@@ -189,6 +204,20 @@ async function getMasterKey() {
189
204
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
190
205
  if (migrated) {
191
206
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
207
+ try {
208
+ await unlink(keyPath);
209
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
210
+ } catch {
211
+ }
212
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
213
+ const fallback = await writeMachineBoundFileFallback(b64Value);
214
+ if (fallback === "encrypted") {
215
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
216
+ } else {
217
+ process.stderr.write(
218
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
219
+ );
220
+ }
192
221
  }
193
222
  return key;
194
223
  } catch (err) {
@@ -212,18 +241,10 @@ async function setMasterKey(key) {
212
241
  } catch {
213
242
  }
214
243
  }
215
- const dir = getKeyDir();
216
- await mkdir(dir, { recursive: true });
217
- const keyPath = getKeyPath();
218
- const machineKey = deriveMachineKey();
219
- if (machineKey) {
220
- const encrypted = encryptWithMachineKey(b64, machineKey);
221
- await writeFile(keyPath, encrypted + "\n", "utf-8");
222
- await chmod(keyPath, 384);
244
+ const fallback = await writeMachineBoundFileFallback(b64);
245
+ if (fallback === "encrypted") {
223
246
  process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
224
247
  } else {
225
- await writeFile(keyPath, b64 + "\n", "utf-8");
226
- await chmod(keyPath, 384);
227
248
  process.stderr.write(
228
249
  "[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
229
250
  );
@@ -6636,6 +6636,15 @@ function readMachineId() {
6636
6636
  return "";
6637
6637
  }
6638
6638
  }
6639
+ function encryptWithMachineKey(plaintext, machineKey) {
6640
+ const crypto7 = __require("crypto");
6641
+ const iv = crypto7.randomBytes(12);
6642
+ const cipher = crypto7.createCipheriv("aes-256-gcm", machineKey, iv);
6643
+ let encrypted = cipher.update(plaintext, "utf-8", "base64");
6644
+ encrypted += cipher.final("base64");
6645
+ const authTag = cipher.getAuthTag().toString("base64");
6646
+ return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
6647
+ }
6639
6648
  function decryptWithMachineKey(encrypted, machineKey) {
6640
6649
  if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
6641
6650
  try {
@@ -6654,6 +6663,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
6654
6663
  return null;
6655
6664
  }
6656
6665
  }
6666
+ async function writeMachineBoundFileFallback(b64) {
6667
+ const dir = getKeyDir();
6668
+ await mkdir4(dir, { recursive: true });
6669
+ const keyPath = getKeyPath();
6670
+ const machineKey = deriveMachineKey();
6671
+ if (machineKey) {
6672
+ const encrypted = encryptWithMachineKey(b64, machineKey);
6673
+ await writeFile5(keyPath, encrypted + "\n", "utf-8");
6674
+ await chmod2(keyPath, 384);
6675
+ return "encrypted";
6676
+ }
6677
+ await writeFile5(keyPath, b64 + "\n", "utf-8");
6678
+ await chmod2(keyPath, 384);
6679
+ return "plaintext";
6680
+ }
6657
6681
  async function getMasterKey() {
6658
6682
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
6659
6683
  if (nativeValue) {
@@ -6705,6 +6729,20 @@ async function getMasterKey() {
6705
6729
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
6706
6730
  if (migrated) {
6707
6731
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
6732
+ try {
6733
+ await unlink(keyPath);
6734
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
6735
+ } catch {
6736
+ }
6737
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
6738
+ const fallback = await writeMachineBoundFileFallback(b64Value);
6739
+ if (fallback === "encrypted") {
6740
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
6741
+ } else {
6742
+ process.stderr.write(
6743
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
6744
+ );
6745
+ }
6708
6746
  }
6709
6747
  return key;
6710
6748
  } catch (err) {
@@ -6922,6 +6960,7 @@ var init_memory_write_governor = __esm({
6922
6960
  // src/lib/shard-manager.ts
6923
6961
  var shard_manager_exports = {};
6924
6962
  __export(shard_manager_exports, {
6963
+ auditShardHealth: () => auditShardHealth,
6925
6964
  disposeShards: () => disposeShards,
6926
6965
  ensureShardSchema: () => ensureShardSchema,
6927
6966
  getOpenShardCount: () => getOpenShardCount,
@@ -6988,6 +7027,70 @@ function listShards() {
6988
7027
  if (!existsSync16(SHARDS_DIR)) return [];
6989
7028
  return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
6990
7029
  }
7030
+ async function auditShardHealth(options = {}) {
7031
+ if (!_encryptionKey) {
7032
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
7033
+ }
7034
+ const repair = options.repair === true;
7035
+ const dryRun = options.dryRun === true;
7036
+ const names = listShards();
7037
+ const shards = [];
7038
+ for (const name of names) {
7039
+ const dbPath = path19.join(SHARDS_DIR, `${name}.db`);
7040
+ const stat = statSync2(dbPath);
7041
+ const item = {
7042
+ name,
7043
+ path: dbPath,
7044
+ ok: false,
7045
+ unreadable: false,
7046
+ error: null,
7047
+ size: stat.size,
7048
+ mtime: stat.mtime.toISOString(),
7049
+ memoryCount: null
7050
+ };
7051
+ const client = createClient2({
7052
+ url: `file:${dbPath}`,
7053
+ encryptionKey: _encryptionKey
7054
+ });
7055
+ try {
7056
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
7057
+ const hasMemories = await client.execute(
7058
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
7059
+ );
7060
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
7061
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
7062
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
7063
+ }
7064
+ item.ok = true;
7065
+ } catch (err) {
7066
+ const message = err instanceof Error ? err.message : String(err);
7067
+ item.error = message;
7068
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
7069
+ if (item.unreadable && repair && !dryRun) {
7070
+ client.close();
7071
+ _shards.delete(name);
7072
+ _shardLastAccess.delete(name);
7073
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
7074
+ const archivedPath = path19.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
7075
+ renameSync4(dbPath, archivedPath);
7076
+ item.archivedPath = archivedPath;
7077
+ }
7078
+ } finally {
7079
+ try {
7080
+ client.close();
7081
+ } catch {
7082
+ }
7083
+ }
7084
+ shards.push(item);
7085
+ }
7086
+ return {
7087
+ total: shards.length,
7088
+ ok: shards.filter((s) => s.ok).length,
7089
+ unreadable: shards.filter((s) => s.unreadable).length,
7090
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
7091
+ shards
7092
+ };
7093
+ }
6991
7094
  async function ensureShardSchema(client) {
6992
7095
  await client.execute("PRAGMA journal_mode = WAL");
6993
7096
  await client.execute("PRAGMA busy_timeout = 30000");