@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
@@ -2927,6 +2927,15 @@ function readMachineId() {
2927
2927
  return "";
2928
2928
  }
2929
2929
  }
2930
+ function encryptWithMachineKey(plaintext, machineKey) {
2931
+ const crypto3 = __require("crypto");
2932
+ const iv = crypto3.randomBytes(12);
2933
+ const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
2934
+ let encrypted = cipher.update(plaintext, "utf-8", "base64");
2935
+ encrypted += cipher.final("base64");
2936
+ const authTag = cipher.getAuthTag().toString("base64");
2937
+ return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
2938
+ }
2930
2939
  function decryptWithMachineKey(encrypted, machineKey) {
2931
2940
  if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
2932
2941
  try {
@@ -2945,6 +2954,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
2945
2954
  return null;
2946
2955
  }
2947
2956
  }
2957
+ async function writeMachineBoundFileFallback(b64) {
2958
+ const dir = getKeyDir();
2959
+ await mkdir3(dir, { recursive: true });
2960
+ const keyPath = getKeyPath();
2961
+ const machineKey = deriveMachineKey();
2962
+ if (machineKey) {
2963
+ const encrypted = encryptWithMachineKey(b64, machineKey);
2964
+ await writeFile3(keyPath, encrypted + "\n", "utf-8");
2965
+ await chmod2(keyPath, 384);
2966
+ return "encrypted";
2967
+ }
2968
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
2969
+ await chmod2(keyPath, 384);
2970
+ return "plaintext";
2971
+ }
2948
2972
  async function getMasterKey() {
2949
2973
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
2950
2974
  if (nativeValue) {
@@ -2996,6 +3020,20 @@ async function getMasterKey() {
2996
3020
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
2997
3021
  if (migrated) {
2998
3022
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
3023
+ try {
3024
+ await unlink(keyPath);
3025
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
3026
+ } catch {
3027
+ }
3028
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
3029
+ const fallback = await writeMachineBoundFileFallback(b64Value);
3030
+ if (fallback === "encrypted") {
3031
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
3032
+ } else {
3033
+ process.stderr.write(
3034
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
3035
+ );
3036
+ }
2999
3037
  }
3000
3038
  return key;
3001
3039
  } catch (err) {
@@ -3268,6 +3306,7 @@ var init_memory_write_governor = __esm({
3268
3306
  // src/lib/shard-manager.ts
3269
3307
  var shard_manager_exports = {};
3270
3308
  __export(shard_manager_exports, {
3309
+ auditShardHealth: () => auditShardHealth,
3271
3310
  disposeShards: () => disposeShards,
3272
3311
  ensureShardSchema: () => ensureShardSchema,
3273
3312
  getOpenShardCount: () => getOpenShardCount,
@@ -3334,6 +3373,70 @@ function listShards() {
3334
3373
  if (!existsSync7(SHARDS_DIR)) return [];
3335
3374
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3336
3375
  }
3376
+ async function auditShardHealth(options = {}) {
3377
+ if (!_encryptionKey) {
3378
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
3379
+ }
3380
+ const repair = options.repair === true;
3381
+ const dryRun = options.dryRun === true;
3382
+ const names = listShards();
3383
+ const shards = [];
3384
+ for (const name of names) {
3385
+ const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
3386
+ const stat = statSync2(dbPath);
3387
+ const item = {
3388
+ name,
3389
+ path: dbPath,
3390
+ ok: false,
3391
+ unreadable: false,
3392
+ error: null,
3393
+ size: stat.size,
3394
+ mtime: stat.mtime.toISOString(),
3395
+ memoryCount: null
3396
+ };
3397
+ const client = createClient2({
3398
+ url: `file:${dbPath}`,
3399
+ encryptionKey: _encryptionKey
3400
+ });
3401
+ try {
3402
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
3403
+ const hasMemories = await client.execute(
3404
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
3405
+ );
3406
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
3407
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
3408
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
3409
+ }
3410
+ item.ok = true;
3411
+ } catch (err) {
3412
+ const message = err instanceof Error ? err.message : String(err);
3413
+ item.error = message;
3414
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
3415
+ if (item.unreadable && repair && !dryRun) {
3416
+ client.close();
3417
+ _shards.delete(name);
3418
+ _shardLastAccess.delete(name);
3419
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3420
+ const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3421
+ renameSync3(dbPath, archivedPath);
3422
+ item.archivedPath = archivedPath;
3423
+ }
3424
+ } finally {
3425
+ try {
3426
+ client.close();
3427
+ } catch {
3428
+ }
3429
+ }
3430
+ shards.push(item);
3431
+ }
3432
+ return {
3433
+ total: shards.length,
3434
+ ok: shards.filter((s) => s.ok).length,
3435
+ unreadable: shards.filter((s) => s.unreadable).length,
3436
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
3437
+ shards
3438
+ };
3439
+ }
3337
3440
  async function ensureShardSchema(client) {
3338
3441
  await client.execute("PRAGMA journal_mode = WAL");
3339
3442
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -2897,6 +2897,15 @@ function readMachineId() {
2897
2897
  return "";
2898
2898
  }
2899
2899
  }
2900
+ function encryptWithMachineKey(plaintext, machineKey) {
2901
+ const crypto3 = __require("crypto");
2902
+ const iv = crypto3.randomBytes(12);
2903
+ const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
2904
+ let encrypted = cipher.update(plaintext, "utf-8", "base64");
2905
+ encrypted += cipher.final("base64");
2906
+ const authTag = cipher.getAuthTag().toString("base64");
2907
+ return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
2908
+ }
2900
2909
  function decryptWithMachineKey(encrypted, machineKey) {
2901
2910
  if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
2902
2911
  try {
@@ -2915,6 +2924,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
2915
2924
  return null;
2916
2925
  }
2917
2926
  }
2927
+ async function writeMachineBoundFileFallback(b64) {
2928
+ const dir = getKeyDir();
2929
+ await mkdir3(dir, { recursive: true });
2930
+ const keyPath = getKeyPath();
2931
+ const machineKey = deriveMachineKey();
2932
+ if (machineKey) {
2933
+ const encrypted = encryptWithMachineKey(b64, machineKey);
2934
+ await writeFile3(keyPath, encrypted + "\n", "utf-8");
2935
+ await chmod2(keyPath, 384);
2936
+ return "encrypted";
2937
+ }
2938
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
2939
+ await chmod2(keyPath, 384);
2940
+ return "plaintext";
2941
+ }
2918
2942
  async function getMasterKey() {
2919
2943
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
2920
2944
  if (nativeValue) {
@@ -2966,6 +2990,20 @@ async function getMasterKey() {
2966
2990
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
2967
2991
  if (migrated) {
2968
2992
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
2993
+ try {
2994
+ await unlink(keyPath);
2995
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
2996
+ } catch {
2997
+ }
2998
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
2999
+ const fallback = await writeMachineBoundFileFallback(b64Value);
3000
+ if (fallback === "encrypted") {
3001
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
3002
+ } else {
3003
+ process.stderr.write(
3004
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
3005
+ );
3006
+ }
2969
3007
  }
2970
3008
  return key;
2971
3009
  } catch (err) {
@@ -3238,6 +3276,7 @@ var init_memory_write_governor = __esm({
3238
3276
  // src/lib/shard-manager.ts
3239
3277
  var shard_manager_exports = {};
3240
3278
  __export(shard_manager_exports, {
3279
+ auditShardHealth: () => auditShardHealth,
3241
3280
  disposeShards: () => disposeShards,
3242
3281
  ensureShardSchema: () => ensureShardSchema,
3243
3282
  getOpenShardCount: () => getOpenShardCount,
@@ -3304,6 +3343,70 @@ function listShards() {
3304
3343
  if (!existsSync7(SHARDS_DIR)) return [];
3305
3344
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3306
3345
  }
3346
+ async function auditShardHealth(options = {}) {
3347
+ if (!_encryptionKey) {
3348
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
3349
+ }
3350
+ const repair = options.repair === true;
3351
+ const dryRun = options.dryRun === true;
3352
+ const names = listShards();
3353
+ const shards = [];
3354
+ for (const name of names) {
3355
+ const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
3356
+ const stat = statSync2(dbPath);
3357
+ const item = {
3358
+ name,
3359
+ path: dbPath,
3360
+ ok: false,
3361
+ unreadable: false,
3362
+ error: null,
3363
+ size: stat.size,
3364
+ mtime: stat.mtime.toISOString(),
3365
+ memoryCount: null
3366
+ };
3367
+ const client = createClient2({
3368
+ url: `file:${dbPath}`,
3369
+ encryptionKey: _encryptionKey
3370
+ });
3371
+ try {
3372
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
3373
+ const hasMemories = await client.execute(
3374
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
3375
+ );
3376
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
3377
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
3378
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
3379
+ }
3380
+ item.ok = true;
3381
+ } catch (err) {
3382
+ const message = err instanceof Error ? err.message : String(err);
3383
+ item.error = message;
3384
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
3385
+ if (item.unreadable && repair && !dryRun) {
3386
+ client.close();
3387
+ _shards.delete(name);
3388
+ _shardLastAccess.delete(name);
3389
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3390
+ const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3391
+ renameSync3(dbPath, archivedPath);
3392
+ item.archivedPath = archivedPath;
3393
+ }
3394
+ } finally {
3395
+ try {
3396
+ client.close();
3397
+ } catch {
3398
+ }
3399
+ }
3400
+ shards.push(item);
3401
+ }
3402
+ return {
3403
+ total: shards.length,
3404
+ ok: shards.filter((s) => s.ok).length,
3405
+ unreadable: shards.filter((s) => s.unreadable).length,
3406
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
3407
+ shards
3408
+ };
3409
+ }
3307
3410
  async function ensureShardSchema(client) {
3308
3411
  await client.execute("PRAGMA journal_mode = WAL");
3309
3412
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -2677,6 +2677,7 @@ var init_database = __esm({
2677
2677
  // src/lib/shard-manager.ts
2678
2678
  var shard_manager_exports = {};
2679
2679
  __export(shard_manager_exports, {
2680
+ auditShardHealth: () => auditShardHealth,
2680
2681
  disposeShards: () => disposeShards,
2681
2682
  ensureShardSchema: () => ensureShardSchema,
2682
2683
  getOpenShardCount: () => getOpenShardCount,
@@ -2743,6 +2744,70 @@ function listShards() {
2743
2744
  if (!existsSync7(SHARDS_DIR)) return [];
2744
2745
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2745
2746
  }
2747
+ async function auditShardHealth(options = {}) {
2748
+ if (!_encryptionKey) {
2749
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
2750
+ }
2751
+ const repair = options.repair === true;
2752
+ const dryRun = options.dryRun === true;
2753
+ const names = listShards();
2754
+ const shards = [];
2755
+ for (const name of names) {
2756
+ const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
2757
+ const stat = statSync2(dbPath);
2758
+ const item = {
2759
+ name,
2760
+ path: dbPath,
2761
+ ok: false,
2762
+ unreadable: false,
2763
+ error: null,
2764
+ size: stat.size,
2765
+ mtime: stat.mtime.toISOString(),
2766
+ memoryCount: null
2767
+ };
2768
+ const client = createClient2({
2769
+ url: `file:${dbPath}`,
2770
+ encryptionKey: _encryptionKey
2771
+ });
2772
+ try {
2773
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
2774
+ const hasMemories = await client.execute(
2775
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
2776
+ );
2777
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
2778
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
2779
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
2780
+ }
2781
+ item.ok = true;
2782
+ } catch (err) {
2783
+ const message = err instanceof Error ? err.message : String(err);
2784
+ item.error = message;
2785
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
2786
+ if (item.unreadable && repair && !dryRun) {
2787
+ client.close();
2788
+ _shards.delete(name);
2789
+ _shardLastAccess.delete(name);
2790
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2791
+ const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
2792
+ renameSync3(dbPath, archivedPath);
2793
+ item.archivedPath = archivedPath;
2794
+ }
2795
+ } finally {
2796
+ try {
2797
+ client.close();
2798
+ } catch {
2799
+ }
2800
+ }
2801
+ shards.push(item);
2802
+ }
2803
+ return {
2804
+ total: shards.length,
2805
+ ok: shards.filter((s) => s.ok).length,
2806
+ unreadable: shards.filter((s) => s.unreadable).length,
2807
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
2808
+ shards
2809
+ };
2810
+ }
2746
2811
  async function ensureShardSchema(client) {
2747
2812
  await client.execute("PRAGMA journal_mode = WAL");
2748
2813
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -3647,6 +3712,15 @@ function readMachineId() {
3647
3712
  return "";
3648
3713
  }
3649
3714
  }
3715
+ function encryptWithMachineKey(plaintext, machineKey) {
3716
+ const crypto3 = __require("crypto");
3717
+ const iv = crypto3.randomBytes(12);
3718
+ const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
3719
+ let encrypted = cipher.update(plaintext, "utf-8", "base64");
3720
+ encrypted += cipher.final("base64");
3721
+ const authTag = cipher.getAuthTag().toString("base64");
3722
+ return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
3723
+ }
3650
3724
  function decryptWithMachineKey(encrypted, machineKey) {
3651
3725
  if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
3652
3726
  try {
@@ -3665,6 +3739,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
3665
3739
  return null;
3666
3740
  }
3667
3741
  }
3742
+ async function writeMachineBoundFileFallback(b64) {
3743
+ const dir = getKeyDir();
3744
+ await mkdir3(dir, { recursive: true });
3745
+ const keyPath = getKeyPath();
3746
+ const machineKey = deriveMachineKey();
3747
+ if (machineKey) {
3748
+ const encrypted = encryptWithMachineKey(b64, machineKey);
3749
+ await writeFile3(keyPath, encrypted + "\n", "utf-8");
3750
+ await chmod2(keyPath, 384);
3751
+ return "encrypted";
3752
+ }
3753
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
3754
+ await chmod2(keyPath, 384);
3755
+ return "plaintext";
3756
+ }
3668
3757
  async function getMasterKey() {
3669
3758
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
3670
3759
  if (nativeValue) {
@@ -3716,6 +3805,20 @@ async function getMasterKey() {
3716
3805
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
3717
3806
  if (migrated) {
3718
3807
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
3808
+ try {
3809
+ await unlink(keyPath);
3810
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
3811
+ } catch {
3812
+ }
3813
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
3814
+ const fallback = await writeMachineBoundFileFallback(b64Value);
3815
+ if (fallback === "encrypted") {
3816
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
3817
+ } else {
3818
+ process.stderr.write(
3819
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
3820
+ );
3821
+ }
3719
3822
  }
3720
3823
  return key;
3721
3824
  } catch (err) {
@@ -170,6 +170,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
170
170
  return null;
171
171
  }
172
172
  }
173
+ async function writeMachineBoundFileFallback(b64) {
174
+ const dir = getKeyDir();
175
+ await mkdir(dir, { recursive: true });
176
+ const keyPath = getKeyPath();
177
+ const machineKey = deriveMachineKey();
178
+ if (machineKey) {
179
+ const encrypted = encryptWithMachineKey(b64, machineKey);
180
+ await writeFile(keyPath, encrypted + "\n", "utf-8");
181
+ await chmod(keyPath, 384);
182
+ return "encrypted";
183
+ }
184
+ await writeFile(keyPath, b64 + "\n", "utf-8");
185
+ await chmod(keyPath, 384);
186
+ return "plaintext";
187
+ }
173
188
  async function getMasterKey() {
174
189
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
175
190
  if (nativeValue) {
@@ -221,6 +236,20 @@ async function getMasterKey() {
221
236
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
222
237
  if (migrated) {
223
238
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
239
+ try {
240
+ await unlink(keyPath);
241
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
242
+ } catch {
243
+ }
244
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
245
+ const fallback = await writeMachineBoundFileFallback(b64Value);
246
+ if (fallback === "encrypted") {
247
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
248
+ } else {
249
+ process.stderr.write(
250
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
251
+ );
252
+ }
224
253
  }
225
254
  return key;
226
255
  } catch (err) {
@@ -244,18 +273,10 @@ async function setMasterKey(key) {
244
273
  } catch {
245
274
  }
246
275
  }
247
- const dir = getKeyDir();
248
- await mkdir(dir, { recursive: true });
249
- const keyPath = getKeyPath();
250
- const machineKey = deriveMachineKey();
251
- if (machineKey) {
252
- const encrypted = encryptWithMachineKey(b64, machineKey);
253
- await writeFile(keyPath, encrypted + "\n", "utf-8");
254
- await chmod(keyPath, 384);
276
+ const fallback = await writeMachineBoundFileFallback(b64);
277
+ if (fallback === "encrypted") {
255
278
  process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
256
279
  } else {
257
- await writeFile(keyPath, b64 + "\n", "utf-8");
258
- await chmod(keyPath, 384);
259
280
  process.stderr.write(
260
281
  "[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
261
282
  );
@@ -2845,6 +2845,31 @@ Audit method:
2845
2845
  6. Write structured report with PASS/FAIL per item
2846
2846
 
2847
2847
  After an audit, fix the findings yourself if you can. Don't hand off when you have the context.`
2848
+ },
2849
+ teddy: {
2850
+ name: "teddy",
2851
+ role: "Chief of Staff",
2852
+ 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.
2853
+
2854
+ Your job is read-first, not action-first:
2855
+ - Retrieve memories, decisions, runbooks, and session context on demand
2856
+ - Summarize what matters without changing source data
2857
+ - Triage inbound conversations and surface likely bugs, requests, and follow-ups
2858
+ - Prepare daily briefs and "what changed?" summaries
2859
+ - Route recommended actions to the COO instead of taking them yourself
2860
+
2861
+ Permissions boundary:
2862
+ - You are read-only by default.
2863
+ - 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.
2864
+ - 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.
2865
+ - If a requested action requires write access, explain the action and recommend that the COO dispatch it.
2866
+
2867
+ Operating style:
2868
+ - Be concise and precise.
2869
+ - Cite memory IDs, task IDs, timestamps, and sender names when available.
2870
+ - Distinguish fact from inference.
2871
+ - Never auto-message people. Never respond in group chats unless explicitly allowed by gateway permissions.
2872
+ - Preserve data sovereignty: do not export or forward private memory unless the founder explicitly asks.`
2848
2873
  }
2849
2874
  };
2850
2875
  function buildCustomEmployeePrompt(name, role) {
@@ -3316,6 +3316,15 @@ function readMachineId() {
3316
3316
  return "";
3317
3317
  }
3318
3318
  }
3319
+ function encryptWithMachineKey(plaintext, machineKey) {
3320
+ const crypto3 = __require("crypto");
3321
+ const iv = crypto3.randomBytes(12);
3322
+ const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
3323
+ let encrypted = cipher.update(plaintext, "utf-8", "base64");
3324
+ encrypted += cipher.final("base64");
3325
+ const authTag = cipher.getAuthTag().toString("base64");
3326
+ return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
3327
+ }
3319
3328
  function decryptWithMachineKey(encrypted, machineKey) {
3320
3329
  if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
3321
3330
  try {
@@ -3334,6 +3343,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
3334
3343
  return null;
3335
3344
  }
3336
3345
  }
3346
+ async function writeMachineBoundFileFallback(b64) {
3347
+ const dir = getKeyDir();
3348
+ await mkdir3(dir, { recursive: true });
3349
+ const keyPath = getKeyPath();
3350
+ const machineKey = deriveMachineKey();
3351
+ if (machineKey) {
3352
+ const encrypted = encryptWithMachineKey(b64, machineKey);
3353
+ await writeFile3(keyPath, encrypted + "\n", "utf-8");
3354
+ await chmod2(keyPath, 384);
3355
+ return "encrypted";
3356
+ }
3357
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
3358
+ await chmod2(keyPath, 384);
3359
+ return "plaintext";
3360
+ }
3337
3361
  async function getMasterKey() {
3338
3362
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
3339
3363
  if (nativeValue) {
@@ -3385,6 +3409,20 @@ async function getMasterKey() {
3385
3409
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
3386
3410
  if (migrated) {
3387
3411
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
3412
+ try {
3413
+ await unlink(keyPath);
3414
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
3415
+ } catch {
3416
+ }
3417
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
3418
+ const fallback = await writeMachineBoundFileFallback(b64Value);
3419
+ if (fallback === "encrypted") {
3420
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
3421
+ } else {
3422
+ process.stderr.write(
3423
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
3424
+ );
3425
+ }
3388
3426
  }
3389
3427
  return key;
3390
3428
  } catch (err) {
@@ -3657,6 +3695,7 @@ var init_memory_write_governor = __esm({
3657
3695
  // src/lib/shard-manager.ts
3658
3696
  var shard_manager_exports = {};
3659
3697
  __export(shard_manager_exports, {
3698
+ auditShardHealth: () => auditShardHealth,
3660
3699
  disposeShards: () => disposeShards,
3661
3700
  ensureShardSchema: () => ensureShardSchema,
3662
3701
  getOpenShardCount: () => getOpenShardCount,
@@ -3723,6 +3762,70 @@ function listShards() {
3723
3762
  if (!existsSync12(SHARDS_DIR)) return [];
3724
3763
  return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3725
3764
  }
3765
+ async function auditShardHealth(options = {}) {
3766
+ if (!_encryptionKey) {
3767
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
3768
+ }
3769
+ const repair = options.repair === true;
3770
+ const dryRun = options.dryRun === true;
3771
+ const names = listShards();
3772
+ const shards = [];
3773
+ for (const name of names) {
3774
+ const dbPath = path13.join(SHARDS_DIR, `${name}.db`);
3775
+ const stat = statSync2(dbPath);
3776
+ const item = {
3777
+ name,
3778
+ path: dbPath,
3779
+ ok: false,
3780
+ unreadable: false,
3781
+ error: null,
3782
+ size: stat.size,
3783
+ mtime: stat.mtime.toISOString(),
3784
+ memoryCount: null
3785
+ };
3786
+ const client = createClient2({
3787
+ url: `file:${dbPath}`,
3788
+ encryptionKey: _encryptionKey
3789
+ });
3790
+ try {
3791
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
3792
+ const hasMemories = await client.execute(
3793
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
3794
+ );
3795
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
3796
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
3797
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
3798
+ }
3799
+ item.ok = true;
3800
+ } catch (err) {
3801
+ const message = err instanceof Error ? err.message : String(err);
3802
+ item.error = message;
3803
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
3804
+ if (item.unreadable && repair && !dryRun) {
3805
+ client.close();
3806
+ _shards.delete(name);
3807
+ _shardLastAccess.delete(name);
3808
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3809
+ const archivedPath = path13.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3810
+ renameSync4(dbPath, archivedPath);
3811
+ item.archivedPath = archivedPath;
3812
+ }
3813
+ } finally {
3814
+ try {
3815
+ client.close();
3816
+ } catch {
3817
+ }
3818
+ }
3819
+ shards.push(item);
3820
+ }
3821
+ return {
3822
+ total: shards.length,
3823
+ ok: shards.filter((s) => s.ok).length,
3824
+ unreadable: shards.filter((s) => s.unreadable).length,
3825
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
3826
+ shards
3827
+ };
3828
+ }
3726
3829
  async function ensureShardSchema(client) {
3727
3830
  await client.execute("PRAGMA journal_mode = WAL");
3728
3831
  await client.execute("PRAGMA busy_timeout = 30000");