@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/index.js CHANGED
@@ -6926,6 +6926,15 @@ function readMachineId() {
6926
6926
  return "";
6927
6927
  }
6928
6928
  }
6929
+ function encryptWithMachineKey(plaintext, machineKey) {
6930
+ const crypto11 = __require("crypto");
6931
+ const iv = crypto11.randomBytes(12);
6932
+ const cipher = crypto11.createCipheriv("aes-256-gcm", machineKey, iv);
6933
+ let encrypted = cipher.update(plaintext, "utf-8", "base64");
6934
+ encrypted += cipher.final("base64");
6935
+ const authTag = cipher.getAuthTag().toString("base64");
6936
+ return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
6937
+ }
6929
6938
  function decryptWithMachineKey(encrypted, machineKey) {
6930
6939
  if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
6931
6940
  try {
@@ -6944,6 +6953,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
6944
6953
  return null;
6945
6954
  }
6946
6955
  }
6956
+ async function writeMachineBoundFileFallback(b64) {
6957
+ const dir = getKeyDir();
6958
+ await mkdir4(dir, { recursive: true });
6959
+ const keyPath = getKeyPath();
6960
+ const machineKey = deriveMachineKey();
6961
+ if (machineKey) {
6962
+ const encrypted = encryptWithMachineKey(b64, machineKey);
6963
+ await writeFile5(keyPath, encrypted + "\n", "utf-8");
6964
+ await chmod2(keyPath, 384);
6965
+ return "encrypted";
6966
+ }
6967
+ await writeFile5(keyPath, b64 + "\n", "utf-8");
6968
+ await chmod2(keyPath, 384);
6969
+ return "plaintext";
6970
+ }
6947
6971
  async function getMasterKey() {
6948
6972
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
6949
6973
  if (nativeValue) {
@@ -6995,6 +7019,20 @@ async function getMasterKey() {
6995
7019
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
6996
7020
  if (migrated) {
6997
7021
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
7022
+ try {
7023
+ await unlink(keyPath);
7024
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
7025
+ } catch {
7026
+ }
7027
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
7028
+ const fallback = await writeMachineBoundFileFallback(b64Value);
7029
+ if (fallback === "encrypted") {
7030
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
7031
+ } else {
7032
+ process.stderr.write(
7033
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
7034
+ );
7035
+ }
6998
7036
  }
6999
7037
  return key;
7000
7038
  } catch (err) {
@@ -7212,6 +7250,7 @@ var init_memory_write_governor = __esm({
7212
7250
  // src/lib/shard-manager.ts
7213
7251
  var shard_manager_exports = {};
7214
7252
  __export(shard_manager_exports, {
7253
+ auditShardHealth: () => auditShardHealth,
7215
7254
  disposeShards: () => disposeShards,
7216
7255
  ensureShardSchema: () => ensureShardSchema,
7217
7256
  getOpenShardCount: () => getOpenShardCount,
@@ -7278,6 +7317,70 @@ function listShards() {
7278
7317
  if (!existsSync16(SHARDS_DIR)) return [];
7279
7318
  return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
7280
7319
  }
7320
+ async function auditShardHealth(options = {}) {
7321
+ if (!_encryptionKey) {
7322
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
7323
+ }
7324
+ const repair = options.repair === true;
7325
+ const dryRun = options.dryRun === true;
7326
+ const names = listShards();
7327
+ const shards = [];
7328
+ for (const name of names) {
7329
+ const dbPath = path20.join(SHARDS_DIR, `${name}.db`);
7330
+ const stat = statSync2(dbPath);
7331
+ const item = {
7332
+ name,
7333
+ path: dbPath,
7334
+ ok: false,
7335
+ unreadable: false,
7336
+ error: null,
7337
+ size: stat.size,
7338
+ mtime: stat.mtime.toISOString(),
7339
+ memoryCount: null
7340
+ };
7341
+ const client = createClient2({
7342
+ url: `file:${dbPath}`,
7343
+ encryptionKey: _encryptionKey
7344
+ });
7345
+ try {
7346
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
7347
+ const hasMemories = await client.execute(
7348
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
7349
+ );
7350
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
7351
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
7352
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
7353
+ }
7354
+ item.ok = true;
7355
+ } catch (err) {
7356
+ const message = err instanceof Error ? err.message : String(err);
7357
+ item.error = message;
7358
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
7359
+ if (item.unreadable && repair && !dryRun) {
7360
+ client.close();
7361
+ _shards.delete(name);
7362
+ _shardLastAccess.delete(name);
7363
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
7364
+ const archivedPath = path20.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
7365
+ renameSync4(dbPath, archivedPath);
7366
+ item.archivedPath = archivedPath;
7367
+ }
7368
+ } finally {
7369
+ try {
7370
+ client.close();
7371
+ } catch {
7372
+ }
7373
+ }
7374
+ shards.push(item);
7375
+ }
7376
+ return {
7377
+ total: shards.length,
7378
+ ok: shards.filter((s) => s.ok).length,
7379
+ unreadable: shards.filter((s) => s.unreadable).length,
7380
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
7381
+ shards
7382
+ };
7383
+ }
7281
7384
  async function ensureShardSchema(client) {
7282
7385
  await client.execute("PRAGMA journal_mode = WAL");
7283
7386
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -2915,6 +2915,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
2915
2915
  return null;
2916
2916
  }
2917
2917
  }
2918
+ async function writeMachineBoundFileFallback(b64) {
2919
+ const dir = getKeyDir();
2920
+ await mkdir3(dir, { recursive: true });
2921
+ const keyPath = getKeyPath();
2922
+ const machineKey = deriveMachineKey();
2923
+ if (machineKey) {
2924
+ const encrypted = encryptWithMachineKey(b64, machineKey);
2925
+ await writeFile3(keyPath, encrypted + "\n", "utf-8");
2926
+ await chmod2(keyPath, 384);
2927
+ return "encrypted";
2928
+ }
2929
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
2930
+ await chmod2(keyPath, 384);
2931
+ return "plaintext";
2932
+ }
2918
2933
  async function getMasterKey() {
2919
2934
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
2920
2935
  if (nativeValue) {
@@ -2966,6 +2981,20 @@ async function getMasterKey() {
2966
2981
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
2967
2982
  if (migrated) {
2968
2983
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
2984
+ try {
2985
+ await unlink(keyPath);
2986
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
2987
+ } catch {
2988
+ }
2989
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
2990
+ const fallback = await writeMachineBoundFileFallback(b64Value);
2991
+ if (fallback === "encrypted") {
2992
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
2993
+ } else {
2994
+ process.stderr.write(
2995
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
2996
+ );
2997
+ }
2969
2998
  }
2970
2999
  return key;
2971
3000
  } catch (err) {
@@ -2989,18 +3018,10 @@ async function setMasterKey(key) {
2989
3018
  } catch {
2990
3019
  }
2991
3020
  }
2992
- const dir = getKeyDir();
2993
- await mkdir3(dir, { recursive: true });
2994
- const keyPath = getKeyPath();
2995
- const machineKey = deriveMachineKey();
2996
- if (machineKey) {
2997
- const encrypted = encryptWithMachineKey(b64, machineKey);
2998
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
2999
- await chmod2(keyPath, 384);
3021
+ const fallback = await writeMachineBoundFileFallback(b64);
3022
+ if (fallback === "encrypted") {
3000
3023
  process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
3001
3024
  } else {
3002
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3003
- await chmod2(keyPath, 384);
3004
3025
  process.stderr.write(
3005
3026
  "[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
3006
3027
  );
@@ -792,6 +792,31 @@ Audit method:
792
792
  6. Write structured report with PASS/FAIL per item
793
793
 
794
794
  After an audit, fix the findings yourself if you can. Don't hand off when you have the context.`
795
+ },
796
+ teddy: {
797
+ name: "teddy",
798
+ role: "Chief of Staff",
799
+ 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.
800
+
801
+ Your job is read-first, not action-first:
802
+ - Retrieve memories, decisions, runbooks, and session context on demand
803
+ - Summarize what matters without changing source data
804
+ - Triage inbound conversations and surface likely bugs, requests, and follow-ups
805
+ - Prepare daily briefs and "what changed?" summaries
806
+ - Route recommended actions to the COO instead of taking them yourself
807
+
808
+ Permissions boundary:
809
+ - You are read-only by default.
810
+ - 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.
811
+ - 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.
812
+ - If a requested action requires write access, explain the action and recommend that the COO dispatch it.
813
+
814
+ Operating style:
815
+ - Be concise and precise.
816
+ - Cite memory IDs, task IDs, timestamps, and sender names when available.
817
+ - Distinguish fact from inference.
818
+ - Never auto-message people. Never respond in group chats unless explicitly allowed by gateway permissions.
819
+ - Preserve data sovereignty: do not export or forward private memory unless the founder explicitly asks.`
795
820
  }
796
821
  };
797
822
  function buildCustomEmployeePrompt(name, role) {
@@ -3635,6 +3635,7 @@ var init_projection_worker = __esm({
3635
3635
  // src/lib/shard-manager.ts
3636
3636
  var shard_manager_exports = {};
3637
3637
  __export(shard_manager_exports, {
3638
+ auditShardHealth: () => auditShardHealth,
3638
3639
  disposeShards: () => disposeShards,
3639
3640
  ensureShardSchema: () => ensureShardSchema,
3640
3641
  getOpenShardCount: () => getOpenShardCount,
@@ -3701,6 +3702,70 @@ function listShards() {
3701
3702
  if (!existsSync9(SHARDS_DIR)) return [];
3702
3703
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3703
3704
  }
3705
+ async function auditShardHealth(options = {}) {
3706
+ if (!_encryptionKey) {
3707
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
3708
+ }
3709
+ const repair = options.repair === true;
3710
+ const dryRun = options.dryRun === true;
3711
+ const names = listShards();
3712
+ const shards = [];
3713
+ for (const name of names) {
3714
+ const dbPath = path9.join(SHARDS_DIR, `${name}.db`);
3715
+ const stat = statSync2(dbPath);
3716
+ const item = {
3717
+ name,
3718
+ path: dbPath,
3719
+ ok: false,
3720
+ unreadable: false,
3721
+ error: null,
3722
+ size: stat.size,
3723
+ mtime: stat.mtime.toISOString(),
3724
+ memoryCount: null
3725
+ };
3726
+ const client = createClient2({
3727
+ url: `file:${dbPath}`,
3728
+ encryptionKey: _encryptionKey
3729
+ });
3730
+ try {
3731
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
3732
+ const hasMemories = await client.execute(
3733
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
3734
+ );
3735
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
3736
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
3737
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
3738
+ }
3739
+ item.ok = true;
3740
+ } catch (err) {
3741
+ const message = err instanceof Error ? err.message : String(err);
3742
+ item.error = message;
3743
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
3744
+ if (item.unreadable && repair && !dryRun) {
3745
+ client.close();
3746
+ _shards.delete(name);
3747
+ _shardLastAccess.delete(name);
3748
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3749
+ const archivedPath = path9.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3750
+ renameSync3(dbPath, archivedPath);
3751
+ item.archivedPath = archivedPath;
3752
+ }
3753
+ } finally {
3754
+ try {
3755
+ client.close();
3756
+ } catch {
3757
+ }
3758
+ }
3759
+ shards.push(item);
3760
+ }
3761
+ return {
3762
+ total: shards.length,
3763
+ ok: shards.filter((s) => s.ok).length,
3764
+ unreadable: shards.filter((s) => s.unreadable).length,
3765
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
3766
+ shards
3767
+ };
3768
+ }
3704
3769
  async function ensureShardSchema(client) {
3705
3770
  await client.execute("PRAGMA journal_mode = WAL");
3706
3771
  await client.execute("PRAGMA busy_timeout = 30000");
@@ -4137,6 +4202,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
4137
4202
  return null;
4138
4203
  }
4139
4204
  }
4205
+ async function writeMachineBoundFileFallback(b64) {
4206
+ const dir = getKeyDir();
4207
+ await mkdir3(dir, { recursive: true });
4208
+ const keyPath = getKeyPath();
4209
+ const machineKey = deriveMachineKey();
4210
+ if (machineKey) {
4211
+ const encrypted = encryptWithMachineKey(b64, machineKey);
4212
+ await writeFile3(keyPath, encrypted + "\n", "utf-8");
4213
+ await chmod2(keyPath, 384);
4214
+ return "encrypted";
4215
+ }
4216
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
4217
+ await chmod2(keyPath, 384);
4218
+ return "plaintext";
4219
+ }
4140
4220
  async function getMasterKey() {
4141
4221
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
4142
4222
  if (nativeValue) {
@@ -4188,6 +4268,20 @@ async function getMasterKey() {
4188
4268
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
4189
4269
  if (migrated) {
4190
4270
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
4271
+ try {
4272
+ await unlink(keyPath);
4273
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
4274
+ } catch {
4275
+ }
4276
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
4277
+ const fallback = await writeMachineBoundFileFallback(b64Value);
4278
+ if (fallback === "encrypted") {
4279
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
4280
+ } else {
4281
+ process.stderr.write(
4282
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
4283
+ );
4284
+ }
4191
4285
  }
4192
4286
  return key;
4193
4287
  } catch (err) {
@@ -4211,18 +4305,10 @@ async function setMasterKey(key) {
4211
4305
  } catch {
4212
4306
  }
4213
4307
  }
4214
- const dir = getKeyDir();
4215
- await mkdir3(dir, { recursive: true });
4216
- const keyPath = getKeyPath();
4217
- const machineKey = deriveMachineKey();
4218
- if (machineKey) {
4219
- const encrypted = encryptWithMachineKey(b64, machineKey);
4220
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
4221
- await chmod2(keyPath, 384);
4308
+ const fallback = await writeMachineBoundFileFallback(b64);
4309
+ if (fallback === "encrypted") {
4222
4310
  process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
4223
4311
  } else {
4224
- await writeFile3(keyPath, b64 + "\n", "utf-8");
4225
- await chmod2(keyPath, 384);
4226
4312
  process.stderr.write(
4227
4313
  "[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
4228
4314
  );
@@ -20841,15 +20927,31 @@ function auditHookHealth() {
20841
20927
  const topPatterns = [...patternCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => ({ pattern, count }));
20842
20928
  return { logExists: true, totalLines, errorsLastHour, topPatterns };
20843
20929
  }
20930
+ async function auditShards() {
20931
+ try {
20932
+ const { auditShardHealth: auditShardHealth2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
20933
+ const report = await auditShardHealth2();
20934
+ return {
20935
+ total: report.total,
20936
+ ok: report.ok,
20937
+ unreadable: report.unreadable,
20938
+ archived: report.archived,
20939
+ unreadableNames: report.shards.filter((s) => s.unreadable).map((s) => s.name)
20940
+ };
20941
+ } catch {
20942
+ return { total: 0, ok: 0, unreadable: 0, archived: 0, unreadableNames: [] };
20943
+ }
20944
+ }
20844
20945
  async function runAudit(client, flags) {
20845
20946
  const runConflicts = flags.conflicts || process.env.EXE_AUDIT_CONFLICTS === "1";
20846
- const [stats, nullVectors, duplicates, bloated, fts, orphanedProjects] = await Promise.all([
20947
+ const [stats, nullVectors, duplicates, bloated, fts, orphanedProjects, shards] = await Promise.all([
20847
20948
  auditStats(client, flags),
20848
20949
  auditNullVectors(client, flags),
20849
20950
  auditDuplicates(client, flags),
20850
20951
  auditBloated(client, flags),
20851
20952
  auditFts(client),
20852
- auditOrphanedProjects(client)
20953
+ auditOrphanedProjects(client),
20954
+ auditShards()
20853
20955
  ]);
20854
20956
  let conflicts;
20855
20957
  if (runConflicts) {
@@ -20869,7 +20971,7 @@ async function runAudit(client, flags) {
20869
20971
  }
20870
20972
  const duplicateCount = duplicates.reduce((sum, d) => sum + d.delete_ids.length, 0);
20871
20973
  const hookHealth = auditHookHealth();
20872
- return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth };
20974
+ return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth, shards };
20873
20975
  }
20874
20976
  function indicator(value, warn) {
20875
20977
  if (value === 0) return "\u{1F7E2}";
@@ -20948,6 +21050,15 @@ function formatReport(report, flags) {
20948
21050
  lines.push(` ${p.count}x: ${p.pattern}`);
20949
21051
  }
20950
21052
  }
21053
+ const sh = report.shards;
21054
+ if (sh.total > 0) {
21055
+ if (sh.unreadable === 0) {
21056
+ lines.push(`\u{1F7E2} Shards: ${fmtNum(sh.ok)} / ${fmtNum(sh.total)} readable`);
21057
+ } else {
21058
+ const names = sh.unreadableNames.slice(0, 5).join(", ");
21059
+ lines.push(`\u{1F534} Shards: ${fmtNum(sh.unreadable)} unreadable (${names}${sh.unreadableNames.length > 5 ? ", ..." : ""})`);
21060
+ }
21061
+ }
20951
21062
  lines.push("");
20952
21063
  if (flags.verbose) {
20953
21064
  if (report.duplicates.length > 0) {
@@ -20995,6 +21106,9 @@ function formatReport(report, flags) {
20995
21106
  if (!report.fts.inSync) {
20996
21107
  recs.push("Run --fix to rebuild FTS index");
20997
21108
  }
21109
+ if (report.shards.unreadable > 0) {
21110
+ recs.push(`Run --fix to archive ${fmtNum(report.shards.unreadable)} unreadable shard(s); global DB remains authoritative`);
21111
+ }
20998
21112
  if (report.orphanedProjects.length > 0) {
20999
21113
  const names = report.orphanedProjects.map((o) => `"${o.project_name}"`).join(", ");
21000
21114
  recs.push(`Consider /exe-forget for orphaned project${report.orphanedProjects.length > 1 ? "s" : ""} ${names}`);
@@ -21100,6 +21214,17 @@ async function fixBloated(client, bloated, dryRun) {
21100
21214
  }
21101
21215
  return chunksCreated;
21102
21216
  }
21217
+ async function fixShards(dryRun) {
21218
+ const { auditShardHealth: auditShardHealth2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
21219
+ const report = await auditShardHealth2({ repair: true, dryRun });
21220
+ return {
21221
+ total: report.total,
21222
+ ok: report.ok,
21223
+ unreadable: report.unreadable,
21224
+ archived: report.archived,
21225
+ unreadableNames: report.shards.filter((s) => s.unreadable).map((s) => s.name)
21226
+ };
21227
+ }
21103
21228
  function splitAtSentences(text3, maxChunkSize) {
21104
21229
  if (text3.length <= maxChunkSize) return [text3];
21105
21230
  const chunks = [];
@@ -21175,6 +21300,11 @@ ${mode} Applying repairs...
21175
21300
  console.log(" Done.");
21176
21301
  }
21177
21302
  }
21303
+ if (report.shards.unreadable > 0) {
21304
+ console.log(`${mode} Archiving ${fmtNum(report.shards.unreadable)} unreadable shard(s)...`);
21305
+ const fixed = await fixShards(flags.dryRun);
21306
+ console.log(` ${flags.dryRun ? "Would archive" : "Archived"} ${fmtNum(flags.dryRun ? fixed.unreadable : fixed.archived)} shard(s).`);
21307
+ }
21178
21308
  console.log(`
21179
21309
  ${mode} Complete.`);
21180
21310
  }
@@ -27510,16 +27640,23 @@ var init_support_inbox = __esm({
27510
27640
  });
27511
27641
 
27512
27642
  // src/mcp/tool-gates.ts
27643
+ function isChiefOfStaffRole(role) {
27644
+ const normalized = (role ?? "").trim().toLowerCase();
27645
+ return normalized === "chief of staff" || normalized === "executive assistant";
27646
+ }
27513
27647
  function isToolAllowed(registerFnName) {
27514
27648
  const role = process.env.AGENT_ROLE;
27515
27649
  if (!role) return true;
27650
+ if (isChiefOfStaffRole(role)) {
27651
+ return CHIEF_OF_STAFF_READ_TOOLS.has(registerFnName);
27652
+ }
27516
27653
  const category = TOOL_CATEGORIES[registerFnName];
27517
27654
  if (!category) return true;
27518
27655
  const allowedRoles = TOOL_GATES[category];
27519
27656
  if (!allowedRoles || allowedRoles.length === 0) return true;
27520
27657
  return allowedRoles.includes(role);
27521
27658
  }
27522
- var TOOL_GATES, TOOL_CATEGORIES;
27659
+ var TOOL_GATES, TOOL_CATEGORIES, CHIEF_OF_STAFF_READ_TOOLS;
27523
27660
  var init_tool_gates = __esm({
27524
27661
  "src/mcp/tool-gates.ts"() {
27525
27662
  "use strict";
@@ -27661,6 +27798,20 @@ var init_tool_gates = __esm({
27661
27798
  registerListGlobalProcedures: "orchestration",
27662
27799
  registerDeactivateGlobalProcedure: "orchestration"
27663
27800
  };
27801
+ CHIEF_OF_STAFF_READ_TOOLS = /* @__PURE__ */ new Set([
27802
+ "registerRecallMyMemory",
27803
+ "registerAskTeamMemory",
27804
+ "registerGetMemoryById",
27805
+ "registerGetSessionContext",
27806
+ "registerSearchEverything",
27807
+ "registerGetDecision",
27808
+ "registerGetIdentity",
27809
+ "registerListTasks",
27810
+ "registerGetTask",
27811
+ "registerListReminders",
27812
+ "registerQueryConversations",
27813
+ "registerQueryCompanyBrain"
27814
+ ]);
27664
27815
  }
27665
27816
  });
27666
27817
 
@@ -2885,6 +2885,15 @@ function readMachineId() {
2885
2885
  return "";
2886
2886
  }
2887
2887
  }
2888
+ function encryptWithMachineKey(plaintext, machineKey) {
2889
+ const crypto3 = __require("crypto");
2890
+ const iv = crypto3.randomBytes(12);
2891
+ const cipher = crypto3.createCipheriv("aes-256-gcm", machineKey, iv);
2892
+ let encrypted = cipher.update(plaintext, "utf-8", "base64");
2893
+ encrypted += cipher.final("base64");
2894
+ const authTag = cipher.getAuthTag().toString("base64");
2895
+ return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
2896
+ }
2888
2897
  function decryptWithMachineKey(encrypted, machineKey) {
2889
2898
  if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
2890
2899
  try {
@@ -2903,6 +2912,21 @@ function decryptWithMachineKey(encrypted, machineKey) {
2903
2912
  return null;
2904
2913
  }
2905
2914
  }
2915
+ async function writeMachineBoundFileFallback(b64) {
2916
+ const dir = getKeyDir();
2917
+ await mkdir3(dir, { recursive: true });
2918
+ const keyPath = getKeyPath();
2919
+ const machineKey = deriveMachineKey();
2920
+ if (machineKey) {
2921
+ const encrypted = encryptWithMachineKey(b64, machineKey);
2922
+ await writeFile3(keyPath, encrypted + "\n", "utf-8");
2923
+ await chmod2(keyPath, 384);
2924
+ return "encrypted";
2925
+ }
2926
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
2927
+ await chmod2(keyPath, 384);
2928
+ return "plaintext";
2929
+ }
2906
2930
  async function getMasterKey() {
2907
2931
  const nativeValue = macKeychainGet() ?? linuxSecretGet();
2908
2932
  if (nativeValue) {
@@ -2954,6 +2978,20 @@ async function getMasterKey() {
2954
2978
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
2955
2979
  if (migrated) {
2956
2980
  process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
2981
+ try {
2982
+ await unlink(keyPath);
2983
+ process.stderr.write("[keychain] Removed legacy master.key file after native keychain migration.\n");
2984
+ } catch {
2985
+ }
2986
+ } else if (!content.startsWith(ENCRYPTED_PREFIX)) {
2987
+ const fallback = await writeMachineBoundFileFallback(b64Value);
2988
+ if (fallback === "encrypted") {
2989
+ process.stderr.write("[keychain] Upgraded legacy plaintext master.key to machine-bound encrypted fallback.\n");
2990
+ } else {
2991
+ process.stderr.write(
2992
+ "[keychain] WARNING: Could not encrypt legacy master.key \u2014 plaintext fallback remains.\n"
2993
+ );
2994
+ }
2957
2995
  }
2958
2996
  return key;
2959
2997
  } catch (err) {
@@ -3226,6 +3264,7 @@ var init_memory_write_governor = __esm({
3226
3264
  // src/lib/shard-manager.ts
3227
3265
  var shard_manager_exports = {};
3228
3266
  __export(shard_manager_exports, {
3267
+ auditShardHealth: () => auditShardHealth,
3229
3268
  disposeShards: () => disposeShards,
3230
3269
  ensureShardSchema: () => ensureShardSchema,
3231
3270
  getOpenShardCount: () => getOpenShardCount,
@@ -3292,6 +3331,70 @@ function listShards() {
3292
3331
  if (!existsSync7(SHARDS_DIR)) return [];
3293
3332
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3294
3333
  }
3334
+ async function auditShardHealth(options = {}) {
3335
+ if (!_encryptionKey) {
3336
+ throw new Error("Shard manager not initialized. Call initShardManager() first.");
3337
+ }
3338
+ const repair = options.repair === true;
3339
+ const dryRun = options.dryRun === true;
3340
+ const names = listShards();
3341
+ const shards = [];
3342
+ for (const name of names) {
3343
+ const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
3344
+ const stat = statSync2(dbPath);
3345
+ const item = {
3346
+ name,
3347
+ path: dbPath,
3348
+ ok: false,
3349
+ unreadable: false,
3350
+ error: null,
3351
+ size: stat.size,
3352
+ mtime: stat.mtime.toISOString(),
3353
+ memoryCount: null
3354
+ };
3355
+ const client = createClient2({
3356
+ url: `file:${dbPath}`,
3357
+ encryptionKey: _encryptionKey
3358
+ });
3359
+ try {
3360
+ await client.execute("SELECT COUNT(*) as cnt FROM sqlite_schema");
3361
+ const hasMemories = await client.execute(
3362
+ "SELECT COUNT(*) as cnt FROM sqlite_schema WHERE type = 'table' AND name = 'memories'"
3363
+ );
3364
+ if (Number(hasMemories.rows[0]?.cnt ?? 0) > 0) {
3365
+ const mem = await client.execute("SELECT COUNT(*) as cnt FROM memories");
3366
+ item.memoryCount = Number(mem.rows[0]?.cnt ?? 0);
3367
+ }
3368
+ item.ok = true;
3369
+ } catch (err) {
3370
+ const message = err instanceof Error ? err.message : String(err);
3371
+ item.error = message;
3372
+ item.unreadable = /SQLITE_NOTADB|file is not a database/i.test(message);
3373
+ if (item.unreadable && repair && !dryRun) {
3374
+ client.close();
3375
+ _shards.delete(name);
3376
+ _shardLastAccess.delete(name);
3377
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3378
+ const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
3379
+ renameSync3(dbPath, archivedPath);
3380
+ item.archivedPath = archivedPath;
3381
+ }
3382
+ } finally {
3383
+ try {
3384
+ client.close();
3385
+ } catch {
3386
+ }
3387
+ }
3388
+ shards.push(item);
3389
+ }
3390
+ return {
3391
+ total: shards.length,
3392
+ ok: shards.filter((s) => s.ok).length,
3393
+ unreadable: shards.filter((s) => s.unreadable).length,
3394
+ archived: shards.filter((s) => Boolean(s.archivedPath)).length,
3395
+ shards
3396
+ };
3397
+ }
3295
3398
  async function ensureShardSchema(client) {
3296
3399
  await client.execute("PRAGMA journal_mode = WAL");
3297
3400
  await client.execute("PRAGMA busy_timeout = 30000");