@askexenow/exe-os 0.9.64 → 0.9.66

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 (105) hide show
  1. package/deploy/stack-manifests/v0.9.json +4 -4
  2. package/dist/bin/backfill-conversations.js +22 -0
  3. package/dist/bin/backfill-responses.js +22 -0
  4. package/dist/bin/backfill-vectors.js +22 -0
  5. package/dist/bin/cleanup-stale-review-tasks.js +22 -0
  6. package/dist/bin/cli.js +2280 -1199
  7. package/dist/bin/exe-agent-config.js +4 -0
  8. package/dist/bin/exe-agent.js +16 -0
  9. package/dist/bin/exe-assign.js +22 -0
  10. package/dist/bin/exe-boot.js +116 -7
  11. package/dist/bin/exe-call.js +16 -0
  12. package/dist/bin/exe-cloud.js +6671 -464
  13. package/dist/bin/exe-dispatch.js +24 -0
  14. package/dist/bin/exe-doctor.js +2845 -1223
  15. package/dist/bin/exe-export-behaviors.js +24 -0
  16. package/dist/bin/exe-forget.js +22 -0
  17. package/dist/bin/exe-gateway.js +24 -0
  18. package/dist/bin/exe-heartbeat.js +23 -0
  19. package/dist/bin/exe-kill.js +22 -0
  20. package/dist/bin/exe-launch-agent.js +24 -0
  21. package/dist/bin/exe-link.js +310 -178
  22. package/dist/bin/exe-new-employee.js +127 -1
  23. package/dist/bin/exe-pending-messages.js +22 -0
  24. package/dist/bin/exe-pending-notifications.js +22 -0
  25. package/dist/bin/exe-pending-reviews.js +22 -0
  26. package/dist/bin/exe-rename.js +22 -0
  27. package/dist/bin/exe-review.js +22 -0
  28. package/dist/bin/exe-search.js +24 -0
  29. package/dist/bin/exe-session-cleanup.js +24 -0
  30. package/dist/bin/exe-settings.js +10 -0
  31. package/dist/bin/exe-start-codex.js +135 -1
  32. package/dist/bin/exe-start-opencode.js +149 -1
  33. package/dist/bin/exe-status.js +22 -0
  34. package/dist/bin/exe-team.js +22 -0
  35. package/dist/bin/git-sweep.js +24 -0
  36. package/dist/bin/graph-backfill.js +22 -0
  37. package/dist/bin/graph-export.js +22 -0
  38. package/dist/bin/install.js +115 -1
  39. package/dist/bin/intercom-check.js +24 -0
  40. package/dist/bin/scan-tasks.js +24 -0
  41. package/dist/bin/setup.js +412 -157
  42. package/dist/bin/shard-migrate.js +22 -0
  43. package/dist/bin/update.js +4 -0
  44. package/dist/gateway/index.js +24 -0
  45. package/dist/hooks/bug-report-worker.js +135 -42
  46. package/dist/hooks/codex-stop-task-finalizer.js +24 -0
  47. package/dist/hooks/commit-complete.js +24 -0
  48. package/dist/hooks/error-recall.js +24 -0
  49. package/dist/hooks/exe-heartbeat-hook.js +4 -0
  50. package/dist/hooks/ingest-worker.js +4 -0
  51. package/dist/hooks/ingest.js +23 -0
  52. package/dist/hooks/instructions-loaded.js +22 -0
  53. package/dist/hooks/notification.js +22 -0
  54. package/dist/hooks/post-compact.js +22 -0
  55. package/dist/hooks/post-tool-combined.js +24 -0
  56. package/dist/hooks/pre-compact.js +260 -109
  57. package/dist/hooks/pre-tool-use.js +22 -0
  58. package/dist/hooks/prompt-submit.js +24 -0
  59. package/dist/hooks/session-end.js +161 -122
  60. package/dist/hooks/session-start.js +142 -0
  61. package/dist/hooks/stop.js +23 -0
  62. package/dist/hooks/subagent-stop.js +22 -0
  63. package/dist/hooks/summary-worker.js +195 -79
  64. package/dist/index.js +24 -0
  65. package/dist/lib/agent-config.js +4 -0
  66. package/dist/lib/cloud-sync.js +50 -6
  67. package/dist/lib/config.js +12 -0
  68. package/dist/lib/consolidation.js +4 -0
  69. package/dist/lib/database.js +4 -0
  70. package/dist/lib/db-daemon-client.js +4 -0
  71. package/dist/lib/db.js +4 -0
  72. package/dist/lib/device-registry.js +4 -0
  73. package/dist/lib/embedder.js +12 -0
  74. package/dist/lib/employee-templates.js +16 -0
  75. package/dist/lib/employees.js +4 -0
  76. package/dist/lib/exe-daemon-client.js +4 -0
  77. package/dist/lib/exe-daemon.js +1144 -480
  78. package/dist/lib/hybrid-search.js +24 -0
  79. package/dist/lib/identity.js +4 -0
  80. package/dist/lib/license.js +4 -0
  81. package/dist/lib/messaging.js +4 -0
  82. package/dist/lib/reminders.js +4 -0
  83. package/dist/lib/schedules.js +22 -0
  84. package/dist/lib/skill-learning.js +12 -0
  85. package/dist/lib/status-brief.js +39 -0
  86. package/dist/lib/store.js +22 -0
  87. package/dist/lib/task-router.js +4 -0
  88. package/dist/lib/tasks.js +12 -0
  89. package/dist/lib/tmux-routing.js +12 -0
  90. package/dist/lib/token-spend.js +4 -0
  91. package/dist/mcp/server.js +1045 -427
  92. package/dist/mcp/tools/complete-reminder.js +4 -0
  93. package/dist/mcp/tools/create-reminder.js +4 -0
  94. package/dist/mcp/tools/create-task.js +12 -0
  95. package/dist/mcp/tools/deactivate-behavior.js +4 -0
  96. package/dist/mcp/tools/list-reminders.js +4 -0
  97. package/dist/mcp/tools/list-tasks.js +4 -0
  98. package/dist/mcp/tools/send-message.js +4 -0
  99. package/dist/mcp/tools/update-task.js +12 -0
  100. package/dist/runtime/index.js +24 -0
  101. package/dist/tui/App.js +24 -0
  102. package/package.json +3 -2
  103. package/src/commands/exe/cloud.md +15 -8
  104. package/src/commands/exe/link.md +7 -6
  105. package/stack.release.json +2 -2
@@ -149,6 +149,11 @@ function normalizeAutoUpdate(raw) {
149
149
  const userAU = raw.autoUpdate ?? {};
150
150
  raw.autoUpdate = { ...defaultAU, ...userAU };
151
151
  }
152
+ function normalizeOrchestration(raw) {
153
+ const defaultOrg = DEFAULT_CONFIG.orchestration;
154
+ const userOrg = raw.orchestration ?? {};
155
+ raw.orchestration = { ...defaultOrg, ...userOrg };
156
+ }
152
157
  async function loadConfig() {
153
158
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
154
159
  await ensurePrivateDir(dir);
@@ -173,6 +178,7 @@ async function loadConfig() {
173
178
  normalizeScalingRoadmap(migratedCfg);
174
179
  normalizeSessionLifecycle(migratedCfg);
175
180
  normalizeAutoUpdate(migratedCfg);
181
+ normalizeOrchestration(migratedCfg);
176
182
  const config2 = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
177
183
  if (config2.dbPath.startsWith("~")) {
178
184
  config2.dbPath = config2.dbPath.replace(/^~/, os.homedir());
@@ -196,6 +202,7 @@ function loadConfigSync() {
196
202
  normalizeScalingRoadmap(migratedCfg);
197
203
  normalizeSessionLifecycle(migratedCfg);
198
204
  normalizeAutoUpdate(migratedCfg);
205
+ normalizeOrchestration(migratedCfg);
199
206
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
200
207
  } catch {
201
208
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
@@ -217,6 +224,7 @@ async function loadConfigFrom(configPath) {
217
224
  normalizeScalingRoadmap(migratedCfg);
218
225
  normalizeSessionLifecycle(migratedCfg);
219
226
  normalizeAutoUpdate(migratedCfg);
227
+ normalizeOrchestration(migratedCfg);
220
228
  return { ...DEFAULT_CONFIG, ...migratedCfg };
221
229
  } catch {
222
230
  return { ...DEFAULT_CONFIG };
@@ -288,6 +296,10 @@ var init_config = __esm({
288
296
  checkOnBoot: true,
289
297
  autoInstall: false,
290
298
  checkIntervalMs: 24 * 60 * 60 * 1e3
299
+ },
300
+ orchestration: {
301
+ phase: "phase_1_coo",
302
+ phaseSetBy: "default"
291
303
  }
292
304
  };
293
305
  CONFIG_MIGRATIONS = [
@@ -313,6 +325,15 @@ var init_memory = __esm({
313
325
  });
314
326
 
315
327
  // src/lib/daemon-protocol.ts
328
+ var daemon_protocol_exports = {};
329
+ __export(daemon_protocol_exports, {
330
+ deserializeArgs: () => deserializeArgs,
331
+ deserializeResultSet: () => deserializeResultSet,
332
+ deserializeValue: () => deserializeValue,
333
+ serializeArgs: () => serializeArgs,
334
+ serializeResultSet: () => serializeResultSet,
335
+ serializeValue: () => serializeValue
336
+ });
316
337
  function serializeValue(v) {
317
338
  if (v === null || v === void 0) return null;
318
339
  if (typeof v === "bigint") return Number(v);
@@ -337,6 +358,9 @@ function deserializeValue(v) {
337
358
  }
338
359
  return v;
339
360
  }
361
+ function serializeArgs(args) {
362
+ return args.map(serializeValue);
363
+ }
340
364
  function deserializeArgs(args) {
341
365
  return args.map(deserializeValue);
342
366
  }
@@ -3498,10 +3522,12 @@ var init_memory_write_governor = __esm({
3498
3522
  // src/lib/projection-worker.ts
3499
3523
  var projection_worker_exports = {};
3500
3524
  __export(projection_worker_exports, {
3525
+ computeProjectionBackoffMs: () => computeProjectionBackoffMs,
3501
3526
  processProjectionBatch: () => processProjectionBatch,
3502
3527
  projectionHandlersForTests: () => projectionHandlersForTests,
3503
3528
  resetProjectionWorkerForTests: () => resetProjectionWorkerForTests,
3504
3529
  setProjectionWorkerPrismaClientForTests: () => setProjectionWorkerPrismaClientForTests,
3530
+ shouldStartProjectionWorker: () => shouldStartProjectionWorker,
3505
3531
  startProjectionWorker: () => startProjectionWorker,
3506
3532
  stopProjectionWorker: () => stopProjectionWorker
3507
3533
  });
@@ -3540,8 +3566,12 @@ function resetProjectionWorkerForTests() {
3540
3566
  clearTimeout(pollTimer);
3541
3567
  pollTimer = null;
3542
3568
  }
3569
+ consecutivePollErrors = 0;
3543
3570
  prismaPromise = null;
3544
3571
  }
3572
+ function isTruthyEnv(value) {
3573
+ return /^(1|true|yes|on)$/i.test(value ?? "");
3574
+ }
3545
3575
  async function processBatch() {
3546
3576
  const prisma = await loadPrisma();
3547
3577
  const events = await prisma.$queryRawUnsafe(
@@ -3583,36 +3613,65 @@ async function processBatch() {
3583
3613
  }
3584
3614
  return processed;
3585
3615
  }
3616
+ async function shouldStartProjectionWorker() {
3617
+ try {
3618
+ const config2 = await loadConfig();
3619
+ const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
3620
+ if (!envEnabled && config2.cloud?.syncToPostgres !== true) {
3621
+ return { start: false, reason: "cloud.syncToPostgres is not enabled" };
3622
+ }
3623
+ } catch (err) {
3624
+ return { start: false, reason: `config unavailable: ${err instanceof Error ? err.message : String(err)}` };
3625
+ }
3626
+ if (!process.env.DATABASE_URL) {
3627
+ return { start: false, reason: "DATABASE_URL is not set" };
3628
+ }
3629
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path8.join(os5.homedir(), "exe-db");
3630
+ if (!existsSync8(path8.join(exeDbRoot, "package.json")) && !process.env.EXE_OS_PRISMA_CLIENT_PATH) {
3631
+ return { start: false, reason: "exe-db Prisma client not found" };
3632
+ }
3633
+ return { start: true };
3634
+ }
3635
+ function computeProjectionBackoffMs(errorCount) {
3636
+ if (errorCount <= 0) return POLL_INTERVAL_MS;
3637
+ return Math.min(POLL_INTERVAL_MS * 2 ** Math.min(errorCount, 5), MAX_POLL_BACKOFF_MS);
3638
+ }
3586
3639
  function startProjectionWorker() {
3587
3640
  if (running) return;
3588
- if (!process.env.DATABASE_URL) {
3589
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path8.join(os5.homedir(), "exe-db");
3590
- if (!existsSync8(path8.join(exeDbRoot, "package.json"))) {
3591
- process.stderr.write("[projection-worker] Skipped \u2014 no exe-db found. Set DATABASE_URL or EXE_DB_ROOT to enable.\n");
3641
+ void (async () => {
3642
+ const decision = await shouldStartProjectionWorker();
3643
+ if (!decision.start) {
3644
+ process.stderr.write(`[projection-worker] Skipped \u2014 ${decision.reason}.
3645
+ `);
3592
3646
  return;
3593
3647
  }
3594
- }
3595
- running = true;
3596
- process.stderr.write("[projection-worker] Starting...\n");
3597
- const tick = async () => {
3598
- if (!running) return;
3599
- try {
3600
- const count = await processBatch();
3601
- if (count > 0) {
3602
- process.stderr.write(`[projection-worker] Processed ${count} events.
3648
+ running = true;
3649
+ consecutivePollErrors = 0;
3650
+ process.stderr.write("[projection-worker] Starting...\n");
3651
+ const tick = async () => {
3652
+ if (!running) return;
3653
+ let nextDelay = POLL_INTERVAL_MS;
3654
+ try {
3655
+ const count = await processBatch();
3656
+ consecutivePollErrors = 0;
3657
+ if (count > 0) {
3658
+ process.stderr.write(`[projection-worker] Processed ${count} events.
3603
3659
  `);
3604
- }
3605
- } catch (err) {
3606
- process.stderr.write(
3607
- `[projection-worker] Poll error: ${err instanceof Error ? err.message : String(err)}
3660
+ }
3661
+ } catch (err) {
3662
+ consecutivePollErrors++;
3663
+ nextDelay = computeProjectionBackoffMs(consecutivePollErrors);
3664
+ process.stderr.write(
3665
+ `[projection-worker] Poll error (${consecutivePollErrors}; next retry ${Math.round(nextDelay / 1e3)}s): ${err instanceof Error ? err.message : String(err)}
3608
3666
  `
3609
- );
3610
- }
3611
- if (running) {
3612
- pollTimer = setTimeout(tick, POLL_INTERVAL_MS);
3613
- }
3614
- };
3615
- void tick();
3667
+ );
3668
+ }
3669
+ if (running) {
3670
+ pollTimer = setTimeout(tick, nextDelay);
3671
+ }
3672
+ };
3673
+ void tick();
3674
+ })();
3616
3675
  }
3617
3676
  function stopProjectionWorker() {
3618
3677
  running = false;
@@ -3625,10 +3684,11 @@ function stopProjectionWorker() {
3625
3684
  async function processProjectionBatch() {
3626
3685
  return processBatch();
3627
3686
  }
3628
- var prismaPromise, projectionHandlers, projectionHandlersForTests, defaultHandler, BATCH_SIZE, POLL_INTERVAL_MS, running, pollTimer;
3687
+ var prismaPromise, projectionHandlers, projectionHandlersForTests, defaultHandler, BATCH_SIZE, POLL_INTERVAL_MS, MAX_POLL_BACKOFF_MS, running, pollTimer, consecutivePollErrors;
3629
3688
  var init_projection_worker = __esm({
3630
3689
  "src/lib/projection-worker.ts"() {
3631
3690
  "use strict";
3691
+ init_config();
3632
3692
  prismaPromise = null;
3633
3693
  projectionHandlers = {
3634
3694
  async whatsapp(event, prisma) {
@@ -3739,8 +3799,10 @@ var init_projection_worker = __esm({
3739
3799
  };
3740
3800
  BATCH_SIZE = 50;
3741
3801
  POLL_INTERVAL_MS = 1e4;
3802
+ MAX_POLL_BACKOFF_MS = 5 * 6e4;
3742
3803
  running = false;
3743
3804
  pollTimer = null;
3805
+ consecutivePollErrors = 0;
3744
3806
  }
3745
3807
  });
3746
3808
 
@@ -4281,8 +4343,8 @@ function deriveMachineKey() {
4281
4343
  }
4282
4344
  function readMachineId() {
4283
4345
  try {
4284
- const { readFileSync: readFileSync35 } = __require("fs");
4285
- return readFileSync35("/etc/machine-id", "utf-8").trim();
4346
+ const { readFileSync: readFileSync36 } = __require("fs");
4347
+ return readFileSync36("/etc/machine-id", "utf-8").trim();
4286
4348
  } catch {
4287
4349
  return "";
4288
4350
  }
@@ -4573,6 +4635,12 @@ var init_platform_procedures = __esm({
4573
4635
  priority: "p0",
4574
4636
  content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
4575
4637
  },
4638
+ {
4639
+ title: "Customer orchestration maturity \u2014 recommend, never trap",
4640
+ domain: "workflow",
4641
+ priority: "p1",
4642
+ content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
4643
+ },
4576
4644
  {
4577
4645
  title: "Single dispatch path \u2014 create_task only",
4578
4646
  domain: "workflow",
@@ -4631,6 +4699,12 @@ var init_platform_procedures = __esm({
4631
4699
  priority: "p0",
4632
4700
  content: "exe-build-adv is MANDATORY for ALL work touching 3+ files. Run /exe-build-adv --auto BEFORE implementation. Pipeline: Spec \u2192 AC \u2192 Tests \u2192 Evaluate \u2192 Fix. No multi-file feature ships without pipeline artifacts. No exceptions \u2014 managers reject work without them."
4633
4701
  },
4702
+ {
4703
+ title: "Commit discipline \u2014 never leave verified work floating",
4704
+ domain: "workflow",
4705
+ priority: "p1",
4706
+ content: "After any code-change batch passes typecheck/tests/build, run git status, summarize changed files, and commit with a clear message before ending the session. If work must remain uncommitted for review/dogfood, explicitly say so, list the files, and state the blocker. Never imply work is complete while verified changes are still floating locally."
4707
+ },
4634
4708
  {
4635
4709
  title: "Desktop and TUI are the same product",
4636
4710
  domain: "architecture",
@@ -5968,10 +6042,10 @@ async function disposeEmbedder() {
5968
6042
  async function embedDirect(text3) {
5969
6043
  const llamaCpp = await import("node-llama-cpp");
5970
6044
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
5971
- const { existsSync: existsSync42 } = await import("fs");
5972
- const path55 = await import("path");
5973
- const modelPath = path55.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
5974
- if (!existsSync42(modelPath)) {
6045
+ const { existsSync: existsSync43 } = await import("fs");
6046
+ const path56 = await import("path");
6047
+ const modelPath = path56.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
6048
+ if (!existsSync43(modelPath)) {
5975
6049
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
5976
6050
  }
5977
6051
  const llama = await llamaCpp.getLlama();
@@ -7062,10 +7136,10 @@ async function hybridSearch(queryText, agentId, options) {
7062
7136
  };
7063
7137
  try {
7064
7138
  const fs = await import("fs");
7065
- const path55 = await import("path");
7139
+ const path56 = await import("path");
7066
7140
  const os24 = await import("os");
7067
- const logPath = path55.join(os24.homedir(), ".exe-os", "search-quality.jsonl");
7068
- fs.mkdirSync(path55.dirname(logPath), { recursive: true });
7141
+ const logPath = path56.join(os24.homedir(), ".exe-os", "search-quality.jsonl");
7142
+ fs.mkdirSync(path56.dirname(logPath), { recursive: true });
7069
7143
  fs.appendFileSync(logPath, JSON.stringify(logEntry) + "\n");
7070
7144
  } catch {
7071
7145
  }
@@ -8698,8 +8772,8 @@ __export(wiki_client_exports, {
8698
8772
  listDocuments: () => listDocuments,
8699
8773
  listWorkspaces: () => listWorkspaces
8700
8774
  });
8701
- async function wikiFetch(config2, path55, method = "GET", body) {
8702
- const url = `${config2.baseUrl}/api/v1${path55}`;
8775
+ async function wikiFetch(config2, path56, method = "GET", body) {
8776
+ const url = `${config2.baseUrl}/api/v1${path56}`;
8703
8777
  const headers = {
8704
8778
  Authorization: `Bearer ${config2.apiKey}`,
8705
8779
  "Content-Type": "application/json"
@@ -8732,7 +8806,7 @@ async function wikiFetch(config2, path55, method = "GET", body) {
8732
8806
  }
8733
8807
  }
8734
8808
  if (!response.ok) {
8735
- throw new Error(`Wiki API ${method} ${path55}: ${response.status} ${response.statusText}`);
8809
+ throw new Error(`Wiki API ${method} ${path56}: ${response.status} ${response.statusText}`);
8736
8810
  }
8737
8811
  return response.json();
8738
8812
  } finally {
@@ -14310,10 +14384,10 @@ function registerCreateTask(server) {
14310
14384
  skipDispatch: true
14311
14385
  });
14312
14386
  try {
14313
- const { existsSync: existsSync42, mkdirSync: mkdirSync21, writeFileSync: writeFileSync25 } = await import("fs");
14387
+ const { existsSync: existsSync43, mkdirSync: mkdirSync22, writeFileSync: writeFileSync26 } = await import("fs");
14314
14388
  const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
14315
14389
  const idPath = identityPath2(assigned_to);
14316
- if (!existsSync42(idPath)) {
14390
+ if (!existsSync43(idPath)) {
14317
14391
  const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
14318
14392
  const employees = await loadEmployees2();
14319
14393
  const emp = employees.find((e) => e.name === assigned_to);
@@ -14322,8 +14396,8 @@ function registerCreateTask(server) {
14322
14396
  const template = getTemplateForTitle2(emp.role);
14323
14397
  if (template) {
14324
14398
  const dir = (await import("path")).dirname(idPath);
14325
- if (!existsSync42(dir)) mkdirSync21(dir, { recursive: true });
14326
- writeFileSync25(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
14399
+ if (!existsSync43(dir)) mkdirSync22(dir, { recursive: true });
14400
+ writeFileSync26(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
14327
14401
  }
14328
14402
  }
14329
14403
  }
@@ -17807,12 +17881,12 @@ function registerExportGraph(server) {
17807
17881
  }
17808
17882
  const html = await exportGraphHTML(client);
17809
17883
  const fs = await import("fs");
17810
- const path55 = await import("path");
17884
+ const path56 = await import("path");
17811
17885
  const os24 = await import("os");
17812
- const outDir = path55.join(os24.homedir(), ".exe-os", "exports");
17886
+ const outDir = path56.join(os24.homedir(), ".exe-os", "exports");
17813
17887
  fs.mkdirSync(outDir, { recursive: true });
17814
17888
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
17815
- const filePath = path55.join(outDir, `graph-${timestamp}.html`);
17889
+ const filePath = path56.join(outDir, `graph-${timestamp}.html`);
17816
17890
  fs.writeFileSync(filePath, html, "utf-8");
17817
17891
  return {
17818
17892
  content: [
@@ -20991,6 +21065,259 @@ var init_conflict_detector = __esm({
20991
21065
  }
20992
21066
  });
20993
21067
 
21068
+ // src/adapters/runtime-hook-manifest.ts
21069
+ function manifestEntryForCommand(command) {
21070
+ return EXE_HOOK_MANIFEST.find((entry) => command.includes(entry.commandMarker));
21071
+ }
21072
+ var EXE_HOOKS, EXE_HOOK_MANIFEST, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS;
21073
+ var init_runtime_hook_manifest = __esm({
21074
+ "src/adapters/runtime-hook-manifest.ts"() {
21075
+ "use strict";
21076
+ EXE_HOOKS = {
21077
+ postToolCombined: "dist/hooks/post-tool-combined.js",
21078
+ sessionStart: "dist/hooks/session-start.js",
21079
+ promptSubmit: "dist/hooks/prompt-submit.js",
21080
+ heartbeat: "dist/hooks/exe-heartbeat-hook.js",
21081
+ stop: "dist/hooks/stop.js",
21082
+ preToolUse: "dist/hooks/pre-tool-use.js",
21083
+ subagentStop: "dist/hooks/subagent-stop.js",
21084
+ preCompact: "dist/hooks/pre-compact.js",
21085
+ postCompact: "dist/hooks/post-compact.js",
21086
+ sessionEnd: "dist/hooks/session-end.js",
21087
+ notification: "dist/hooks/notification.js",
21088
+ instructionsLoaded: "dist/hooks/instructions-loaded.js"
21089
+ };
21090
+ EXE_HOOK_MANIFEST = [
21091
+ {
21092
+ key: "postToolCombined",
21093
+ event: "PostToolUse",
21094
+ commandMarker: EXE_HOOKS.postToolCombined,
21095
+ owner: "exe-os",
21096
+ purpose: "Single PostToolUse entrypoint for ingestion, error recall, summaries, and bug detection.",
21097
+ runtimes: ["claude", "codex", "opencode"],
21098
+ checkpointRole: "none"
21099
+ },
21100
+ {
21101
+ key: "sessionStart",
21102
+ event: "SessionStart",
21103
+ commandMarker: EXE_HOOKS.sessionStart,
21104
+ owner: "exe-os",
21105
+ purpose: "Loads agent identity, procedures, and boot context.",
21106
+ runtimes: ["claude", "codex", "opencode"],
21107
+ checkpointRole: "none"
21108
+ },
21109
+ {
21110
+ key: "promptSubmit",
21111
+ event: "UserPromptSubmit",
21112
+ commandMarker: EXE_HOOKS.promptSubmit,
21113
+ owner: "exe-os",
21114
+ purpose: "Injects current tasks, pending reviews, and local context before each prompt.",
21115
+ runtimes: ["claude", "codex", "opencode"],
21116
+ checkpointRole: "none"
21117
+ },
21118
+ {
21119
+ key: "heartbeat",
21120
+ event: "UserPromptSubmit",
21121
+ commandMarker: EXE_HOOKS.heartbeat,
21122
+ owner: "exe-os",
21123
+ purpose: "Lightweight heartbeat/status sidecar for Claude Code sessions.",
21124
+ runtimes: ["claude"],
21125
+ checkpointRole: "none"
21126
+ },
21127
+ {
21128
+ key: "stop",
21129
+ event: "Stop",
21130
+ commandMarker: EXE_HOOKS.stop,
21131
+ owner: "exe-os",
21132
+ purpose: "Finalizes task state, capacity signals, and emergency checkpointing.",
21133
+ runtimes: ["claude", "codex", "opencode"],
21134
+ checkpointRole: "capacity_checkpoint"
21135
+ },
21136
+ {
21137
+ key: "preToolUse",
21138
+ event: "PreToolUse",
21139
+ commandMarker: EXE_HOOKS.preToolUse,
21140
+ owner: "exe-os",
21141
+ purpose: "Preflight guardrails before shell/tool execution.",
21142
+ runtimes: ["claude", "codex", "opencode"],
21143
+ checkpointRole: "none"
21144
+ },
21145
+ {
21146
+ key: "subagentStop",
21147
+ event: "SubagentStop",
21148
+ commandMarker: EXE_HOOKS.subagentStop,
21149
+ owner: "exe-os",
21150
+ purpose: "Captures subagent completion context.",
21151
+ runtimes: ["claude"],
21152
+ checkpointRole: "none"
21153
+ },
21154
+ {
21155
+ key: "preCompact",
21156
+ event: "PreCompact",
21157
+ commandMarker: EXE_HOOKS.preCompact,
21158
+ owner: "exe-os",
21159
+ purpose: "Writes active-task snapshot and compaction recovery context.",
21160
+ runtimes: ["claude"],
21161
+ checkpointRole: "recovery_context"
21162
+ },
21163
+ {
21164
+ key: "postCompact",
21165
+ event: "PostCompact",
21166
+ commandMarker: EXE_HOOKS.postCompact,
21167
+ owner: "exe-os",
21168
+ purpose: "Rehydrates recovery context after compaction.",
21169
+ runtimes: ["claude"],
21170
+ checkpointRole: "recovery_context"
21171
+ },
21172
+ {
21173
+ key: "sessionEnd",
21174
+ event: "SessionEnd",
21175
+ commandMarker: EXE_HOOKS.sessionEnd,
21176
+ owner: "exe-os",
21177
+ purpose: "Stores session-end checkpoint and triages orphaned in-progress tasks.",
21178
+ runtimes: ["claude"],
21179
+ checkpointRole: "session_summary"
21180
+ },
21181
+ {
21182
+ key: "notification",
21183
+ event: "Notification",
21184
+ commandMarker: EXE_HOOKS.notification,
21185
+ owner: "exe-os",
21186
+ purpose: "Captures runtime notifications and nudges.",
21187
+ runtimes: ["claude"],
21188
+ checkpointRole: "none"
21189
+ },
21190
+ {
21191
+ key: "instructionsLoaded",
21192
+ event: "InstructionsLoaded",
21193
+ commandMarker: EXE_HOOKS.instructionsLoaded,
21194
+ owner: "exe-os",
21195
+ purpose: "Applies runtime instruction post-processing.",
21196
+ runtimes: ["claude"],
21197
+ checkpointRole: "none"
21198
+ }
21199
+ ];
21200
+ LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS = [
21201
+ "dist/hooks/ingest.js",
21202
+ "dist/hooks/error-recall.js",
21203
+ "dist/hooks/ingest-worker.js"
21204
+ ];
21205
+ }
21206
+ });
21207
+
21208
+ // src/lib/key-backup-status.ts
21209
+ var key_backup_status_exports = {};
21210
+ __export(key_backup_status_exports, {
21211
+ getKeyBackupStatus: () => getKeyBackupStatus,
21212
+ keyBackupMarkerPath: () => keyBackupMarkerPath,
21213
+ markKeyBackupConfirmed: () => markKeyBackupConfirmed
21214
+ });
21215
+ import { existsSync as existsSync30, mkdirSync as mkdirSync12, readFileSync as readFileSync23, writeFileSync as writeFileSync16 } from "fs";
21216
+ import path35 from "path";
21217
+ function keyBackupMarkerPath() {
21218
+ return path35.join(EXE_AI_DIR, "key-backup-confirmed.json");
21219
+ }
21220
+ function getKeyBackupStatus() {
21221
+ const marker = keyBackupMarkerPath();
21222
+ if (!existsSync30(marker)) return { exists: false };
21223
+ try {
21224
+ const parsed = JSON.parse(readFileSync23(marker, "utf8"));
21225
+ return {
21226
+ exists: true,
21227
+ confirmedAt: parsed.confirmedAt,
21228
+ source: parsed.source
21229
+ };
21230
+ } catch {
21231
+ return { exists: true };
21232
+ }
21233
+ }
21234
+ function markKeyBackupConfirmed(source) {
21235
+ mkdirSync12(EXE_AI_DIR, { recursive: true, mode: 448 });
21236
+ writeFileSync16(
21237
+ keyBackupMarkerPath(),
21238
+ JSON.stringify({ confirmedAt: (/* @__PURE__ */ new Date()).toISOString(), source }, null, 2) + "\n",
21239
+ { mode: 384 }
21240
+ );
21241
+ }
21242
+ var init_key_backup_status = __esm({
21243
+ "src/lib/key-backup-status.ts"() {
21244
+ "use strict";
21245
+ init_config();
21246
+ }
21247
+ });
21248
+
21249
+ // src/bin/fast-db-init.ts
21250
+ var fast_db_init_exports = {};
21251
+ __export(fast_db_init_exports, {
21252
+ fastDbInit: () => fastDbInit
21253
+ });
21254
+ async function fastDbInit() {
21255
+ const { isInitialized: isInitialized2, getClient: getClient2, setExternalClient: setExternalClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
21256
+ if (isInitialized2()) {
21257
+ return getClient2();
21258
+ }
21259
+ try {
21260
+ const { connectEmbedDaemon: connectEmbedDaemon2, sendDaemonRequest: sendDaemonRequest2, isClientConnected: isClientConnected2 } = await Promise.resolve().then(() => (init_exe_daemon_client(), exe_daemon_client_exports));
21261
+ const { deserializeResultSet: deserializeResultSet2 } = await Promise.resolve().then(() => (init_daemon_protocol(), daemon_protocol_exports));
21262
+ await connectEmbedDaemon2();
21263
+ if (isClientConnected2()) {
21264
+ const daemonClient = {
21265
+ async execute(stmt) {
21266
+ const sql = typeof stmt === "string" ? stmt : stmt.sql;
21267
+ const args = typeof stmt === "string" ? [] : Array.isArray(stmt.args) ? stmt.args : [];
21268
+ const resp = await sendDaemonRequest2({ type: "db-execute", sql, args });
21269
+ if (resp.error) throw new Error(String(resp.error));
21270
+ if (resp.db) return deserializeResultSet2(resp.db);
21271
+ throw new Error("Unexpected daemon response");
21272
+ },
21273
+ async batch(stmts, mode) {
21274
+ const statements = stmts.map((s) => {
21275
+ const sql = typeof s === "string" ? s : s.sql;
21276
+ const args = typeof s === "string" ? [] : Array.isArray(s.args) ? s.args : [];
21277
+ return { sql, args };
21278
+ });
21279
+ const resp = await sendDaemonRequest2({ type: "db-batch", statements, mode: mode ?? "deferred" });
21280
+ if (resp.error) throw new Error(String(resp.error));
21281
+ const batchResults = resp["db-batch"];
21282
+ if (batchResults) return batchResults.map(deserializeResultSet2);
21283
+ throw new Error("Unexpected daemon batch response");
21284
+ },
21285
+ async transaction(_mode) {
21286
+ throw new Error("Transactions not supported via daemon socket");
21287
+ },
21288
+ async executeMultiple(_sql) {
21289
+ throw new Error("executeMultiple not supported via daemon socket");
21290
+ },
21291
+ async migrate(_stmts) {
21292
+ throw new Error("migrate not supported via daemon socket");
21293
+ },
21294
+ sync() {
21295
+ return Promise.resolve(void 0);
21296
+ },
21297
+ close() {
21298
+ },
21299
+ get closed() {
21300
+ return false;
21301
+ },
21302
+ get protocol() {
21303
+ return "file";
21304
+ }
21305
+ };
21306
+ setExternalClient2(daemonClient);
21307
+ return daemonClient;
21308
+ }
21309
+ } catch {
21310
+ }
21311
+ const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
21312
+ await initStore2({ lightweight: true });
21313
+ return getClient2();
21314
+ }
21315
+ var init_fast_db_init = __esm({
21316
+ "src/bin/fast-db-init.ts"() {
21317
+ "use strict";
21318
+ }
21319
+ });
21320
+
20994
21321
  // src/lib/db-backup.ts
20995
21322
  var db_backup_exports = {};
20996
21323
  __export(db_backup_exports, {
@@ -21002,33 +21329,33 @@ __export(db_backup_exports, {
21002
21329
  listBackups: () => listBackups,
21003
21330
  rotateBackups: () => rotateBackups
21004
21331
  });
21005
- import { copyFileSync, existsSync as existsSync30, mkdirSync as mkdirSync12, readdirSync as readdirSync10, unlinkSync as unlinkSync10, statSync as statSync5 } from "fs";
21006
- import path35 from "path";
21332
+ import { copyFileSync, existsSync as existsSync31, mkdirSync as mkdirSync13, readdirSync as readdirSync10, unlinkSync as unlinkSync10, statSync as statSync5 } from "fs";
21333
+ import path36 from "path";
21007
21334
  function findActiveDb() {
21008
21335
  for (const name of DB_NAMES) {
21009
- const p = path35.join(EXE_AI_DIR, name);
21010
- if (existsSync30(p)) return p;
21336
+ const p = path36.join(EXE_AI_DIR, name);
21337
+ if (existsSync31(p)) return p;
21011
21338
  }
21012
21339
  return null;
21013
21340
  }
21014
21341
  function createBackup(reason = "manual") {
21015
21342
  const dbPath = findActiveDb();
21016
21343
  if (!dbPath) return null;
21017
- mkdirSync12(BACKUP_DIR, { recursive: true });
21018
- const dbName = path35.basename(dbPath, ".db");
21344
+ mkdirSync13(BACKUP_DIR, { recursive: true });
21345
+ const dbName = path36.basename(dbPath, ".db");
21019
21346
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
21020
21347
  const backupName = `${dbName}-${reason}-${timestamp}.db`;
21021
- const backupPath = path35.join(BACKUP_DIR, backupName);
21348
+ const backupPath = path36.join(BACKUP_DIR, backupName);
21022
21349
  copyFileSync(dbPath, backupPath);
21023
21350
  const walPath = dbPath + "-wal";
21024
- if (existsSync30(walPath)) {
21351
+ if (existsSync31(walPath)) {
21025
21352
  try {
21026
21353
  copyFileSync(walPath, backupPath + "-wal");
21027
21354
  } catch {
21028
21355
  }
21029
21356
  }
21030
21357
  const shmPath = dbPath + "-shm";
21031
- if (existsSync30(shmPath)) {
21358
+ if (existsSync31(shmPath)) {
21032
21359
  try {
21033
21360
  copyFileSync(shmPath, backupPath + "-shm");
21034
21361
  } catch {
@@ -21037,14 +21364,14 @@ function createBackup(reason = "manual") {
21037
21364
  return backupPath;
21038
21365
  }
21039
21366
  function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
21040
- if (!existsSync30(BACKUP_DIR)) return 0;
21367
+ if (!existsSync31(BACKUP_DIR)) return 0;
21041
21368
  const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1e3;
21042
21369
  let deleted = 0;
21043
21370
  try {
21044
21371
  const files = readdirSync10(BACKUP_DIR);
21045
21372
  for (const file of files) {
21046
21373
  if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
21047
- const filePath = path35.join(BACKUP_DIR, file);
21374
+ const filePath = path36.join(BACKUP_DIR, file);
21048
21375
  try {
21049
21376
  const stat = statSync5(filePath);
21050
21377
  if (stat.mtimeMs < cutoff) {
@@ -21059,11 +21386,11 @@ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
21059
21386
  return deleted;
21060
21387
  }
21061
21388
  function listBackups() {
21062
- if (!existsSync30(BACKUP_DIR)) return [];
21389
+ if (!existsSync31(BACKUP_DIR)) return [];
21063
21390
  try {
21064
21391
  const files = readdirSync10(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
21065
21392
  return files.map((name) => {
21066
- const p = path35.join(BACKUP_DIR, name);
21393
+ const p = path36.join(BACKUP_DIR, name);
21067
21394
  const stat = statSync5(p);
21068
21395
  return { path: p, name, size: stat.size, date: stat.mtime };
21069
21396
  }).sort((a, b) => b.date.getTime() - a.date.getTime());
@@ -21088,7 +21415,7 @@ var init_db_backup = __esm({
21088
21415
  "src/lib/db-backup.ts"() {
21089
21416
  "use strict";
21090
21417
  init_config();
21091
- BACKUP_DIR = path35.join(EXE_AI_DIR, "backups");
21418
+ BACKUP_DIR = path36.join(EXE_AI_DIR, "backups");
21092
21419
  DEFAULT_KEEP_DAYS = 3;
21093
21420
  DB_NAMES = ["memories.db", "exe-mem.db", "exe-os.db", "exe.db"];
21094
21421
  }
@@ -21096,9 +21423,9 @@ var init_db_backup = __esm({
21096
21423
 
21097
21424
  // src/bin/exe-doctor.ts
21098
21425
  import os16 from "os";
21099
- import { existsSync as existsSync31, readFileSync as readFileSync23 } from "fs";
21426
+ import { existsSync as existsSync32, readFileSync as readFileSync24 } from "fs";
21100
21427
  import { spawn as spawn2 } from "child_process";
21101
- import path36 from "path";
21428
+ import path37 from "path";
21102
21429
  import { randomUUID as randomUUID5 } from "crypto";
21103
21430
  function parseFlags(argv) {
21104
21431
  const flags = { fix: false, dryRun: false, verbose: false, conflicts: false };
@@ -21250,7 +21577,7 @@ async function auditOrphanedProjects(client) {
21250
21577
  for (const row of result.rows) {
21251
21578
  const name = row.project_name;
21252
21579
  const count = Number(row.cnt);
21253
- const exists = existsSync31(path36.join(home, name)) || existsSync31(path36.join(home, "..", name)) || existsSync31(path36.join(process.cwd(), "..", name));
21580
+ const exists = existsSync32(path37.join(home, name)) || existsSync32(path37.join(home, "..", name)) || existsSync32(path37.join(process.cwd(), "..", name));
21254
21581
  if (!exists) {
21255
21582
  orphans.push({ project_name: name, count });
21256
21583
  }
@@ -21258,18 +21585,18 @@ async function auditOrphanedProjects(client) {
21258
21585
  return orphans;
21259
21586
  }
21260
21587
  function auditHookHealth() {
21261
- const logPath = path36.join(
21588
+ const logPath = path37.join(
21262
21589
  process.env.HOME ?? process.env.USERPROFILE ?? "",
21263
21590
  ".exe-os",
21264
21591
  "logs",
21265
21592
  "hooks.log"
21266
21593
  );
21267
- if (!existsSync31(logPath)) {
21594
+ if (!existsSync32(logPath)) {
21268
21595
  return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
21269
21596
  }
21270
21597
  let content;
21271
21598
  try {
21272
- content = readFileSync23(logPath, "utf-8");
21599
+ content = readFileSync24(logPath, "utf-8");
21273
21600
  } catch {
21274
21601
  return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
21275
21602
  }
@@ -21297,6 +21624,121 @@ function auditHookHealth() {
21297
21624
  const topPatterns = [...patternCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => ({ pattern, count }));
21298
21625
  return { logExists: true, totalLines, errorsLastHour, topPatterns };
21299
21626
  }
21627
+ function safeReadJson(filePath) {
21628
+ if (!existsSync32(filePath)) return null;
21629
+ try {
21630
+ return JSON.parse(readFileSync24(filePath, "utf-8"));
21631
+ } catch {
21632
+ return null;
21633
+ }
21634
+ }
21635
+ function collectHookCommandsFromClaudeSettings(settings) {
21636
+ const hooks = settings?.hooks;
21637
+ if (!hooks || typeof hooks !== "object") return [];
21638
+ const commands = [];
21639
+ for (const [event, groups] of Object.entries(hooks)) {
21640
+ if (!Array.isArray(groups)) continue;
21641
+ for (const group of groups) {
21642
+ const hooksForGroup = group?.hooks;
21643
+ if (!Array.isArray(hooksForGroup)) continue;
21644
+ for (const hook of hooksForGroup) {
21645
+ const command = hook?.command;
21646
+ if (typeof command === "string") commands.push({ event, command });
21647
+ }
21648
+ }
21649
+ }
21650
+ return commands;
21651
+ }
21652
+ function collectHookCommandsFromCodexHooks(config2) {
21653
+ const commands = [];
21654
+ if (!config2 || typeof config2 !== "object") return commands;
21655
+ const root = config2;
21656
+ for (const [event, value] of Object.entries(root)) {
21657
+ const entries = Array.isArray(value) ? value : value && typeof value === "object" ? Object.values(value) : [];
21658
+ for (const entry of entries) {
21659
+ if (typeof entry === "string") commands.push({ event, command: entry });
21660
+ const command = entry?.command;
21661
+ if (typeof command === "string") commands.push({ event, command });
21662
+ }
21663
+ }
21664
+ return commands;
21665
+ }
21666
+ function buildHookOwnershipIssues(runtime, commands) {
21667
+ const issues = [];
21668
+ for (const marker of LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS) {
21669
+ const count = commands.filter((cmd) => cmd.command.includes(marker)).length;
21670
+ if (count > 0) {
21671
+ issues.push({
21672
+ runtime,
21673
+ event: "PostToolUse",
21674
+ marker,
21675
+ count,
21676
+ message: `Legacy split PostToolUse hook still installed: ${marker}`
21677
+ });
21678
+ }
21679
+ }
21680
+ for (const entry of EXE_HOOK_MANIFEST.filter((hook) => hook.runtimes.includes(runtime))) {
21681
+ const matching = commands.filter((cmd) => cmd.command.includes(entry.commandMarker));
21682
+ if (matching.length > 1) {
21683
+ issues.push({
21684
+ runtime,
21685
+ event: entry.event,
21686
+ marker: entry.commandMarker,
21687
+ count: matching.length,
21688
+ message: `Duplicate exe-os hook owner for ${entry.event}: ${entry.commandMarker}`
21689
+ });
21690
+ }
21691
+ for (const cmd of matching) {
21692
+ if (cmd.event !== entry.event) {
21693
+ issues.push({
21694
+ runtime,
21695
+ event: cmd.event,
21696
+ marker: entry.commandMarker,
21697
+ count: 1,
21698
+ message: `exe-os hook ${entry.commandMarker} is registered under ${cmd.event}, expected ${entry.event}`
21699
+ });
21700
+ }
21701
+ }
21702
+ }
21703
+ for (const cmd of commands) {
21704
+ if (!cmd.command.includes("dist/hooks/")) continue;
21705
+ if (!cmd.command.includes("exe-os")) continue;
21706
+ const entry = manifestEntryForCommand(cmd.command);
21707
+ const isLegacy = LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS.some((marker) => cmd.command.includes(marker));
21708
+ if (!entry && !isLegacy) {
21709
+ issues.push({
21710
+ runtime,
21711
+ event: cmd.event,
21712
+ marker: "dist/hooks/",
21713
+ count: 1,
21714
+ message: `Unknown exe-os hook command not present in ownership manifest: ${cmd.command.slice(0, 160)}`
21715
+ });
21716
+ }
21717
+ }
21718
+ return issues;
21719
+ }
21720
+ function auditHookOwnership() {
21721
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
21722
+ const checkedFiles = [];
21723
+ const issues = [];
21724
+ const claudeSettingsPath = path37.join(home, ".claude", "settings.json");
21725
+ const claudeSettings = safeReadJson(claudeSettingsPath);
21726
+ if (claudeSettings) {
21727
+ checkedFiles.push(claudeSettingsPath);
21728
+ issues.push(...buildHookOwnershipIssues("claude", collectHookCommandsFromClaudeSettings(claudeSettings)));
21729
+ }
21730
+ const codexHooksPath = path37.join(home, ".codex", "hooks.json");
21731
+ const codexHooks = safeReadJson(codexHooksPath);
21732
+ if (codexHooks) {
21733
+ checkedFiles.push(codexHooksPath);
21734
+ issues.push(...buildHookOwnershipIssues("codex", collectHookCommandsFromCodexHooks(codexHooks)));
21735
+ }
21736
+ return {
21737
+ checkedFiles,
21738
+ issues,
21739
+ staleLegacyHooks: issues.filter((issue) => issue.message.includes("Legacy split"))
21740
+ };
21741
+ }
21300
21742
  async function auditShards() {
21301
21743
  try {
21302
21744
  const { auditShardHealth: auditShardHealth2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -21312,16 +21754,33 @@ async function auditShards() {
21312
21754
  return { total: 0, ok: 0, unreadable: 0, archived: 0, unreadableNames: [] };
21313
21755
  }
21314
21756
  }
21757
+ async function auditKeyHealth() {
21758
+ try {
21759
+ const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
21760
+ const { getKeyBackupStatus: getKeyBackupStatus2 } = await Promise.resolve().then(() => (init_key_backup_status(), key_backup_status_exports));
21761
+ const key = await getMasterKey2();
21762
+ const backup = getKeyBackupStatus2();
21763
+ return {
21764
+ masterKeyPresent: Boolean(key),
21765
+ recoveryBackupMarked: backup.exists,
21766
+ recoveryBackupConfirmedAt: backup.confirmedAt,
21767
+ recoveryBackupSource: backup.source
21768
+ };
21769
+ } catch {
21770
+ return { masterKeyPresent: false, recoveryBackupMarked: false };
21771
+ }
21772
+ }
21315
21773
  async function runAudit(client, flags) {
21316
21774
  const runConflicts = flags.conflicts || process.env.EXE_AUDIT_CONFLICTS === "1";
21317
- const [stats, nullVectors, duplicates, bloated, fts, orphanedProjects, shards] = await Promise.all([
21775
+ const [stats, nullVectors, duplicates, bloated, fts, orphanedProjects, shards, keyHealth] = await Promise.all([
21318
21776
  auditStats(client, flags),
21319
21777
  auditNullVectors(client, flags),
21320
21778
  auditDuplicates(client, flags),
21321
21779
  auditBloated(client, flags),
21322
21780
  auditFts(client),
21323
21781
  auditOrphanedProjects(client),
21324
- auditShards()
21782
+ auditShards(),
21783
+ auditKeyHealth()
21325
21784
  ]);
21326
21785
  let conflicts;
21327
21786
  if (runConflicts) {
@@ -21341,7 +21800,8 @@ async function runAudit(client, flags) {
21341
21800
  }
21342
21801
  const duplicateCount = duplicates.reduce((sum, d) => sum + d.delete_ids.length, 0);
21343
21802
  const hookHealth = auditHookHealth();
21344
- return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth, shards };
21803
+ const hookOwnership = auditHookOwnership();
21804
+ return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth, hookOwnership, shards, keyHealth };
21345
21805
  }
21346
21806
  function indicator(value, warn) {
21347
21807
  if (value === 0) return "\u{1F7E2}";
@@ -21389,6 +21849,15 @@ function formatReport(report, flags) {
21389
21849
  lines.push(`${indicator(report.bloated.length, 20)} Bloated (>5KB): ${fmtNum(report.bloated.length)} / ${fmtNum(s.total)} (${pct(report.bloated.length, s.total)})`);
21390
21850
  const ftsIndicator = report.fts.inSync ? "\u{1F7E2}" : "\u{1F534}";
21391
21851
  lines.push(`${ftsIndicator} FTS index: ${report.fts.inSync ? "in sync" : "OUT OF SYNC"} (${fmtNum(report.fts.memoryCount)} / ${fmtNum(report.fts.ftsCount)})`);
21852
+ const kh = report.keyHealth;
21853
+ if (!kh.masterKeyPresent) {
21854
+ lines.push("\u{1F534} Recovery key: master key missing \u2014 import phrase or run setup before syncing");
21855
+ } else if (!kh.recoveryBackupMarked) {
21856
+ lines.push("\u{1F534} Recovery backup: not confirmed \u2014 run `exe-os link export --local-terminal-only` and save the 24-word phrase");
21857
+ } else {
21858
+ const suffix = kh.recoveryBackupConfirmedAt ? ` (${kh.recoveryBackupConfirmedAt.slice(0, 10)}${kh.recoveryBackupSource ? ` via ${kh.recoveryBackupSource}` : ""})` : "";
21859
+ lines.push(`\u{1F7E2} Recovery backup: confirmed${suffix}`);
21860
+ }
21392
21861
  if (report.orphanedProjects.length > 0) {
21393
21862
  const orphanList = report.orphanedProjects.map((o) => `${o.project_name} \u2014 ${o.count} memories`).join(", ");
21394
21863
  lines.push(`\u2139\uFE0F Orphaned projects: ${report.orphanedProjects.length} (${orphanList})`);
@@ -21420,6 +21889,15 @@ function formatReport(report, flags) {
21420
21889
  lines.push(` ${p.count}x: ${p.pattern}`);
21421
21890
  }
21422
21891
  }
21892
+ const ho = report.hookOwnership;
21893
+ if (ho.issues.length === 0) {
21894
+ lines.push(`\u{1F7E2} Hook ownership: ${ho.checkedFiles.length > 0 ? "manifest clean" : "no local hook config found"}`);
21895
+ } else {
21896
+ lines.push(`\u{1F534} Hook ownership: ${fmtNum(ho.issues.length)} issue(s)`);
21897
+ for (const issue of ho.issues.slice(0, 5)) {
21898
+ lines.push(` [${issue.runtime}/${issue.event}] ${issue.message}`);
21899
+ }
21900
+ }
21423
21901
  const sh = report.shards;
21424
21902
  if (sh.total > 0) {
21425
21903
  if (sh.unreadable === 0) {
@@ -21489,6 +21967,9 @@ function formatReport(report, flags) {
21489
21967
  if (report.conflicts.superseded > 0) {
21490
21968
  recs.push(`${fmtNum(report.conflicts.superseded)} superseded memories can be deactivated`);
21491
21969
  }
21970
+ if (report.hookOwnership.issues.length > 0) {
21971
+ recs.push(`Run exe-os install to refresh hook config; remove stale exe-os hook commands if they remain`);
21972
+ }
21492
21973
  if (recs.length > 0) {
21493
21974
  lines.push("Recommendations:");
21494
21975
  for (const r of recs) {
@@ -21510,7 +21991,7 @@ async function fixNullVectors() {
21510
21991
  }
21511
21992
  }
21512
21993
  const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
21513
- const backfillPath = path36.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
21994
+ const backfillPath = path37.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
21514
21995
  return new Promise((resolve, reject) => {
21515
21996
  const child = spawn2("node", [backfillPath], { stdio: "inherit" });
21516
21997
  if (child.pid) registerWorkerPid2(child.pid);
@@ -21622,8 +22103,8 @@ function splitAtSentences(text3, maxChunkSize) {
21622
22103
  }
21623
22104
  async function main(argv = process.argv.slice(2)) {
21624
22105
  const flags = parseFlags(argv);
21625
- await initStore();
21626
- const client = getClient();
22106
+ const { fastDbInit: fastDbInit2 } = await Promise.resolve().then(() => (init_fast_db_init(), fast_db_init_exports));
22107
+ const client = await fastDbInit2();
21627
22108
  const report = await runAudit(client, flags);
21628
22109
  console.log(formatReport(report, flags));
21629
22110
  if (flags.fix || flags.dryRun) {
@@ -21683,10 +22164,9 @@ ${mode} Complete.`);
21683
22164
  var init_exe_doctor = __esm({
21684
22165
  "src/bin/exe-doctor.ts"() {
21685
22166
  "use strict";
21686
- init_store();
21687
- init_database();
21688
22167
  init_is_main();
21689
22168
  init_conflict_detector();
22169
+ init_runtime_hook_manifest();
21690
22170
  if (isMainModule(import.meta.url) && (process.argv[1] ?? "").includes("exe-doctor")) {
21691
22171
  main().catch((err) => {
21692
22172
  console.error(err instanceof Error ? err.message : String(err));
@@ -21922,8 +22402,8 @@ __export(crdt_sync_exports, {
21922
22402
  rebuildFromDb: () => rebuildFromDb
21923
22403
  });
21924
22404
  import * as Y from "yjs";
21925
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync16, existsSync as existsSync32, mkdirSync as mkdirSync13, unlinkSync as unlinkSync11 } from "fs";
21926
- import path37 from "path";
22405
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync17, existsSync as existsSync33, mkdirSync as mkdirSync14, unlinkSync as unlinkSync11 } from "fs";
22406
+ import path38 from "path";
21927
22407
  import { homedir as homedir5 } from "os";
21928
22408
  function getStatePath() {
21929
22409
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -21935,9 +22415,9 @@ function initCrdtDoc() {
21935
22415
  if (doc) return doc;
21936
22416
  doc = new Y.Doc();
21937
22417
  const sp = getStatePath();
21938
- if (existsSync32(sp)) {
22418
+ if (existsSync33(sp)) {
21939
22419
  try {
21940
- const state = readFileSync24(sp);
22420
+ const state = readFileSync25(sp);
21941
22421
  Y.applyUpdate(doc, new Uint8Array(state));
21942
22422
  } catch {
21943
22423
  console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
@@ -22079,10 +22559,10 @@ function persistState() {
22079
22559
  if (!doc) return;
22080
22560
  try {
22081
22561
  const sp = getStatePath();
22082
- const dir = path37.dirname(sp);
22083
- if (!existsSync32(dir)) mkdirSync13(dir, { recursive: true });
22562
+ const dir = path38.dirname(sp);
22563
+ if (!existsSync33(dir)) mkdirSync14(dir, { recursive: true });
22084
22564
  const state = Y.encodeStateAsUpdate(doc);
22085
- writeFileSync16(sp, Buffer.from(state));
22565
+ writeFileSync17(sp, Buffer.from(state));
22086
22566
  } catch {
22087
22567
  }
22088
22568
  }
@@ -22123,7 +22603,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
22123
22603
  var init_crdt_sync = __esm({
22124
22604
  "src/lib/crdt-sync.ts"() {
22125
22605
  "use strict";
22126
- DEFAULT_STATE_PATH = path37.join(homedir5(), ".exe-os", "crdt-state.bin");
22606
+ DEFAULT_STATE_PATH = path38.join(homedir5(), ".exe-os", "crdt-state.bin");
22127
22607
  _statePathOverride = null;
22128
22608
  doc = null;
22129
22609
  }
@@ -22132,8 +22612,10 @@ var init_crdt_sync = __esm({
22132
22612
  // src/lib/cloud-sync.ts
22133
22613
  var cloud_sync_exports = {};
22134
22614
  __export(cloud_sync_exports, {
22615
+ CLOUD_RELINK_REQUIRED_MESSAGE: () => CLOUD_RELINK_REQUIRED_MESSAGE,
22135
22616
  assertSecureEndpoint: () => assertSecureEndpoint,
22136
22617
  buildRosterBlob: () => buildRosterBlob,
22618
+ clearCloudRelinkRequired: () => clearCloudRelinkRequired,
22137
22619
  cloudPull: () => cloudPull,
22138
22620
  cloudPullBehaviors: () => cloudPullBehaviors,
22139
22621
  cloudPullBlob: () => cloudPullBlob,
@@ -22153,53 +22635,66 @@ __export(cloud_sync_exports, {
22153
22635
  cloudPushRoster: () => cloudPushRoster,
22154
22636
  cloudPushTasks: () => cloudPushTasks,
22155
22637
  cloudSync: () => cloudSync,
22638
+ getCloudRelinkRequired: () => getCloudRelinkRequired,
22156
22639
  mergeConfig: () => mergeConfig,
22157
22640
  mergeRosterFromRemote: () => mergeRosterFromRemote,
22158
22641
  pushToPostgres: () => pushToPostgres,
22159
22642
  recordRosterDeletion: () => recordRosterDeletion
22160
22643
  });
22161
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync17, existsSync as existsSync33, readdirSync as readdirSync11, mkdirSync as mkdirSync14, appendFileSync as appendFileSync3, unlinkSync as unlinkSync12, openSync as openSync2, closeSync as closeSync2, statSync as statSync6 } from "fs";
22644
+ import { readFileSync as readFileSync26, writeFileSync as writeFileSync18, existsSync as existsSync34, readdirSync as readdirSync11, mkdirSync as mkdirSync15, appendFileSync as appendFileSync3, unlinkSync as unlinkSync12, openSync as openSync2, closeSync as closeSync2, statSync as statSync6 } from "fs";
22162
22645
  import crypto16 from "crypto";
22163
- import path38 from "path";
22646
+ import path39 from "path";
22164
22647
  import { homedir as homedir6 } from "os";
22165
22648
  function sqlSafe(v) {
22166
22649
  return v === void 0 ? null : v;
22167
22650
  }
22168
22651
  function logError(msg) {
22169
22652
  try {
22170
- const logPath = path38.join(homedir6(), ".exe-os", "workers.log");
22653
+ const logPath = path39.join(homedir6(), ".exe-os", "workers.log");
22171
22654
  appendFileSync3(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
22172
22655
  `);
22173
22656
  } catch {
22174
22657
  }
22175
22658
  }
22659
+ function isTruthyEnv2(value) {
22660
+ return /^(1|true|yes|on)$/i.test(value ?? "");
22661
+ }
22176
22662
  function loadPgClient() {
22177
22663
  if (_pgFailed) return null;
22178
- const postgresUrl = process.env.DATABASE_URL;
22179
- const configPath = path38.join(EXE_AI_DIR, "config.json");
22664
+ const configPath = path39.join(EXE_AI_DIR, "config.json");
22180
22665
  let cloudPostgresUrl;
22666
+ let configEnabled = false;
22181
22667
  try {
22182
- if (existsSync33(configPath)) {
22183
- const cfg = JSON.parse(readFileSync25(configPath, "utf8"));
22668
+ if (existsSync34(configPath)) {
22669
+ const cfg = JSON.parse(readFileSync26(configPath, "utf8"));
22184
22670
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
22185
- if (cfg.cloud?.syncToPostgres === false) {
22186
- _pgFailed = true;
22187
- return null;
22188
- }
22671
+ configEnabled = cfg.cloud?.syncToPostgres === true;
22189
22672
  }
22190
22673
  } catch {
22191
22674
  }
22192
- const url = postgresUrl || cloudPostgresUrl;
22675
+ const envEnabled = isTruthyEnv2(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
22676
+ if (!envEnabled && !configEnabled) {
22677
+ return null;
22678
+ }
22679
+ const url = process.env.DATABASE_URL || cloudPostgresUrl;
22193
22680
  if (!url) {
22194
22681
  _pgFailed = true;
22195
22682
  return null;
22196
22683
  }
22197
22684
  if (!_pgPromise) {
22198
22685
  _pgPromise = (async () => {
22686
+ if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
22199
22687
  const { createRequire: createRequire7 } = await import("module");
22200
22688
  const { pathToFileURL: pathToFileURL7 } = await import("url");
22201
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path38.join(homedir6(), "exe-db");
22202
- const req = createRequire7(path38.join(exeDbRoot, "package.json"));
22689
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
22690
+ if (explicitPath) {
22691
+ const mod2 = await import(pathToFileURL7(explicitPath).href);
22692
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
22693
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
22694
+ return new Ctor2();
22695
+ }
22696
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path39.join(homedir6(), "exe-db");
22697
+ const req = createRequire7(path39.join(exeDbRoot, "package.json"));
22203
22698
  const entry = req.resolve("@prisma/client");
22204
22699
  const mod = await import(pathToFileURL7(entry).href);
22205
22700
  const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
@@ -22244,18 +22739,18 @@ async function withRosterLock(fn) {
22244
22739
  try {
22245
22740
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
22246
22741
  closeSync2(fd);
22247
- writeFileSync17(ROSTER_LOCK_PATH, String(Date.now()));
22742
+ writeFileSync18(ROSTER_LOCK_PATH, String(Date.now()));
22248
22743
  } catch (err) {
22249
22744
  if (err.code === "EEXIST") {
22250
22745
  try {
22251
- const ts2 = parseInt(readFileSync25(ROSTER_LOCK_PATH, "utf-8"), 10);
22746
+ const ts2 = parseInt(readFileSync26(ROSTER_LOCK_PATH, "utf-8"), 10);
22252
22747
  if (Date.now() - ts2 < LOCK_STALE_MS) {
22253
22748
  throw new Error("Roster merge already in progress \u2014 another sync is running");
22254
22749
  }
22255
22750
  unlinkSync12(ROSTER_LOCK_PATH);
22256
22751
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
22257
22752
  closeSync2(fd);
22258
- writeFileSync17(ROSTER_LOCK_PATH, String(Date.now()));
22753
+ writeFileSync18(ROSTER_LOCK_PATH, String(Date.now()));
22259
22754
  } catch (retryErr) {
22260
22755
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
22261
22756
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -22374,6 +22869,24 @@ async function cloudPull(sinceVersion, config2) {
22374
22869
  return { records: [], maxVersion: sinceVersion };
22375
22870
  }
22376
22871
  }
22872
+ async function getCloudRelinkRequired(client = getClient()) {
22873
+ try {
22874
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
22875
+ const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
22876
+ return String(relink.rows[0]?.value ?? "") === "1";
22877
+ } catch {
22878
+ return false;
22879
+ }
22880
+ }
22881
+ async function clearCloudRelinkRequired(client = getClient()) {
22882
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
22883
+ await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relink_required', '0')");
22884
+ await client.execute({
22885
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relinked_at', ?)",
22886
+ args: [(/* @__PURE__ */ new Date()).toISOString()]
22887
+ });
22888
+ await client.execute("DELETE FROM sync_meta WHERE key IN ('last_cloud_pull_version', 'last_cloud_push_version')");
22889
+ }
22377
22890
  async function cloudSync(config2) {
22378
22891
  if (!isSyncCryptoInitialized()) {
22379
22892
  try {
@@ -22394,6 +22907,12 @@ async function cloudSync(config2) {
22394
22907
  } catch {
22395
22908
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
22396
22909
  }
22910
+ try {
22911
+ if (await getCloudRelinkRequired(client)) throw new Error(CLOUD_RELINK_REQUIRED_MESSAGE);
22912
+ } catch (err) {
22913
+ const msg = err instanceof Error ? err.message : String(err);
22914
+ if (msg.includes("Paused after key rotation")) throw err;
22915
+ }
22397
22916
  try {
22398
22917
  const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
22399
22918
  await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
@@ -22639,8 +23158,8 @@ async function cloudSync(config2) {
22639
23158
  try {
22640
23159
  const employees = await loadEmployees();
22641
23160
  rosterResult.employees = employees.length;
22642
- const idDir = path38.join(EXE_AI_DIR, "identity");
22643
- if (existsSync33(idDir)) {
23161
+ const idDir = path39.join(EXE_AI_DIR, "identity");
23162
+ if (existsSync34(idDir)) {
22644
23163
  rosterResult.identities = readdirSync11(idDir).filter((f) => f.endsWith(".md")).length;
22645
23164
  }
22646
23165
  } catch {
@@ -22653,7 +23172,7 @@ async function cloudSync(config2) {
22653
23172
  const backupSize = statSync6(latestBackup).size;
22654
23173
  const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
22655
23174
  if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
22656
- const backupData = readFileSync25(latestBackup);
23175
+ const backupData = readFileSync26(latestBackup);
22657
23176
  const deviceId = loadDeviceId() ?? "unknown";
22658
23177
  const encrypted = encryptSyncBlob(backupData);
22659
23178
  const backupRes = await fetchWithRetry(`${config2.endpoint}/sync/push-db-backup`, {
@@ -22661,7 +23180,7 @@ async function cloudSync(config2) {
22661
23180
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${config2.apiKey}` },
22662
23181
  body: JSON.stringify({
22663
23182
  device_id: deviceId,
22664
- filename: path38.basename(latestBackup),
23183
+ filename: path39.basename(latestBackup),
22665
23184
  blob: encrypted,
22666
23185
  size: backupData.length
22667
23186
  })
@@ -22689,56 +23208,56 @@ async function cloudSync(config2) {
22689
23208
  function recordRosterDeletion(name) {
22690
23209
  let deletions = [];
22691
23210
  try {
22692
- if (existsSync33(ROSTER_DELETIONS_PATH)) {
22693
- deletions = JSON.parse(readFileSync25(ROSTER_DELETIONS_PATH, "utf-8"));
23211
+ if (existsSync34(ROSTER_DELETIONS_PATH)) {
23212
+ deletions = JSON.parse(readFileSync26(ROSTER_DELETIONS_PATH, "utf-8"));
22694
23213
  }
22695
23214
  } catch {
22696
23215
  }
22697
23216
  if (!deletions.includes(name)) deletions.push(name);
22698
- writeFileSync17(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
23217
+ writeFileSync18(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
22699
23218
  }
22700
23219
  function consumeRosterDeletions() {
22701
23220
  try {
22702
- if (!existsSync33(ROSTER_DELETIONS_PATH)) return [];
22703
- const deletions = JSON.parse(readFileSync25(ROSTER_DELETIONS_PATH, "utf-8"));
22704
- writeFileSync17(ROSTER_DELETIONS_PATH, "[]");
23221
+ if (!existsSync34(ROSTER_DELETIONS_PATH)) return [];
23222
+ const deletions = JSON.parse(readFileSync26(ROSTER_DELETIONS_PATH, "utf-8"));
23223
+ writeFileSync18(ROSTER_DELETIONS_PATH, "[]");
22705
23224
  return deletions;
22706
23225
  } catch {
22707
23226
  return [];
22708
23227
  }
22709
23228
  }
22710
23229
  function buildRosterBlob(paths) {
22711
- const rosterPath = paths?.rosterPath ?? path38.join(EXE_AI_DIR, "exe-employees.json");
22712
- const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
22713
- const configPath = paths?.configPath ?? path38.join(EXE_AI_DIR, "config.json");
23230
+ const rosterPath = paths?.rosterPath ?? path39.join(EXE_AI_DIR, "exe-employees.json");
23231
+ const identityDir = paths?.identityDir ?? path39.join(EXE_AI_DIR, "identity");
23232
+ const configPath = paths?.configPath ?? path39.join(EXE_AI_DIR, "config.json");
22714
23233
  let roster = [];
22715
- if (existsSync33(rosterPath)) {
23234
+ if (existsSync34(rosterPath)) {
22716
23235
  try {
22717
- roster = JSON.parse(readFileSync25(rosterPath, "utf-8"));
23236
+ roster = JSON.parse(readFileSync26(rosterPath, "utf-8"));
22718
23237
  } catch {
22719
23238
  }
22720
23239
  }
22721
23240
  const identities = {};
22722
- if (existsSync33(identityDir)) {
23241
+ if (existsSync34(identityDir)) {
22723
23242
  for (const file of readdirSync11(identityDir).filter((f) => f.endsWith(".md"))) {
22724
23243
  try {
22725
- identities[file] = readFileSync25(path38.join(identityDir, file), "utf-8");
23244
+ identities[file] = readFileSync26(path39.join(identityDir, file), "utf-8");
22726
23245
  } catch {
22727
23246
  }
22728
23247
  }
22729
23248
  }
22730
23249
  let config2;
22731
- if (existsSync33(configPath)) {
23250
+ if (existsSync34(configPath)) {
22732
23251
  try {
22733
- config2 = JSON.parse(readFileSync25(configPath, "utf-8"));
23252
+ config2 = JSON.parse(readFileSync26(configPath, "utf-8"));
22734
23253
  } catch {
22735
23254
  }
22736
23255
  }
22737
23256
  let agentConfig;
22738
- const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
22739
- if (existsSync33(agentConfigPath)) {
23257
+ const agentConfigPath = path39.join(EXE_AI_DIR, "agent-config.json");
23258
+ if (existsSync34(agentConfigPath)) {
22740
23259
  try {
22741
- agentConfig = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
23260
+ agentConfig = JSON.parse(readFileSync26(agentConfigPath, "utf-8"));
22742
23261
  } catch {
22743
23262
  }
22744
23263
  }
@@ -22814,24 +23333,24 @@ async function cloudPullRoster(config2) {
22814
23333
  }
22815
23334
  }
22816
23335
  function mergeConfig(remoteConfig, configPath) {
22817
- const cfgPath = configPath ?? path38.join(EXE_AI_DIR, "config.json");
23336
+ const cfgPath = configPath ?? path39.join(EXE_AI_DIR, "config.json");
22818
23337
  let local = {};
22819
- if (existsSync33(cfgPath)) {
23338
+ if (existsSync34(cfgPath)) {
22820
23339
  try {
22821
- local = JSON.parse(readFileSync25(cfgPath, "utf-8"));
23340
+ local = JSON.parse(readFileSync26(cfgPath, "utf-8"));
22822
23341
  } catch {
22823
23342
  }
22824
23343
  }
22825
23344
  const merged = { ...remoteConfig, ...local };
22826
- const dir = path38.dirname(cfgPath);
23345
+ const dir = path39.dirname(cfgPath);
22827
23346
  ensurePrivateDirSync(dir);
22828
- writeFileSync17(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
23347
+ writeFileSync18(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
22829
23348
  enforcePrivateFileSync(cfgPath);
22830
23349
  }
22831
23350
  async function mergeRosterFromRemote(remote, paths) {
22832
23351
  return withRosterLock(async () => {
22833
23352
  const rosterPath = paths?.rosterPath ?? void 0;
22834
- const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
23353
+ const identityDir = paths?.identityDir ?? path39.join(EXE_AI_DIR, "identity");
22835
23354
  const localEmployees = await loadEmployees(rosterPath);
22836
23355
  const localNames = new Set(localEmployees.map((e) => e.name));
22837
23356
  let added = 0;
@@ -22852,15 +23371,15 @@ async function mergeRosterFromRemote(remote, paths) {
22852
23371
  ) ?? lookupKey;
22853
23372
  const remoteIdentity = remote.identities[matchedKey];
22854
23373
  if (remoteIdentity) {
22855
- if (!existsSync33(identityDir)) mkdirSync14(identityDir, { recursive: true });
22856
- const idPath = path38.join(identityDir, `${remoteEmp.name}.md`);
23374
+ if (!existsSync34(identityDir)) mkdirSync15(identityDir, { recursive: true });
23375
+ const idPath = path39.join(identityDir, `${remoteEmp.name}.md`);
22857
23376
  let localIdentity = null;
22858
23377
  try {
22859
- localIdentity = existsSync33(idPath) ? readFileSync25(idPath, "utf-8") : null;
23378
+ localIdentity = existsSync34(idPath) ? readFileSync26(idPath, "utf-8") : null;
22860
23379
  } catch {
22861
23380
  }
22862
23381
  if (localIdentity !== remoteIdentity) {
22863
- writeFileSync17(idPath, remoteIdentity, "utf-8");
23382
+ writeFileSync18(idPath, remoteIdentity, "utf-8");
22864
23383
  identitiesUpdated++;
22865
23384
  }
22866
23385
  }
@@ -22886,17 +23405,17 @@ async function mergeRosterFromRemote(remote, paths) {
22886
23405
  }
22887
23406
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
22888
23407
  try {
22889
- const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
23408
+ const agentConfigPath = path39.join(EXE_AI_DIR, "agent-config.json");
22890
23409
  let local = {};
22891
- if (existsSync33(agentConfigPath)) {
23410
+ if (existsSync34(agentConfigPath)) {
22892
23411
  try {
22893
- local = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
23412
+ local = JSON.parse(readFileSync26(agentConfigPath, "utf-8"));
22894
23413
  } catch {
22895
23414
  }
22896
23415
  }
22897
23416
  const merged = { ...remote.agentConfig, ...local };
22898
- ensurePrivateDirSync(path38.dirname(agentConfigPath));
22899
- writeFileSync17(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
23417
+ ensurePrivateDirSync(path39.dirname(agentConfigPath));
23418
+ writeFileSync18(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
22900
23419
  enforcePrivateFileSync(agentConfigPath);
22901
23420
  } catch {
22902
23421
  }
@@ -23321,7 +23840,7 @@ async function cloudPullDocuments(config2) {
23321
23840
  }
23322
23841
  return { pulled };
23323
23842
  }
23324
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS2, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
23843
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS2, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_RELINK_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
23325
23844
  var init_cloud_sync = __esm({
23326
23845
  "src/lib/cloud-sync.ts"() {
23327
23846
  "use strict";
@@ -23336,11 +23855,12 @@ var init_cloud_sync = __esm({
23336
23855
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
23337
23856
  FETCH_TIMEOUT_MS2 = 3e4;
23338
23857
  PUSH_BATCH_SIZE = 5e3;
23339
- ROSTER_LOCK_PATH = path38.join(EXE_AI_DIR, "roster-merge.lock");
23858
+ ROSTER_LOCK_PATH = path39.join(EXE_AI_DIR, "roster-merge.lock");
23340
23859
  LOCK_STALE_MS = 3e4;
23341
23860
  _pgPromise = null;
23342
23861
  _pgFailed = false;
23343
- ROSTER_DELETIONS_PATH = path38.join(EXE_AI_DIR, "roster-deletions.json");
23862
+ CLOUD_RELINK_REQUIRED_MESSAGE = "[cloud-sync] Paused after key rotation. Run `exe-os cloud relink --dry-run` for the safe relink checklist.";
23863
+ ROSTER_DELETIONS_PATH = path39.join(EXE_AI_DIR, "roster-deletions.json");
23344
23864
  }
23345
23865
  });
23346
23866
 
@@ -23411,6 +23931,143 @@ var init_cloud_sync2 = __esm({
23411
23931
  }
23412
23932
  });
23413
23933
 
23934
+ // src/lib/orchestration-phase.ts
23935
+ function normalizeOrchestrationPhase(input) {
23936
+ if (typeof input !== "string") return "phase_1_coo";
23937
+ const normalized = input.trim().toLowerCase().replace(/\s+/g, "_");
23938
+ if (["1", "phase1", "phase_1", "coo", "coo_mode", "chief_of_staff", "phase_1_coo"].includes(normalized)) {
23939
+ return "phase_1_coo";
23940
+ }
23941
+ if (["2", "phase2", "phase_2", "executives", "executive", "executive_bench", "phase_2_executives"].includes(normalized)) {
23942
+ return "phase_2_executives";
23943
+ }
23944
+ if (["3", "phase3", "phase_3", "parallel", "parallel_org", "parallel_execution", "phase_3_parallel_org"].includes(normalized)) {
23945
+ return "phase_3_parallel_org";
23946
+ }
23947
+ if (ORCHESTRATION_PHASES.includes(normalized)) return normalized;
23948
+ return "phase_1_coo";
23949
+ }
23950
+ function getOrchestrationPhaseInfo(phase) {
23951
+ return PHASE_INFO[normalizeOrchestrationPhase(phase)];
23952
+ }
23953
+ async function loadOrchestrationPhase() {
23954
+ const config2 = await loadConfig();
23955
+ return getOrchestrationPhaseInfo(config2.orchestration?.phase);
23956
+ }
23957
+ async function setOrchestrationPhase(phaseInput, setBy = "user") {
23958
+ const config2 = await loadConfig();
23959
+ const phase = normalizeOrchestrationPhase(phaseInput);
23960
+ config2.orchestration = {
23961
+ ...config2.orchestration ?? {},
23962
+ phase,
23963
+ phaseSetAt: (/* @__PURE__ */ new Date()).toISOString(),
23964
+ phaseSetBy: setBy
23965
+ };
23966
+ await saveConfig(config2);
23967
+ return getOrchestrationPhaseInfo(phase);
23968
+ }
23969
+ var ORCHESTRATION_PHASES, PHASE_INFO;
23970
+ var init_orchestration_phase = __esm({
23971
+ "src/lib/orchestration-phase.ts"() {
23972
+ "use strict";
23973
+ init_config();
23974
+ ORCHESTRATION_PHASES = [
23975
+ "phase_1_coo",
23976
+ "phase_2_executives",
23977
+ "phase_3_parallel_org"
23978
+ ];
23979
+ PHASE_INFO = {
23980
+ phase_1_coo: {
23981
+ phase: "phase_1_coo",
23982
+ shortLabel: "Phase 1 \u2014 COO mode",
23983
+ label: "Phase 1 \u2014 COO / Chief of Staff mode",
23984
+ focus: "building company context before delegation",
23985
+ nextSuggestion: "Unlock executives when technical, marketing, ops, legal, or finance work repeats."
23986
+ },
23987
+ phase_2_executives: {
23988
+ phase: "phase_2_executives",
23989
+ shortLabel: "Phase 2 \u2014 Executive bench",
23990
+ label: "Phase 2 \u2014 Executive bench",
23991
+ focus: "COO works with domain executives like CTO/CMO before specialist fan-out",
23992
+ nextSuggestion: "Unlock parallel execution when review gates, permissions, and workflows are ready."
23993
+ },
23994
+ phase_3_parallel_org: {
23995
+ phase: "phase_3_parallel_org",
23996
+ shortLabel: "Phase 3 \u2014 Parallel org",
23997
+ label: "Phase 3 \u2014 Parallel execution org",
23998
+ focus: "executives can delegate to specialists in parallel with review gates",
23999
+ nextSuggestion: "Keep review/CI/permission gates healthy; downgrade anytime if you want simpler COO-only mode."
24000
+ }
24001
+ };
24002
+ }
24003
+ });
24004
+
24005
+ // src/mcp/tools/orchestration-phase.ts
24006
+ import { z as z59 } from "zod";
24007
+ function render(info, prefix) {
24008
+ return [
24009
+ prefix,
24010
+ `## ${info.label}`,
24011
+ "",
24012
+ `- **Focus:** ${info.focus}`,
24013
+ `- **Suggestion:** ${info.nextSuggestion}`,
24014
+ "",
24015
+ "This is guidance, not a blocker. The user can switch phases anytime. Changing phase never overwrites role titles, identities, memories, tasks, or custom org design."
24016
+ ].filter(Boolean).join("\n");
24017
+ }
24018
+ function registerOrchestrationPhase(server) {
24019
+ server.registerTool(
24020
+ "orchestration_phase",
24021
+ {
24022
+ title: "Orchestration Phase",
24023
+ description: "View or change the customer-owned orchestration maturity phase. MCP parity for `exe-os org phase`, `exe-os org unlock executives`, and `exe-os org unlock parallel`. This is a recommendation layer, not a blocker; it never exposes recovery phrases or secrets.",
24024
+ inputSchema: {
24025
+ action: z59.enum(["status", "set", "unlock_executives", "unlock_parallel"]).default("status"),
24026
+ phase: z59.enum(["phase_1_coo", "phase_2_executives", "phase_3_parallel_org", "1", "2", "3"]).optional().describe("Phase to set when action='set'.")
24027
+ }
24028
+ },
24029
+ async ({ action = "status", phase }) => {
24030
+ try {
24031
+ if (action === "status") {
24032
+ const info2 = await loadOrchestrationPhase();
24033
+ return { content: [{ type: "text", text: render(info2) }] };
24034
+ }
24035
+ if (action === "unlock_executives") {
24036
+ const info2 = await setOrchestrationPhase("phase_2_executives", "mcp");
24037
+ return {
24038
+ content: [{ type: "text", text: render(info2, "Phase 2 enabled. Your COO can now lean on the executive bench when useful.") }]
24039
+ };
24040
+ }
24041
+ if (action === "unlock_parallel") {
24042
+ const info2 = await setOrchestrationPhase("phase_3_parallel_org", "mcp");
24043
+ return {
24044
+ content: [{ type: "text", text: render(info2, "Phase 3 enabled. Parallel execution is available with review/permission gates.") }]
24045
+ };
24046
+ }
24047
+ if (!phase) {
24048
+ return {
24049
+ content: [{ type: "text", text: "action='set' requires phase: 1, 2, 3, phase_1_coo, phase_2_executives, or phase_3_parallel_org." }],
24050
+ isError: true
24051
+ };
24052
+ }
24053
+ const info = await setOrchestrationPhase(phase, "mcp");
24054
+ return { content: [{ type: "text", text: render(info, "Updated orchestration phase.") }] };
24055
+ } catch (err) {
24056
+ return {
24057
+ content: [{ type: "text", text: `Failed to update orchestration phase: ${err instanceof Error ? err.message : String(err)}` }],
24058
+ isError: true
24059
+ };
24060
+ }
24061
+ }
24062
+ );
24063
+ }
24064
+ var init_orchestration_phase2 = __esm({
24065
+ "src/mcp/tools/orchestration-phase.ts"() {
24066
+ "use strict";
24067
+ init_orchestration_phase();
24068
+ }
24069
+ });
24070
+
23414
24071
  // src/lib/vps-backup.ts
23415
24072
  import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
23416
24073
  import { Readable } from "stream";
@@ -23734,50 +24391,50 @@ var init_vps_backup = __esm({
23734
24391
  });
23735
24392
 
23736
24393
  // src/mcp/tools/backup-vps.ts
23737
- import { z as z59 } from "zod";
24394
+ import { z as z60 } from "zod";
23738
24395
  function registerBackupVps(server) {
23739
24396
  server.registerTool(
23740
24397
  "backup_vps",
23741
24398
  {
23742
24399
  title: "VPS Backup",
23743
24400
  description: "Run VPS Postgres backup/restore workflows and check status.",
23744
- inputSchema: z59.discriminatedUnion("action", [
23745
- z59.object({
23746
- action: z59.literal("backup"),
23747
- databaseUrl: z59.string().min(1).describe("Postgres DATABASE_URL to dump from"),
23748
- encryptionKeyB64: z59.string().min(1).max(4096).optional().describe(
24401
+ inputSchema: z60.discriminatedUnion("action", [
24402
+ z60.object({
24403
+ action: z60.literal("backup"),
24404
+ databaseUrl: z60.string().min(1).describe("Postgres DATABASE_URL to dump from"),
24405
+ encryptionKeyB64: z60.string().min(1).max(4096).optional().describe(
23749
24406
  "Base64 AES-256 key (defaults to local master key if omitted)"
23750
24407
  ),
23751
- r2Bucket: z59.string().min(1).describe("R2 bucket name"),
23752
- r2Endpoint: z59.string().min(1).describe("R2 endpoint URL"),
23753
- r2AccessKeyId: z59.string().min(1).describe("R2 access key ID"),
23754
- r2SecretAccessKey: z59.string().min(1).describe("R2 secret access key")
24408
+ r2Bucket: z60.string().min(1).describe("R2 bucket name"),
24409
+ r2Endpoint: z60.string().min(1).describe("R2 endpoint URL"),
24410
+ r2AccessKeyId: z60.string().min(1).describe("R2 access key ID"),
24411
+ r2SecretAccessKey: z60.string().min(1).describe("R2 secret access key")
23755
24412
  }),
23756
- z59.object({
23757
- action: z59.literal("restore"),
23758
- databaseUrl: z59.string().min(1).describe("Target Postgres DATABASE_URL for restore"),
23759
- backupKey: z59.string().min(1).describe("R2 object key to restore from"),
23760
- encryptionKeyB64: z59.string().min(1).max(4096).optional().describe(
24413
+ z60.object({
24414
+ action: z60.literal("restore"),
24415
+ databaseUrl: z60.string().min(1).describe("Target Postgres DATABASE_URL for restore"),
24416
+ backupKey: z60.string().min(1).describe("R2 object key to restore from"),
24417
+ encryptionKeyB64: z60.string().min(1).max(4096).optional().describe(
23761
24418
  "Base64 AES-256 key (defaults to local master key if omitted)"
23762
24419
  ),
23763
- r2Bucket: z59.string().min(1).describe("R2 bucket name"),
23764
- r2Endpoint: z59.string().min(1).describe("R2 endpoint URL"),
23765
- r2AccessKeyId: z59.string().min(1).describe("R2 access key ID"),
23766
- r2SecretAccessKey: z59.string().min(1).describe("R2 secret access key")
24420
+ r2Bucket: z60.string().min(1).describe("R2 bucket name"),
24421
+ r2Endpoint: z60.string().min(1).describe("R2 endpoint URL"),
24422
+ r2AccessKeyId: z60.string().min(1).describe("R2 access key ID"),
24423
+ r2SecretAccessKey: z60.string().min(1).describe("R2 secret access key")
23767
24424
  }),
23768
- z59.object({
23769
- action: z59.literal("list"),
23770
- r2Bucket: z59.string().min(1).describe("R2 bucket name"),
23771
- r2Endpoint: z59.string().min(1).describe("R2 endpoint URL"),
23772
- r2AccessKeyId: z59.string().min(1).describe("R2 access key ID"),
23773
- r2SecretAccessKey: z59.string().min(1).describe("R2 secret access key")
24425
+ z60.object({
24426
+ action: z60.literal("list"),
24427
+ r2Bucket: z60.string().min(1).describe("R2 bucket name"),
24428
+ r2Endpoint: z60.string().min(1).describe("R2 endpoint URL"),
24429
+ r2AccessKeyId: z60.string().min(1).describe("R2 access key ID"),
24430
+ r2SecretAccessKey: z60.string().min(1).describe("R2 secret access key")
23774
24431
  }),
23775
- z59.object({
23776
- action: z59.literal("health"),
23777
- r2Bucket: z59.string().min(1).describe("R2 bucket name"),
23778
- r2Endpoint: z59.string().min(1).describe("R2 endpoint URL"),
23779
- r2AccessKeyId: z59.string().min(1).describe("R2 access key ID"),
23780
- r2SecretAccessKey: z59.string().min(1).describe("R2 secret access key")
24432
+ z60.object({
24433
+ action: z60.literal("health"),
24434
+ r2Bucket: z60.string().min(1).describe("R2 bucket name"),
24435
+ r2Endpoint: z60.string().min(1).describe("R2 endpoint URL"),
24436
+ r2AccessKeyId: z60.string().min(1).describe("R2 access key ID"),
24437
+ r2SecretAccessKey: z60.string().min(1).describe("R2 secret access key")
23781
24438
  })
23782
24439
  ])
23783
24440
  },
@@ -23971,9 +24628,9 @@ var init_hostinger_api = __esm({
23971
24628
  }
23972
24629
  this.lastRequestTime = Date.now();
23973
24630
  }
23974
- async request(method, path55, body) {
24631
+ async request(method, path56, body) {
23975
24632
  await this.rateLimit();
23976
- const url = `${this.baseUrl}${path55}`;
24633
+ const url = `${this.baseUrl}${path56}`;
23977
24634
  const headers = {
23978
24635
  Authorization: `Bearer ${this.apiKey}`,
23979
24636
  "Content-Type": "application/json",
@@ -24042,8 +24699,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
24042
24699
  }
24043
24700
  return envelope.result;
24044
24701
  }
24045
- function buildUrl(zoneId, path55 = "/dns_records", query) {
24046
- const normalizedPath = path55.startsWith("/") ? path55 : `/${path55}`;
24702
+ function buildUrl(zoneId, path56 = "/dns_records", query) {
24703
+ const normalizedPath = path56.startsWith("/") ? path56 : `/${path56}`;
24047
24704
  const url = new URL(
24048
24705
  `${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
24049
24706
  );
@@ -24114,11 +24771,11 @@ var init_cloudflare_dns = __esm({
24114
24771
  });
24115
24772
 
24116
24773
  // src/mcp/tools/deploy-client.ts
24117
- import { z as z60 } from "zod";
24774
+ import { z as z61 } from "zod";
24118
24775
  import { execFile } from "child_process";
24119
24776
  import { promisify } from "util";
24120
- import path39 from "path";
24121
- import { existsSync as existsSync34 } from "fs";
24777
+ import path40 from "path";
24778
+ import { existsSync as existsSync35 } from "fs";
24122
24779
  async function executeDeployment(params, client) {
24123
24780
  const {
24124
24781
  client_name,
@@ -24190,12 +24847,12 @@ function registerDeployClient(server) {
24190
24847
  title: "Deploy Client",
24191
24848
  description: "Provision a Hostinger VPS and deploy exe-os for a client. Creates VPS, waits for ready state, runs Ansible playbook, verifies health.",
24192
24849
  inputSchema: {
24193
- client_name: z60.string().min(1).describe("Client name (used for hostname and identification)"),
24194
- domain: z60.string().min(1).describe("Domain name for the deployment (e.g., client.exe.ai)"),
24195
- region: z60.string().default("jakarta").describe("VPS region (default: jakarta)"),
24196
- plan: z60.string().default("kvm-2").describe("Hostinger VPS plan (default: kvm-2)"),
24197
- ssl_email: z60.string().email().describe("Email for Let's Encrypt SSL certificate"),
24198
- user_id: z60.string().min(1).describe("User/customer ID for inventory tracking")
24850
+ client_name: z61.string().min(1).describe("Client name (used for hostname and identification)"),
24851
+ domain: z61.string().min(1).describe("Domain name for the deployment (e.g., client.exe.ai)"),
24852
+ region: z61.string().default("jakarta").describe("VPS region (default: jakarta)"),
24853
+ plan: z61.string().default("kvm-2").describe("Hostinger VPS plan (default: kvm-2)"),
24854
+ ssl_email: z61.string().email().describe("Email for Let's Encrypt SSL certificate"),
24855
+ user_id: z61.string().min(1).describe("User/customer ID for inventory tracking")
24199
24856
  }
24200
24857
  },
24201
24858
  async ({ client_name, domain, region, plan, ssl_email, user_id }) => {
@@ -24263,12 +24920,12 @@ async function waitForReady(client, vpsId) {
24263
24920
  }
24264
24921
  async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
24265
24922
  const safeClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "-");
24266
- const playbookDir = path39.resolve(process.cwd(), "infrastructure", "ansible");
24267
- const playbookPath = path39.join(playbookDir, "deploy.yml");
24268
- const inventoryPath = path39.join(playbookDir, "inventory", "hosts.yml");
24269
- const clientVarsPath = path39.join(playbookDir, "vars", `${safeClientName}.yml`);
24270
- const varsDir = path39.join(playbookDir, "vars");
24271
- if (!path39.resolve(clientVarsPath).startsWith(path39.resolve(varsDir))) {
24923
+ const playbookDir = path40.resolve(process.cwd(), "infrastructure", "ansible");
24924
+ const playbookPath = path40.join(playbookDir, "deploy.yml");
24925
+ const inventoryPath = path40.join(playbookDir, "inventory", "hosts.yml");
24926
+ const clientVarsPath = path40.join(playbookDir, "vars", `${safeClientName}.yml`);
24927
+ const varsDir = path40.join(playbookDir, "vars");
24928
+ if (!path40.resolve(clientVarsPath).startsWith(path40.resolve(varsDir))) {
24272
24929
  throw new Error(`Invalid client name for vars path: ${clientName}`);
24273
24930
  }
24274
24931
  const args = [
@@ -24284,7 +24941,7 @@ async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
24284
24941
  "-e",
24285
24942
  `client_name=${safeClientName}`
24286
24943
  ];
24287
- if (existsSync34(clientVarsPath)) {
24944
+ if (existsSync35(clientVarsPath)) {
24288
24945
  args.push("-e", `@${clientVarsPath}`);
24289
24946
  }
24290
24947
  try {
@@ -24374,7 +25031,7 @@ var init_deploy_client = __esm({
24374
25031
  });
24375
25032
 
24376
25033
  // src/mcp/tools/get-license-status.ts
24377
- import { z as z61 } from "zod";
25034
+ import { z as z62 } from "zod";
24378
25035
  function registerGetLicenseStatus(server) {
24379
25036
  server.registerTool(
24380
25037
  "get_license_status",
@@ -24382,7 +25039,7 @@ function registerGetLicenseStatus(server) {
24382
25039
  title: "Get License Status",
24383
25040
  description: "Get current license status: plan, validity, feature gates, limits, and expiry.",
24384
25041
  inputSchema: {
24385
- _dummy: z61.string().optional().describe("Unused \u2014 no input required")
25042
+ _dummy: z62.string().optional().describe("Unused \u2014 no input required")
24386
25043
  }
24387
25044
  },
24388
25045
  async () => {
@@ -24448,11 +25105,11 @@ var init_get_license_status = __esm({
24448
25105
 
24449
25106
  // src/mcp/tools/create-license.ts
24450
25107
  import os17 from "os";
24451
- import path40 from "path";
25108
+ import path41 from "path";
24452
25109
  import { randomBytes as randomBytes2, randomUUID as randomUUID6 } from "crypto";
24453
25110
  import { createRequire as createRequire4 } from "module";
24454
25111
  import { pathToFileURL as pathToFileURL4 } from "url";
24455
- import { z as z62 } from "zod";
25112
+ import { z as z63 } from "zod";
24456
25113
  function loadPrisma2() {
24457
25114
  if (!prismaPromise2) {
24458
25115
  prismaPromise2 = (async () => {
@@ -24463,8 +25120,8 @@ function loadPrisma2() {
24463
25120
  if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
24464
25121
  return new Ctor2();
24465
25122
  }
24466
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path40.join(os17.homedir(), "exe-db");
24467
- const req = createRequire4(path40.join(exeDbRoot, "package.json"));
25123
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path41.join(os17.homedir(), "exe-db");
25124
+ const req = createRequire4(path41.join(exeDbRoot, "package.json"));
24468
25125
  const entry = req.resolve("@prisma/client");
24469
25126
  const mod = await import(pathToFileURL4(entry).href);
24470
25127
  const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
@@ -24484,10 +25141,10 @@ function registerCreateLicense(server) {
24484
25141
  title: "Create License",
24485
25142
  description: "Generate an exe_sk_* license key for a user. Stores in billing.licenses. Returns the key to give to the customer.",
24486
25143
  inputSchema: {
24487
- email: z62.string().email().describe("Customer email address"),
24488
- name: z62.string().optional().describe("Customer name"),
24489
- plan: z62.enum(["free", "pro", "team", "agency", "enterprise"]).default("pro").describe("License plan tier"),
24490
- expires_in_days: z62.number().int().positive().default(365).describe("Days until expiration (default 365)")
25144
+ email: z63.string().email().describe("Customer email address"),
25145
+ name: z63.string().optional().describe("Customer name"),
25146
+ plan: z63.enum(["free", "pro", "team", "agency", "enterprise"]).default("pro").describe("License plan tier"),
25147
+ expires_in_days: z63.number().int().positive().default(365).describe("Days until expiration (default 365)")
24491
25148
  }
24492
25149
  },
24493
25150
  async ({ email, name, plan, expires_in_days }) => {
@@ -24547,10 +25204,10 @@ var init_create_license = __esm({
24547
25204
 
24548
25205
  // src/mcp/tools/list-licenses.ts
24549
25206
  import os18 from "os";
24550
- import path41 from "path";
25207
+ import path42 from "path";
24551
25208
  import { createRequire as createRequire5 } from "module";
24552
25209
  import { pathToFileURL as pathToFileURL5 } from "url";
24553
- import { z as z63 } from "zod";
25210
+ import { z as z64 } from "zod";
24554
25211
  function loadPrisma3() {
24555
25212
  if (!prismaPromise3) {
24556
25213
  prismaPromise3 = (async () => {
@@ -24561,8 +25218,8 @@ function loadPrisma3() {
24561
25218
  if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
24562
25219
  return new Ctor2();
24563
25220
  }
24564
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path41.join(os18.homedir(), "exe-db");
24565
- const req = createRequire5(path41.join(exeDbRoot, "package.json"));
25221
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path42.join(os18.homedir(), "exe-db");
25222
+ const req = createRequire5(path42.join(exeDbRoot, "package.json"));
24566
25223
  const entry = req.resolve("@prisma/client");
24567
25224
  const mod = await import(pathToFileURL5(entry).href);
24568
25225
  const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
@@ -24579,7 +25236,7 @@ function registerListLicenses(server) {
24579
25236
  title: "List Licenses",
24580
25237
  description: "List all issued licenses with status (active/expired/revoked). Optionally filter by plan.",
24581
25238
  inputSchema: {
24582
- plan: z63.enum(["free", "pro", "team", "agency", "enterprise"]).optional().describe("Filter by plan tier (omit for all)")
25239
+ plan: z64.enum(["free", "pro", "team", "agency", "enterprise"]).optional().describe("Filter by plan tier (omit for all)")
24583
25240
  }
24584
25241
  },
24585
25242
  async ({ plan }) => {
@@ -24636,7 +25293,7 @@ var init_list_licenses = __esm({
24636
25293
  });
24637
25294
 
24638
25295
  // src/mcp/tools/activate-license.ts
24639
- import { z as z64 } from "zod";
25296
+ import { z as z65 } from "zod";
24640
25297
  function registerActivateLicense(server) {
24641
25298
  server.registerTool(
24642
25299
  "activate_license",
@@ -24644,7 +25301,7 @@ function registerActivateLicense(server) {
24644
25301
  title: "Activate License",
24645
25302
  description: "Activate an exe_sk_* license key on this device. Writes to ~/.exe-os/license.key, validates against Postgres, and caches the result.",
24646
25303
  inputSchema: {
24647
- key: z64.string().startsWith("exe_sk_").describe("License key (exe_sk_*)")
25304
+ key: z65.string().startsWith("exe_sk_").describe("License key (exe_sk_*)")
24648
25305
  }
24649
25306
  },
24650
25307
  async ({ key }) => {
@@ -24696,14 +25353,14 @@ var init_activate_license = __esm({
24696
25353
  });
24697
25354
 
24698
25355
  // src/automation/trigger-engine.ts
24699
- import { readFileSync as readFileSync26, writeFileSync as writeFileSync18, existsSync as existsSync35, mkdirSync as mkdirSync15 } from "fs";
25356
+ import { readFileSync as readFileSync27, writeFileSync as writeFileSync19, existsSync as existsSync36, mkdirSync as mkdirSync16 } from "fs";
24700
25357
  import { randomUUID as randomUUID7 } from "crypto";
24701
- import path42 from "path";
25358
+ import path43 from "path";
24702
25359
  import os19 from "os";
24703
25360
  function loadTriggers(project) {
24704
- if (!existsSync35(TRIGGERS_PATH)) return [];
25361
+ if (!existsSync36(TRIGGERS_PATH)) return [];
24705
25362
  try {
24706
- const raw = readFileSync26(TRIGGERS_PATH, "utf-8");
25363
+ const raw = readFileSync27(TRIGGERS_PATH, "utf-8");
24707
25364
  const all = JSON.parse(raw);
24708
25365
  if (!Array.isArray(all)) return [];
24709
25366
  if (project) {
@@ -24715,9 +25372,9 @@ function loadTriggers(project) {
24715
25372
  }
24716
25373
  }
24717
25374
  function saveTriggers(triggers) {
24718
- const dir = path42.dirname(TRIGGERS_PATH);
24719
- if (!existsSync35(dir)) mkdirSync15(dir, { recursive: true });
24720
- writeFileSync18(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
25375
+ const dir = path43.dirname(TRIGGERS_PATH);
25376
+ if (!existsSync36(dir)) mkdirSync16(dir, { recursive: true });
25377
+ writeFileSync19(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
24721
25378
  }
24722
25379
  function createNewTrigger(input) {
24723
25380
  const triggers = loadTriggers();
@@ -24736,7 +25393,7 @@ var TRIGGERS_PATH;
24736
25393
  var init_trigger_engine = __esm({
24737
25394
  "src/automation/trigger-engine.ts"() {
24738
25395
  "use strict";
24739
- TRIGGERS_PATH = path42.join(os19.homedir(), ".exe-os", "triggers.json");
25396
+ TRIGGERS_PATH = path43.join(os19.homedir(), ".exe-os", "triggers.json");
24740
25397
  }
24741
25398
  });
24742
25399
 
@@ -24886,7 +25543,7 @@ var init_schedules = __esm({
24886
25543
  });
24887
25544
 
24888
25545
  // src/mcp/tools/create-trigger.ts
24889
- import { z as z65 } from "zod";
25546
+ import { z as z66 } from "zod";
24890
25547
  function registerCreateTrigger(server) {
24891
25548
  server.registerTool(
24892
25549
  "create_trigger",
@@ -24894,18 +25551,18 @@ function registerCreateTrigger(server) {
24894
25551
  title: "Create Trigger",
24895
25552
  description: "Create a CRM event trigger or scheduled automation. When matching events occur (e.g., Deal.updated with stage=won), configured actions fire automatically (send_whatsapp, create_task, etc.).",
24896
25553
  inputSchema: {
24897
- name: z65.string().describe("Human-readable trigger name"),
24898
- event: z65.string().describe(
25554
+ name: z66.string().describe("Human-readable trigger name"),
25555
+ event: z66.string().describe(
24899
25556
  'CRM event to match, e.g., "Deal.updated", "Order.created", "*" for all'
24900
25557
  ),
24901
- conditions: z65.array(conditionSchema).default([]).describe("Conditions that must all match (AND logic)"),
24902
- actions: z65.array(actionSchema).min(1).describe("Actions to execute when trigger fires"),
24903
- project: z65.string().optional().describe("Scope trigger to a specific project"),
24904
- enabled: z65.boolean().default(true).describe("Whether trigger is active"),
24905
- schedule: z65.string().optional().describe(
25558
+ conditions: z66.array(conditionSchema).default([]).describe("Conditions that must all match (AND logic)"),
25559
+ actions: z66.array(actionSchema).min(1).describe("Actions to execute when trigger fires"),
25560
+ project: z66.string().optional().describe("Scope trigger to a specific project"),
25561
+ enabled: z66.boolean().default(true).describe("Whether trigger is active"),
25562
+ schedule: z66.string().optional().describe(
24906
25563
  'Cron schedule for time-based triggers, e.g., "0 9 * * *" or "Monday 9am"'
24907
25564
  ),
24908
- query: z65.string().optional().describe(
25565
+ query: z66.string().optional().describe(
24909
25566
  "CRM GraphQL query to run on schedule (required if schedule is set)"
24910
25567
  )
24911
25568
  }
@@ -24982,14 +25639,14 @@ var init_create_trigger = __esm({
24982
25639
  "use strict";
24983
25640
  init_trigger_engine();
24984
25641
  init_schedules();
24985
- conditionSchema = z65.object({
24986
- field: z65.string().describe("Dot-path field to evaluate, e.g., 'stage' or 'amount'"),
24987
- op: z65.enum(["eq", "neq", "gt", "lt", "gte", "lte", "contains", "not_contains"]).describe("Comparison operator"),
24988
- value: z65.string().or(z65.number()).or(z65.boolean()).describe("Value to compare against")
25642
+ conditionSchema = z66.object({
25643
+ field: z66.string().describe("Dot-path field to evaluate, e.g., 'stage' or 'amount'"),
25644
+ op: z66.enum(["eq", "neq", "gt", "lt", "gte", "lte", "contains", "not_contains"]).describe("Comparison operator"),
25645
+ value: z66.string().or(z66.number()).or(z66.boolean()).describe("Value to compare against")
24989
25646
  });
24990
- actionSchema = z65.object({
24991
- type: z65.enum(["send_whatsapp", "send_message", "create_task", "mcp_tool"]).describe("Action type to execute"),
24992
- params: z65.record(z65.string(), z65.string()).describe(
25647
+ actionSchema = z66.object({
25648
+ type: z66.enum(["send_whatsapp", "send_message", "create_task", "mcp_tool"]).describe("Action type to execute"),
25649
+ params: z66.record(z66.string(), z66.string()).describe(
24993
25650
  "Action parameters. Supports {{record.field}} templates for dynamic values."
24994
25651
  )
24995
25652
  });
@@ -24997,7 +25654,7 @@ var init_create_trigger = __esm({
24997
25654
  });
24998
25655
 
24999
25656
  // src/mcp/tools/list-triggers.ts
25000
- import { z as z66 } from "zod";
25657
+ import { z as z67 } from "zod";
25001
25658
  function registerListTriggers(server) {
25002
25659
  server.registerTool(
25003
25660
  "list_triggers",
@@ -25005,7 +25662,7 @@ function registerListTriggers(server) {
25005
25662
  title: "List Triggers",
25006
25663
  description: "List configured CRM event triggers and scheduled automations.",
25007
25664
  inputSchema: {
25008
- project: z66.string().optional().describe("Filter triggers by project name")
25665
+ project: z67.string().optional().describe("Filter triggers by project name")
25009
25666
  }
25010
25667
  },
25011
25668
  async ({ project }) => {
@@ -25046,47 +25703,47 @@ var init_list_triggers = __esm({
25046
25703
  });
25047
25704
 
25048
25705
  // src/automation/starter-packs/index.ts
25049
- import { readFileSync as readFileSync27, readdirSync as readdirSync12, existsSync as existsSync36 } from "fs";
25050
- import path43 from "path";
25706
+ import { readFileSync as readFileSync28, readdirSync as readdirSync12, existsSync as existsSync37 } from "fs";
25707
+ import path44 from "path";
25051
25708
  import { fileURLToPath as fileURLToPath4 } from "url";
25052
25709
  function listPacks() {
25053
- const packsDir = path43.join(__dirname, ".");
25054
- if (!existsSync36(packsDir)) return [];
25710
+ const packsDir = path44.join(__dirname, ".");
25711
+ if (!existsSync37(packsDir)) return [];
25055
25712
  return readdirSync12(packsDir, { withFileTypes: true }).filter(
25056
- (d) => d.isDirectory() && existsSync36(path43.join(packsDir, d.name, "custom-objects.json"))
25713
+ (d) => d.isDirectory() && existsSync37(path44.join(packsDir, d.name, "custom-objects.json"))
25057
25714
  ).map((d) => d.name);
25058
25715
  }
25059
25716
  function loadPack(industry) {
25060
- const packDir = path43.join(__dirname, industry);
25061
- const objectsPath = path43.join(packDir, "custom-objects.json");
25062
- const triggersPath = path43.join(packDir, "triggers.json");
25063
- const wikiDir = path43.join(packDir, "wiki-seeds");
25064
- const manifestPath = path43.join(packDir, "pack.json");
25065
- const identityContextPath = path43.join(packDir, "identity-context.md");
25066
- if (!existsSync36(objectsPath)) return null;
25717
+ const packDir = path44.join(__dirname, industry);
25718
+ const objectsPath = path44.join(packDir, "custom-objects.json");
25719
+ const triggersPath = path44.join(packDir, "triggers.json");
25720
+ const wikiDir = path44.join(packDir, "wiki-seeds");
25721
+ const manifestPath = path44.join(packDir, "pack.json");
25722
+ const identityContextPath = path44.join(packDir, "identity-context.md");
25723
+ if (!existsSync37(objectsPath)) return null;
25067
25724
  let customObjects = [];
25068
25725
  try {
25069
25726
  customObjects = JSON.parse(
25070
- readFileSync27(objectsPath, "utf-8")
25727
+ readFileSync28(objectsPath, "utf-8")
25071
25728
  );
25072
25729
  } catch {
25073
25730
  customObjects = [];
25074
25731
  }
25075
25732
  let triggers = [];
25076
- if (existsSync36(triggersPath)) {
25733
+ if (existsSync37(triggersPath)) {
25077
25734
  try {
25078
25735
  triggers = JSON.parse(
25079
- readFileSync27(triggersPath, "utf-8")
25736
+ readFileSync28(triggersPath, "utf-8")
25080
25737
  );
25081
25738
  } catch {
25082
25739
  triggers = [];
25083
25740
  }
25084
25741
  }
25085
25742
  const wikiSeeds = [];
25086
- if (existsSync36(wikiDir)) {
25743
+ if (existsSync37(wikiDir)) {
25087
25744
  const files = readdirSync12(wikiDir).filter((f) => f.endsWith(".md"));
25088
25745
  for (const file of files) {
25089
- const content = readFileSync27(path43.join(wikiDir, file), "utf-8");
25746
+ const content = readFileSync28(path44.join(wikiDir, file), "utf-8");
25090
25747
  const titleMatch = content.match(/^#\s+(.+)/m);
25091
25748
  wikiSeeds.push({
25092
25749
  filename: file,
@@ -25096,17 +25753,17 @@ function loadPack(industry) {
25096
25753
  }
25097
25754
  }
25098
25755
  let manifest = {};
25099
- if (existsSync36(manifestPath)) {
25756
+ if (existsSync37(manifestPath)) {
25100
25757
  try {
25101
- manifest = JSON.parse(readFileSync27(manifestPath, "utf-8"));
25758
+ manifest = JSON.parse(readFileSync28(manifestPath, "utf-8"));
25102
25759
  } catch {
25103
25760
  manifest = {};
25104
25761
  }
25105
25762
  }
25106
25763
  let identityContext = null;
25107
- if (existsSync36(identityContextPath)) {
25764
+ if (existsSync37(identityContextPath)) {
25108
25765
  try {
25109
- identityContext = readFileSync27(identityContextPath, "utf-8");
25766
+ identityContext = readFileSync28(identityContextPath, "utf-8");
25110
25767
  } catch {
25111
25768
  identityContext = null;
25112
25769
  }
@@ -25165,7 +25822,7 @@ var init_starter_packs = __esm({
25165
25822
  "src/automation/starter-packs/index.ts"() {
25166
25823
  "use strict";
25167
25824
  init_trigger_engine();
25168
- __dirname = path43.dirname(fileURLToPath4(import.meta.url));
25825
+ __dirname = path44.dirname(fileURLToPath4(import.meta.url));
25169
25826
  }
25170
25827
  });
25171
25828
 
@@ -25297,21 +25954,21 @@ All memory, tasks, behaviors, documents, and wiki content belonging to {{company
25297
25954
  });
25298
25955
 
25299
25956
  // src/lib/client-coo.ts
25300
- import { existsSync as existsSync37, mkdirSync as mkdirSync16, writeFileSync as writeFileSync19 } from "fs";
25301
- import path44 from "path";
25957
+ import { existsSync as existsSync38, mkdirSync as mkdirSync17, writeFileSync as writeFileSync20 } from "fs";
25958
+ import path45 from "path";
25302
25959
  async function provisionClientCOO(vars, opts = {}) {
25303
- const identityDir = opts.identityDir ?? path44.join(EXE_AI_DIR, "identity");
25960
+ const identityDir = opts.identityDir ?? path45.join(EXE_AI_DIR, "identity");
25304
25961
  const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
25305
25962
  const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
25306
- const identityPath2 = path44.join(identityDir, `${vars.agent_name}.md`);
25307
- if (existsSync37(identityPath2)) {
25963
+ const identityPath2 = path45.join(identityDir, `${vars.agent_name}.md`);
25964
+ if (existsSync38(identityPath2)) {
25308
25965
  throw new ClientCOOClobberError(vars.agent_name, identityPath2);
25309
25966
  }
25310
25967
  const body = renderClientCOOTemplate(vars);
25311
- if (!existsSync37(identityDir)) {
25312
- mkdirSync16(identityDir, { recursive: true });
25968
+ if (!existsSync38(identityDir)) {
25969
+ mkdirSync17(identityDir, { recursive: true });
25313
25970
  }
25314
- writeFileSync19(identityPath2, body, "utf-8");
25971
+ writeFileSync20(identityPath2, body, "utf-8");
25315
25972
  const employees = await loadEmployees(rosterPath);
25316
25973
  const existing = employees.find((e) => e.name === vars.agent_name);
25317
25974
  let addedToRoster = false;
@@ -25363,7 +26020,7 @@ var init_client_coo = __esm({
25363
26020
  });
25364
26021
 
25365
26022
  // src/mcp/tools/apply-starter-pack.ts
25366
- import { z as z67 } from "zod";
26023
+ import { z as z68 } from "zod";
25367
26024
  function registerApplyStarterPack(server) {
25368
26025
  server.registerTool(
25369
26026
  "apply_starter_pack",
@@ -25371,17 +26028,17 @@ function registerApplyStarterPack(server) {
25371
26028
  title: "Apply Starter Pack",
25372
26029
  description: "Apply an industry starter pack to a project. Sets up CRM custom objects, pre-wired automation triggers, and wiki seed content. When the pack creates a Client COO (e.g. distribution) and agent_name, company_name, founder_name are all provided, also provisions the COO identity, roster entry, and feedback-loop behavior. Available packs: distribution. More coming.",
25373
26030
  inputSchema: {
25374
- industry: z67.string().describe(
26031
+ industry: z68.string().describe(
25375
26032
  'Industry pack to apply (e.g., "distribution"). Use without args to list available packs.'
25376
26033
  ),
25377
- project: z67.string().describe("Project name to scope the triggers to"),
25378
- agent_name: z67.string().optional().describe(
26034
+ project: z68.string().describe("Project name to scope the triggers to"),
26035
+ agent_name: z68.string().optional().describe(
25379
26036
  "Optional. Lowercase alphanumeric name for the Client COO agent. Required (alongside company_name and founder_name) to provision a COO."
25380
26037
  ),
25381
- company_name: z67.string().optional().describe(
26038
+ company_name: z68.string().optional().describe(
25382
26039
  "Optional. The client company the COO serves. Required to provision a COO."
25383
26040
  ),
25384
- founder_name: z67.string().optional().describe(
26041
+ founder_name: z68.string().optional().describe(
25385
26042
  "Optional. The founder the COO reports to. Required to provision a COO."
25386
26043
  )
25387
26044
  }
@@ -25533,18 +26190,18 @@ var init_apply_starter_pack = __esm({
25533
26190
  });
25534
26191
 
25535
26192
  // src/mcp/tools/load-skill.ts
25536
- import { z as z68 } from "zod";
25537
- import { readFileSync as readFileSync28, readdirSync as readdirSync13, statSync as statSync7 } from "fs";
25538
- import path45 from "path";
26193
+ import { z as z69 } from "zod";
26194
+ import { readFileSync as readFileSync29, readdirSync as readdirSync13, statSync as statSync7 } from "fs";
26195
+ import path46 from "path";
25539
26196
  import { homedir as homedir7 } from "os";
25540
26197
  function listAvailableSkills() {
25541
26198
  try {
25542
26199
  const entries = readdirSync13(SKILLS_DIR);
25543
26200
  return entries.filter((entry) => {
25544
26201
  try {
25545
- const entryPath = path45.join(SKILLS_DIR, entry);
26202
+ const entryPath = path46.join(SKILLS_DIR, entry);
25546
26203
  if (!statSync7(entryPath).isDirectory()) return false;
25547
- const skillFile = path45.join(entryPath, "SKILL.md");
26204
+ const skillFile = path46.join(entryPath, "SKILL.md");
25548
26205
  statSync7(skillFile);
25549
26206
  return true;
25550
26207
  } catch {
@@ -25562,7 +26219,7 @@ function registerLoadSkill(server) {
25562
26219
  title: "Load Skill",
25563
26220
  description: "Load domain-specific guidance into your context. Use when you need specialized knowledge for a task (e.g., load_skill('seo') before doing SEO work, load_skill('code-reviewer') before reviewing code). Pass skill_name='list' to see all available skills.",
25564
26221
  inputSchema: {
25565
- skill_name: z68.string().describe(
26222
+ skill_name: z69.string().describe(
25566
26223
  "Skill to load (e.g. 'seo', 'code-reviewer', 'frontend-design'). Pass 'list' to see all available skills."
25567
26224
  )
25568
26225
  }
@@ -25587,10 +26244,10 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
25587
26244
  }]
25588
26245
  };
25589
26246
  }
25590
- const sanitized = path45.basename(skill_name);
25591
- const skillFile = path45.join(SKILLS_DIR, sanitized, "SKILL.md");
26247
+ const sanitized = path46.basename(skill_name);
26248
+ const skillFile = path46.join(SKILLS_DIR, sanitized, "SKILL.md");
25592
26249
  try {
25593
- const content = readFileSync28(skillFile, "utf-8");
26250
+ const content = readFileSync29(skillFile, "utf-8");
25594
26251
  return {
25595
26252
  content: [{
25596
26253
  type: "text",
@@ -25619,15 +26276,15 @@ var SKILLS_DIR;
25619
26276
  var init_load_skill = __esm({
25620
26277
  "src/mcp/tools/load-skill.ts"() {
25621
26278
  "use strict";
25622
- SKILLS_DIR = path45.join(homedir7(), ".claude", "skills");
26279
+ SKILLS_DIR = path46.join(homedir7(), ".claude", "skills");
25623
26280
  }
25624
26281
  });
25625
26282
 
25626
26283
  // src/lib/orchestration-package.ts
25627
26284
  import { randomUUID as randomUUID8 } from "crypto";
25628
- import { copyFileSync as copyFileSync2, existsSync as existsSync38, mkdirSync as mkdirSync17, readFileSync as readFileSync29, writeFileSync as writeFileSync20 } from "fs";
26285
+ import { copyFileSync as copyFileSync2, existsSync as existsSync39, mkdirSync as mkdirSync18, readFileSync as readFileSync30, writeFileSync as writeFileSync21 } from "fs";
25629
26286
  import os20 from "os";
25630
- import path46 from "path";
26287
+ import path47 from "path";
25631
26288
  function ensureObject(value, label) {
25632
26289
  if (value == null || Array.isArray(value) || typeof value !== "object") {
25633
26290
  throw new Error(`${label} must be an object`);
@@ -25686,15 +26343,15 @@ function validateProcedureEntry(value, index) {
25686
26343
  };
25687
26344
  }
25688
26345
  function getRosterPath() {
25689
- return path46.join(os20.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
26346
+ return path47.join(os20.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
25690
26347
  }
25691
26348
  function getBackupPath() {
25692
- return path46.join(os20.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
26349
+ return path47.join(os20.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
25693
26350
  }
25694
26351
  function readRosterFile() {
25695
26352
  const rosterPath = getRosterPath();
25696
- if (!existsSync38(rosterPath)) return [];
25697
- const raw = readFileSync29(rosterPath, "utf-8");
26353
+ if (!existsSync39(rosterPath)) return [];
26354
+ const raw = readFileSync30(rosterPath, "utf-8");
25698
26355
  const parsed = JSON.parse(raw);
25699
26356
  if (!Array.isArray(parsed)) {
25700
26357
  throw new Error("Roster file must contain a JSON array");
@@ -25706,8 +26363,8 @@ function writeRosterFile(roster) {
25706
26363
  throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
25707
26364
  }
25708
26365
  const rosterPath = getRosterPath();
25709
- mkdirSync17(path46.dirname(rosterPath), { recursive: true });
25710
- if (existsSync38(rosterPath)) {
26366
+ mkdirSync18(path47.dirname(rosterPath), { recursive: true });
26367
+ if (existsSync39(rosterPath)) {
25711
26368
  const currentRoster = readRosterFile();
25712
26369
  if (roster.length < currentRoster.length) {
25713
26370
  throw new Error(
@@ -25716,7 +26373,7 @@ function writeRosterFile(roster) {
25716
26373
  }
25717
26374
  copyFileSync2(rosterPath, getBackupPath());
25718
26375
  }
25719
- writeFileSync20(rosterPath, `${JSON.stringify(roster, null, 2)}
26376
+ writeFileSync21(rosterPath, `${JSON.stringify(roster, null, 2)}
25720
26377
  `, "utf-8");
25721
26378
  }
25722
26379
  function buildImportedRosterEntries(roster, timestamp) {
@@ -25979,9 +26636,9 @@ var init_orchestration_package = __esm({
25979
26636
  });
25980
26637
 
25981
26638
  // src/mcp/tools/export-orchestration.ts
25982
- import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync21 } from "fs";
25983
- import path47 from "path";
25984
- import { z as z69 } from "zod";
26639
+ import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync22 } from "fs";
26640
+ import path48 from "path";
26641
+ import { z as z70 } from "zod";
25985
26642
  function registerExportOrchestration(server) {
25986
26643
  server.registerTool(
25987
26644
  "export_orchestration",
@@ -25989,15 +26646,15 @@ function registerExportOrchestration(server) {
25989
26646
  title: "Export Orchestration",
25990
26647
  description: "Export roster, identities, behaviors, and customer procedures to a JSON package.",
25991
26648
  inputSchema: {
25992
- output_path: z69.string().describe("File path to write the JSON package")
26649
+ output_path: z70.string().describe("File path to write the JSON package")
25993
26650
  }
25994
26651
  },
25995
26652
  async ({ output_path }) => {
25996
26653
  try {
25997
26654
  await initStore();
25998
26655
  const pkg = await exportOrchestration(getActiveAgent().agentId);
25999
- mkdirSync18(path47.dirname(output_path), { recursive: true });
26000
- writeFileSync21(output_path, `${JSON.stringify(pkg, null, 2)}
26656
+ mkdirSync19(path48.dirname(output_path), { recursive: true });
26657
+ writeFileSync22(output_path, `${JSON.stringify(pkg, null, 2)}
26001
26658
  `, "utf-8");
26002
26659
  return {
26003
26660
  content: [{
@@ -26027,8 +26684,8 @@ var init_export_orchestration = __esm({
26027
26684
  });
26028
26685
 
26029
26686
  // src/mcp/tools/import-orchestration.ts
26030
- import { readFileSync as readFileSync30 } from "fs";
26031
- import { z as z70 } from "zod";
26687
+ import { readFileSync as readFileSync31 } from "fs";
26688
+ import { z as z71 } from "zod";
26032
26689
  function registerImportOrchestration(server) {
26033
26690
  server.registerTool(
26034
26691
  "import_orchestration",
@@ -26036,8 +26693,8 @@ function registerImportOrchestration(server) {
26036
26693
  title: "Import Orchestration",
26037
26694
  description: "Import roster, identities, behaviors, and procedures from an orchestration package. Restricted to coordinator/founder.",
26038
26695
  inputSchema: {
26039
- package_path: z70.string().describe("Path to the orchestration package JSON file"),
26040
- merge_strategy: z70.enum(["replace", "merge"]).default("merge").describe("How to apply the package: both strategies are additive-only \u2014 existing data is never deleted or overwritten")
26696
+ package_path: z71.string().describe("Path to the orchestration package JSON file"),
26697
+ merge_strategy: z71.enum(["replace", "merge"]).default("merge").describe("How to apply the package: both strategies are additive-only \u2014 existing data is never deleted or overwritten")
26041
26698
  }
26042
26699
  },
26043
26700
  async ({ package_path, merge_strategy }) => {
@@ -26054,7 +26711,7 @@ function registerImportOrchestration(server) {
26054
26711
  };
26055
26712
  }
26056
26713
  await initStore();
26057
- const raw = readFileSync30(package_path, "utf-8");
26714
+ const raw = readFileSync31(package_path, "utf-8");
26058
26715
  const pkg = validatePackage(JSON.parse(raw));
26059
26716
  const result = await importOrchestration(pkg, merge_strategy);
26060
26717
  return {
@@ -26086,7 +26743,7 @@ var init_import_orchestration = __esm({
26086
26743
  });
26087
26744
 
26088
26745
  // src/mcp/tools/global-procedure.ts
26089
- import { z as z71 } from "zod";
26746
+ import { z as z72 } from "zod";
26090
26747
  function registerCompanyProcedureTool(server, toolName) {
26091
26748
  server.registerTool(
26092
26749
  toolName,
@@ -26094,12 +26751,12 @@ function registerCompanyProcedureTool(server, toolName) {
26094
26751
  title: "Company Procedure",
26095
26752
  description: "Manage company procedures (customer-owned Layer 0 rules) that supersede identity, expertise, and experience. Actions: store (create new, restricted), list (view all, open), deactivate (soft-delete, restricted).",
26096
26753
  inputSchema: {
26097
- action: z71.enum(["store", "list", "deactivate"]).describe("Action to perform"),
26098
- title: z71.string().optional().describe("Short title for the procedure (store)"),
26099
- content: z71.string().max(500).optional().describe("The procedure content \u2014 clear, actionable instruction (store)"),
26100
- priority: z71.enum(["p0", "p1", "p2"]).optional().describe("Priority tier. p0 = always (default). p1 = standard. p2 = nice-to-have."),
26101
- domain: z71.string().optional().describe("Category: workflow, code-style, communication, architecture, testing, security"),
26102
- procedure_id: z71.string().optional().describe("UUID of the company procedure (deactivate)")
26754
+ action: z72.enum(["store", "list", "deactivate"]).describe("Action to perform"),
26755
+ title: z72.string().optional().describe("Short title for the procedure (store)"),
26756
+ content: z72.string().max(500).optional().describe("The procedure content \u2014 clear, actionable instruction (store)"),
26757
+ priority: z72.enum(["p0", "p1", "p2"]).optional().describe("Priority tier. p0 = always (default). p1 = standard. p2 = nice-to-have."),
26758
+ domain: z72.string().optional().describe("Category: workflow, code-style, communication, architecture, testing, security"),
26759
+ procedure_id: z72.string().optional().describe("UUID of the company procedure (deactivate)")
26103
26760
  }
26104
26761
  },
26105
26762
  async ({ action, title, content, priority, domain, procedure_id }) => {
@@ -26230,7 +26887,7 @@ var init_global_procedure = __esm({
26230
26887
  });
26231
26888
 
26232
26889
  // src/mcp/tools/config.ts
26233
- import { z as z72 } from "zod";
26890
+ import { z as z73 } from "zod";
26234
26891
  function errorResult8(text3) {
26235
26892
  return { content: [{ type: "text", text: text3 }], isError: true };
26236
26893
  }
@@ -26252,6 +26909,7 @@ function buildHandlers5() {
26252
26909
  registerRunMemoryAudit(localServer);
26253
26910
  registerRunConsolidation(localServer);
26254
26911
  registerCloudSync(localServer);
26912
+ registerOrchestrationPhase(localServer);
26255
26913
  registerBackupVps(localServer);
26256
26914
  registerDeployClient(localServer);
26257
26915
  registerGetLicenseStatus(localServer);
@@ -26274,41 +26932,42 @@ function registerConfig(server) {
26274
26932
  title: "Config",
26275
26933
  description: "Consolidated COO/admin tool for runtime config, system health, licensing, triggers, orchestration import/export, and company procedures.",
26276
26934
  inputSchema: {
26277
- action: z72.enum(Object.keys(ACTION_TO_TOOL2)).describe("Admin/config operation"),
26278
- agent_id: z72.string().optional().describe("Agent id for set_agent_config/agent_spend/session queries"),
26279
- runtime: z72.string().optional().describe("Runtime for set_agent_config"),
26280
- model: z72.string().optional().describe("Model for set_agent_config"),
26281
- reasoning_effort: z72.string().optional().describe("Reasoning effort for Codex agents"),
26282
- dry_run: z72.boolean().optional().describe("Preview without applying where supported"),
26283
- fix: z72.boolean().optional().describe("Apply fixes for memory_audit where supported"),
26284
- verbose: z72.boolean().optional().describe("Verbose output where supported"),
26285
- project_name: z72.string().optional().describe("Project filter/name"),
26286
- since: z72.string().optional().describe("ISO lower-bound timestamp"),
26287
- limit: z72.coerce.number().optional().describe("Result limit"),
26288
- output_path: z72.string().optional().describe("Output path for export/backup"),
26289
- input_path: z72.string().optional().describe("Input path for import_orchestration"),
26290
- strategy: z72.enum(["merge", "replace"]).optional().describe("Import strategy; replace must still be additive-only per platform rules"),
26291
- license_key: z72.string().optional().describe("License key for activation/status"),
26292
- email: z72.string().optional().describe("Customer email for license creation"),
26293
- plan: z72.string().optional().describe("License plan"),
26294
- name: z72.string().optional().describe("Trigger/starter pack/procedure/client name"),
26295
- event: z72.string().optional().describe("Trigger event"),
26296
- conditions: z72.array(z72.record(z72.string(), z72.unknown())).optional().describe("Trigger conditions"),
26297
- actions: z72.array(z72.record(z72.string(), z72.unknown())).optional().describe("Trigger actions"),
26298
- enabled: z72.boolean().optional().describe("Trigger enabled flag"),
26299
- schedule: z72.string().optional().describe("Trigger schedule"),
26300
- query: z72.string().optional().describe("Trigger query or filter"),
26301
- skill_name: z72.string().optional().describe("Skill name for load_skill"),
26302
- pack_name: z72.string().optional().describe("Starter pack name"),
26303
- title: z72.string().optional().describe("Procedure title"),
26304
- content: z72.string().optional().describe("Procedure content"),
26305
- priority: z72.enum(["p0", "p1", "p2"]).optional().describe("Procedure priority"),
26306
- domain: z72.string().optional().describe("Procedure domain"),
26307
- procedure_id: z72.string().optional().describe("Procedure id for deactivate"),
26308
- subaction: z72.enum(["store", "list", "deactivate"]).optional().describe("Nested action for company_procedure/global_procedure"),
26309
- max_clusters: z72.coerce.number().optional().describe("Consolidation max clusters"),
26310
- force: z72.boolean().optional().describe("Force operation where supported"),
26311
- domain_name: z72.string().optional().describe("Client deployment domain")
26935
+ action: z73.enum(Object.keys(ACTION_TO_TOOL2)).describe("Admin/config operation"),
26936
+ agent_id: z73.string().optional().describe("Agent id for set_agent_config/agent_spend/session queries"),
26937
+ runtime: z73.string().optional().describe("Runtime for set_agent_config"),
26938
+ model: z73.string().optional().describe("Model for set_agent_config"),
26939
+ reasoning_effort: z73.string().optional().describe("Reasoning effort for Codex agents"),
26940
+ dry_run: z73.boolean().optional().describe("Preview without applying where supported"),
26941
+ fix: z73.boolean().optional().describe("Apply fixes for memory_audit where supported"),
26942
+ verbose: z73.boolean().optional().describe("Verbose output where supported"),
26943
+ project_name: z73.string().optional().describe("Project filter/name"),
26944
+ since: z73.string().optional().describe("ISO lower-bound timestamp"),
26945
+ limit: z73.coerce.number().optional().describe("Result limit"),
26946
+ output_path: z73.string().optional().describe("Output path for export/backup"),
26947
+ input_path: z73.string().optional().describe("Input path for import_orchestration"),
26948
+ strategy: z73.enum(["merge", "replace"]).optional().describe("Import strategy; replace must still be additive-only per platform rules"),
26949
+ license_key: z73.string().optional().describe("License key for activation/status"),
26950
+ email: z73.string().optional().describe("Customer email for license creation"),
26951
+ plan: z73.string().optional().describe("License plan"),
26952
+ name: z73.string().optional().describe("Trigger/starter pack/procedure/client name"),
26953
+ event: z73.string().optional().describe("Trigger event"),
26954
+ conditions: z73.array(z73.record(z73.string(), z73.unknown())).optional().describe("Trigger conditions"),
26955
+ actions: z73.array(z73.record(z73.string(), z73.unknown())).optional().describe("Trigger actions"),
26956
+ enabled: z73.boolean().optional().describe("Trigger enabled flag"),
26957
+ schedule: z73.string().optional().describe("Trigger schedule"),
26958
+ query: z73.string().optional().describe("Trigger query or filter"),
26959
+ skill_name: z73.string().optional().describe("Skill name for load_skill"),
26960
+ pack_name: z73.string().optional().describe("Starter pack name"),
26961
+ title: z73.string().optional().describe("Procedure title"),
26962
+ content: z73.string().optional().describe("Procedure content"),
26963
+ priority: z73.enum(["p0", "p1", "p2"]).optional().describe("Procedure priority"),
26964
+ domain: z73.string().optional().describe("Procedure domain"),
26965
+ procedure_id: z73.string().optional().describe("Procedure id for deactivate"),
26966
+ subaction: z73.enum(["store", "list", "deactivate"]).optional().describe("Nested action for company_procedure/global_procedure"),
26967
+ max_clusters: z73.coerce.number().optional().describe("Consolidation max clusters"),
26968
+ force: z73.boolean().optional().describe("Force operation where supported"),
26969
+ domain_name: z73.string().optional().describe("Client deployment domain"),
26970
+ phase: z73.enum(["phase_1_coo", "phase_2_executives", "phase_3_parallel_org", "1", "2", "3"]).optional().describe("Orchestration phase for orchestration_phase action")
26312
26971
  }
26313
26972
  }, async (input, extra) => {
26314
26973
  const action = input.action;
@@ -26342,6 +27001,7 @@ var init_config2 = __esm({
26342
27001
  init_run_memory_audit();
26343
27002
  init_run_consolidation();
26344
27003
  init_cloud_sync2();
27004
+ init_orchestration_phase2();
26345
27005
  init_backup_vps();
26346
27006
  init_deploy_client();
26347
27007
  init_get_license_status();
@@ -26367,6 +27027,7 @@ var init_config2 = __esm({
26367
27027
  memory_audit: "run_memory_audit",
26368
27028
  run_consolidation: "run_consolidation",
26369
27029
  cloud_sync: "cloud_sync",
27030
+ orchestration_phase: "orchestration_phase",
26370
27031
  backup_vps: "backup_vps",
26371
27032
  deploy_client: "deploy_client",
26372
27033
  license_status: "get_license_status",
@@ -26386,7 +27047,7 @@ var init_config2 = __esm({
26386
27047
  });
26387
27048
 
26388
27049
  // src/mcp/tools/list-wiki-pages.ts
26389
- import { z as z73 } from "zod";
27050
+ import { z as z74 } from "zod";
26390
27051
  function registerListWikiPages(server) {
26391
27052
  server.registerTool(
26392
27053
  "list_wiki_pages",
@@ -26394,8 +27055,8 @@ function registerListWikiPages(server) {
26394
27055
  title: "List Wiki Pages",
26395
27056
  description: "List documents in an exe-wiki workspace. Optionally filter by folder.",
26396
27057
  inputSchema: {
26397
- workspace: z73.string().describe('Wiki workspace slug (e.g., "hygo")'),
26398
- folder: z73.string().optional().describe("Filter by folder path")
27058
+ workspace: z74.string().describe('Wiki workspace slug (e.g., "hygo")'),
27059
+ folder: z74.string().optional().describe("Filter by folder path")
26399
27060
  }
26400
27061
  },
26401
27062
  async ({ workspace, folder }) => {
@@ -26491,7 +27152,7 @@ var init_list_wiki_pages = __esm({
26491
27152
  });
26492
27153
 
26493
27154
  // src/mcp/tools/get-wiki-page.ts
26494
- import { z as z74 } from "zod";
27155
+ import { z as z75 } from "zod";
26495
27156
  function registerGetWikiPage(server) {
26496
27157
  server.registerTool(
26497
27158
  "get_wiki_page",
@@ -26499,9 +27160,9 @@ function registerGetWikiPage(server) {
26499
27160
  title: "Get Wiki Page",
26500
27161
  description: "Read a wiki page by document ID or title. Returns content, metadata, and folder path. Use title search when you know the page name but not the ID.",
26501
27162
  inputSchema: {
26502
- workspace: z74.string().describe('Wiki workspace slug (e.g., "hygo")'),
26503
- document_id: z74.string().optional().describe("Specific document ID (exact lookup)"),
26504
- title: z74.string().optional().describe("Search by title (fuzzy match)")
27163
+ workspace: z75.string().describe('Wiki workspace slug (e.g., "hygo")'),
27164
+ document_id: z75.string().optional().describe("Specific document ID (exact lookup)"),
27165
+ title: z75.string().optional().describe("Search by title (fuzzy match)")
26505
27166
  }
26506
27167
  },
26507
27168
  async ({ workspace, document_id, title }) => {
@@ -26657,7 +27318,7 @@ var init_get_wiki_page = __esm({
26657
27318
  });
26658
27319
 
26659
27320
  // src/mcp/tools/wiki.ts
26660
- import { z as z75 } from "zod";
27321
+ import { z as z76 } from "zod";
26661
27322
  function errorResult9(text3) {
26662
27323
  return { content: [{ type: "text", text: text3 }], isError: true };
26663
27324
  }
@@ -26683,14 +27344,14 @@ function registerWiki(server) {
26683
27344
  title: "Wiki",
26684
27345
  description: "Consolidated wiki domain tool. Actions: list, get. Wiki writes flow through raw data ingestion/projection into the curated wiki store; direct create/update MCP tools have been removed.",
26685
27346
  inputSchema: {
26686
- action: z75.enum(["list", "get"]).describe("Wiki read operation. Writes use raw-data ingestion/projection, not direct MCP writes."),
26687
- workspace: z75.string().optional().describe("Wiki workspace slug"),
26688
- title: z75.string().optional().describe("Fuzzy page title lookup for get"),
26689
- content: z75.string().optional().describe("Reserved; wiki writes use raw-data ingestion/projection"),
26690
- folder: z75.string().optional().describe("Optional folder path for list"),
26691
- document_id: z75.string().optional().describe("Document ID for get"),
26692
- mode: z75.enum(["replace", "append"]).optional().describe("Reserved; direct wiki updates are removed"),
26693
- section: z75.string().optional().describe("Reserved; direct wiki updates are removed")
27347
+ action: z76.enum(["list", "get"]).describe("Wiki read operation. Writes use raw-data ingestion/projection, not direct MCP writes."),
27348
+ workspace: z76.string().optional().describe("Wiki workspace slug"),
27349
+ title: z76.string().optional().describe("Fuzzy page title lookup for get"),
27350
+ content: z76.string().optional().describe("Reserved; wiki writes use raw-data ingestion/projection"),
27351
+ folder: z76.string().optional().describe("Optional folder path for list"),
27352
+ document_id: z76.string().optional().describe("Document ID for get"),
27353
+ mode: z76.enum(["replace", "append"]).optional().describe("Reserved; direct wiki updates are removed"),
27354
+ section: z76.string().optional().describe("Reserved; direct wiki updates are removed")
26694
27355
  }
26695
27356
  },
26696
27357
  async (input, extra) => {
@@ -26729,7 +27390,7 @@ var init_wiki = __esm({
26729
27390
  });
26730
27391
 
26731
27392
  // src/mcp/tools/behavior.ts
26732
- import { z as z76 } from "zod";
27393
+ import { z as z77 } from "zod";
26733
27394
  function rowToBehavior2(r) {
26734
27395
  return {
26735
27396
  id: String(r.id),
@@ -26751,13 +27412,13 @@ function registerBehavior(server) {
26751
27412
  title: "Behavior",
26752
27413
  description: "Manage behavioral patterns, corrections, and reusable procedures for employees. Actions: store (create new), list (query existing), deactivate (soft-delete, restricted).",
26753
27414
  inputSchema: {
26754
- action: z76.enum(["store", "list", "deactivate"]).describe("Action to perform"),
26755
- content: z76.string().max(500).optional().describe("The behavioral instruction \u2014 one clear sentence (store)"),
26756
- domain: z76.string().optional().describe("Category: workflow, code-style, tool-use, communication, architecture, testing"),
26757
- priority: z76.enum(["p0", "p1", "p2"]).optional().describe("Priority tier. p0 = always included. p1 = standard (default). p2 = nice-to-have."),
26758
- agent_id: z76.string().optional().describe("Employee name. Defaults to current agent. Pass 'all' to list everyone's (list only)."),
26759
- project_name: z76.string().optional().describe("Defaults to current project. Pass 'global' for a behavior that applies everywhere (store)."),
26760
- behavior_id: z76.string().optional().describe("UUID of the behavior (deactivate)")
27415
+ action: z77.enum(["store", "list", "deactivate"]).describe("Action to perform"),
27416
+ content: z77.string().max(500).optional().describe("The behavioral instruction \u2014 one clear sentence (store)"),
27417
+ domain: z77.string().optional().describe("Category: workflow, code-style, tool-use, communication, architecture, testing"),
27418
+ priority: z77.enum(["p0", "p1", "p2"]).optional().describe("Priority tier. p0 = always included. p1 = standard (default). p2 = nice-to-have."),
27419
+ agent_id: z77.string().optional().describe("Employee name. Defaults to current agent. Pass 'all' to list everyone's (list only)."),
27420
+ project_name: z77.string().optional().describe("Defaults to current project. Pass 'global' for a behavior that applies everywhere (store)."),
27421
+ behavior_id: z77.string().optional().describe("UUID of the behavior (deactivate)")
26761
27422
  }
26762
27423
  },
26763
27424
  async ({ action, content, domain, priority, agent_id, project_name, behavior_id }) => {
@@ -26938,7 +27599,7 @@ var init_behavior = __esm({
26938
27599
  });
26939
27600
 
26940
27601
  // src/mcp/tools/reminder.ts
26941
- import { z as z77 } from "zod";
27602
+ import { z as z78 } from "zod";
26942
27603
  function registerReminder(server) {
26943
27604
  server.registerTool(
26944
27605
  "reminder",
@@ -26946,11 +27607,11 @@ function registerReminder(server) {
26946
27607
  title: "Reminder",
26947
27608
  description: "Manage reminders for the founder. Shown in the boot brief every session. Actions: create (set new), list (view active), complete (mark done).",
26948
27609
  inputSchema: {
26949
- action: z77.enum(["create", "list", "complete"]).describe("Action to perform"),
26950
- text: z77.string().optional().describe("What to remind about (create)"),
26951
- due_date: z77.string().optional().describe("Optional due date \u2014 ISO date (2026-04-01) or null for persistent (create)"),
26952
- reminder_id: z77.string().optional().describe("Reminder UUID or text substring to match (complete)"),
26953
- include_completed: z77.boolean().optional().default(false).describe("Include completed reminders (list)")
27610
+ action: z78.enum(["create", "list", "complete"]).describe("Action to perform"),
27611
+ text: z78.string().optional().describe("What to remind about (create)"),
27612
+ due_date: z78.string().optional().describe("Optional due date \u2014 ISO date (2026-04-01) or null for persistent (create)"),
27613
+ reminder_id: z78.string().optional().describe("Reminder UUID or text substring to match (complete)"),
27614
+ include_completed: z78.boolean().optional().default(false).describe("Include completed reminders (list)")
26954
27615
  }
26955
27616
  },
26956
27617
  async ({ action, text: text3, due_date, reminder_id, include_completed }) => {
@@ -27009,7 +27670,7 @@ var init_reminder = __esm({
27009
27670
  });
27010
27671
 
27011
27672
  // src/mcp/tools/store-global-procedure.ts
27012
- import { z as z78 } from "zod";
27673
+ import { z as z79 } from "zod";
27013
27674
  function registerStoreGlobalProcedure(server) {
27014
27675
  server.registerTool(
27015
27676
  "store_global_procedure",
@@ -27017,10 +27678,10 @@ function registerStoreGlobalProcedure(server) {
27017
27678
  title: "Store Company Procedure (use global_procedure instead)",
27018
27679
  description: "DEPRECATED \u2014 use global_procedure with action='store'. Create a company procedure (customer-owned Layer 0 rule). RESTRICTED: only coordinator or founder sessions.",
27019
27680
  inputSchema: {
27020
- title: z78.string().describe("Short title for the procedure"),
27021
- content: z78.string().max(500).describe("The procedure content \u2014 clear, actionable instruction"),
27022
- priority: z78.enum(["p0", "p1", "p2"]).optional().describe("Priority tier. p0 = always (default)."),
27023
- domain: z78.string().optional().describe("Category: workflow, code-style, communication, architecture, testing, security")
27681
+ title: z79.string().describe("Short title for the procedure"),
27682
+ content: z79.string().max(500).describe("The procedure content \u2014 clear, actionable instruction"),
27683
+ priority: z79.enum(["p0", "p1", "p2"]).optional().describe("Priority tier. p0 = always (default)."),
27684
+ domain: z79.string().optional().describe("Category: workflow, code-style, communication, architecture, testing, security")
27024
27685
  }
27025
27686
  },
27026
27687
  async ({ title, content, priority, domain }) => {
@@ -27107,7 +27768,7 @@ var init_list_global_procedures = __esm({
27107
27768
  });
27108
27769
 
27109
27770
  // src/mcp/tools/deactivate-global-procedure.ts
27110
- import { z as z79 } from "zod";
27771
+ import { z as z80 } from "zod";
27111
27772
  function registerDeactivateGlobalProcedure(server) {
27112
27773
  server.registerTool(
27113
27774
  "deactivate_global_procedure",
@@ -27115,7 +27776,7 @@ function registerDeactivateGlobalProcedure(server) {
27115
27776
  title: "Deactivate Company Procedure (use global_procedure instead)",
27116
27777
  description: "DEPRECATED \u2014 use global_procedure with action='deactivate'. Soft-delete a company procedure. RESTRICTED: only coordinator or founder sessions.",
27117
27778
  inputSchema: {
27118
- procedure_id: z79.string().describe("UUID of the company procedure to deactivate")
27779
+ procedure_id: z80.string().describe("UUID of the company procedure to deactivate")
27119
27780
  }
27120
27781
  },
27121
27782
  async ({ procedure_id }) => {
@@ -27179,7 +27840,7 @@ var init_deactivate_global_procedure = __esm({
27179
27840
  });
27180
27841
 
27181
27842
  // src/mcp/tools/store-decision.ts
27182
- import { z as z80 } from "zod";
27843
+ import { z as z81 } from "zod";
27183
27844
  import crypto18 from "crypto";
27184
27845
  function registerStoreDecision(server) {
27185
27846
  server.registerTool(
@@ -27188,13 +27849,13 @@ function registerStoreDecision(server) {
27188
27849
  title: "Store Decision",
27189
27850
  description: "Store an authoritative decision keyed by domain. Use this when a decision is made that should be canonical \u2014 future lookups via get_decision return the latest decision for that domain. Supports supersession chains.",
27190
27851
  inputSchema: {
27191
- domain: z80.string().describe(
27852
+ domain: z81.string().describe(
27192
27853
  "Domain key, e.g. 'auth-strategy', 'db-migration-approach', 'api-versioning'"
27193
27854
  ),
27194
- decision: z80.string().describe("The decision text \u2014 what was decided"),
27195
- rationale: z80.string().optional().describe("Why this decision was made \u2014 constraints, trade-offs, context"),
27196
- supersedes: z80.string().optional().describe("UUID of the decision this supersedes (previous decision for this domain)"),
27197
- project_name: z80.string().optional().describe("Project name")
27855
+ decision: z81.string().describe("The decision text \u2014 what was decided"),
27856
+ rationale: z81.string().optional().describe("Why this decision was made \u2014 constraints, trade-offs, context"),
27857
+ supersedes: z81.string().optional().describe("UUID of the decision this supersedes (previous decision for this domain)"),
27858
+ project_name: z81.string().optional().describe("Project name")
27198
27859
  }
27199
27860
  },
27200
27861
  async ({ domain, decision, rationale, supersedes, project_name }) => {
@@ -27271,7 +27932,7 @@ var init_store_decision = __esm({
27271
27932
  });
27272
27933
 
27273
27934
  // src/mcp/tools/get-decision.ts
27274
- import { z as z81 } from "zod";
27935
+ import { z as z82 } from "zod";
27275
27936
  function registerGetDecision(server) {
27276
27937
  server.registerTool(
27277
27938
  "get_decision",
@@ -27279,7 +27940,7 @@ function registerGetDecision(server) {
27279
27940
  title: "Get Decision",
27280
27941
  description: "Retrieve the latest authoritative decision for a domain. Returns the current active decision and the supersession history.",
27281
27942
  inputSchema: {
27282
- domain: z81.string().describe(
27943
+ domain: z82.string().describe(
27283
27944
  "Domain key to look up, e.g. 'auth-strategy', 'db-migration-approach'"
27284
27945
  )
27285
27946
  }
@@ -27349,10 +28010,10 @@ var init_get_decision = __esm({
27349
28010
 
27350
28011
  // src/lib/people.ts
27351
28012
  import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
27352
- import { existsSync as existsSync39, readFileSync as readFileSync31 } from "fs";
27353
- import path48 from "path";
28013
+ import { existsSync as existsSync40, readFileSync as readFileSync32 } from "fs";
28014
+ import path49 from "path";
27354
28015
  async function loadPeople() {
27355
- if (!existsSync39(PEOPLE_PATH)) return [];
28016
+ if (!existsSync40(PEOPLE_PATH)) return [];
27356
28017
  try {
27357
28018
  const raw = await readFile5(PEOPLE_PATH, "utf-8");
27358
28019
  return JSON.parse(raw);
@@ -27361,7 +28022,7 @@ async function loadPeople() {
27361
28022
  }
27362
28023
  }
27363
28024
  async function savePeople(people) {
27364
- await mkdir5(path48.dirname(PEOPLE_PATH), { recursive: true });
28025
+ await mkdir5(path49.dirname(PEOPLE_PATH), { recursive: true });
27365
28026
  await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
27366
28027
  }
27367
28028
  async function addPerson(person) {
@@ -27387,12 +28048,12 @@ var init_people = __esm({
27387
28048
  "src/lib/people.ts"() {
27388
28049
  "use strict";
27389
28050
  init_config();
27390
- PEOPLE_PATH = path48.join(EXE_AI_DIR, "people.json");
28051
+ PEOPLE_PATH = path49.join(EXE_AI_DIR, "people.json");
27391
28052
  }
27392
28053
  });
27393
28054
 
27394
28055
  // src/mcp/tools/people-roster.ts
27395
- import { z as z82 } from "zod";
28056
+ import { z as z83 } from "zod";
27396
28057
  function registerAddPerson(server) {
27397
28058
  server.registerTool(
27398
28059
  "add_person",
@@ -27400,10 +28061,10 @@ function registerAddPerson(server) {
27400
28061
  title: "Add Person",
27401
28062
  description: "Add or update a key human in the people roster. Used for co-founders, partners, customers \u2014 anyone agents need to know about.",
27402
28063
  inputSchema: {
27403
- name: z82.string().describe("Person's name"),
27404
- role: z82.string().describe("Their role (e.g. co-founder, customer, partner)"),
27405
- relationship: z82.string().describe("Relationship to the organization (e.g. co-founder, early adopter, investor)"),
27406
- notes: z82.string().optional().describe("Additional context about this person")
28064
+ name: z83.string().describe("Person's name"),
28065
+ role: z83.string().describe("Their role (e.g. co-founder, customer, partner)"),
28066
+ relationship: z83.string().describe("Relationship to the organization (e.g. co-founder, early adopter, investor)"),
28067
+ notes: z83.string().optional().describe("Additional context about this person")
27407
28068
  }
27408
28069
  },
27409
28070
  async ({ name, role, relationship, notes }) => {
@@ -27449,7 +28110,7 @@ function registerGetPerson(server) {
27449
28110
  title: "Get Person",
27450
28111
  description: "Look up a specific person by name from the people roster.",
27451
28112
  inputSchema: {
27452
- name: z82.string().describe("Person's name to look up")
28113
+ name: z83.string().describe("Person's name to look up")
27453
28114
  }
27454
28115
  },
27455
28116
  async ({ name }) => {
@@ -27479,7 +28140,7 @@ var init_people_roster = __esm({
27479
28140
  });
27480
28141
 
27481
28142
  // src/lib/exe-db-read.ts
27482
- import path49 from "path";
28143
+ import path50 from "path";
27483
28144
  import os21 from "os";
27484
28145
  import { createRequire as createRequire6 } from "module";
27485
28146
  import { pathToFileURL as pathToFileURL6 } from "url";
@@ -27496,8 +28157,8 @@ async function getExeDbReadClient() {
27496
28157
  if (!Ctor2) throw new Error(`No PrismaClient export found at ${explicitPath}`);
27497
28158
  return new Ctor2();
27498
28159
  }
27499
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path49.join(os21.homedir(), "exe-db");
27500
- const req = createRequire6(path49.join(exeDbRoot, "package.json"));
28160
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path50.join(os21.homedir(), "exe-db");
28161
+ const req = createRequire6(path50.join(exeDbRoot, "package.json"));
27501
28162
  const entry = req.resolve("@prisma/client");
27502
28163
  const mod = await import(pathToFileURL6(entry).href);
27503
28164
  const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
@@ -27524,7 +28185,7 @@ var init_exe_db_read = __esm({
27524
28185
  });
27525
28186
 
27526
28187
  // src/mcp/tools/crm.ts
27527
- import { z as z83 } from "zod";
28188
+ import { z as z84 } from "zod";
27528
28189
  function text(content, isError = false) {
27529
28190
  return { content: [{ type: "text", text: content }], ...isError ? { isError: true } : {} };
27530
28191
  }
@@ -27538,12 +28199,12 @@ function registerCrm(server) {
27538
28199
  title: "CRM",
27539
28200
  description: "Read-only CRM access from exe-db crm schema. Actions: list_people, get_person, list_tables, describe_table.",
27540
28201
  inputSchema: {
27541
- action: z83.enum(["list_people", "get_person", "list_tables", "describe_table"]).describe("CRM read operation"),
27542
- id: z83.string().optional().describe("CRM row/person id for get_person"),
27543
- query: z83.string().optional().describe("Text search for list_people/get_person"),
27544
- table: z83.string().optional().describe("crm schema table name for describe_table"),
27545
- limit: z83.coerce.number().int().min(1).max(50).optional().describe("Max rows, capped at 50"),
27546
- offset: z83.coerce.number().int().min(0).optional().describe("Rows to skip")
28202
+ action: z84.enum(["list_people", "get_person", "list_tables", "describe_table"]).describe("CRM read operation"),
28203
+ id: z84.string().optional().describe("CRM row/person id for get_person"),
28204
+ query: z84.string().optional().describe("Text search for list_people/get_person"),
28205
+ table: z84.string().optional().describe("crm schema table name for describe_table"),
28206
+ limit: z84.coerce.number().int().min(1).max(50).optional().describe("Max rows, capped at 50"),
28207
+ offset: z84.coerce.number().int().min(0).optional().describe("Rows to skip")
27547
28208
  }
27548
28209
  }, async ({ action, id, query, table, limit, offset }) => {
27549
28210
  try {
@@ -27608,7 +28269,7 @@ var init_crm = __esm({
27608
28269
  });
27609
28270
 
27610
28271
  // src/mcp/tools/raw-data.ts
27611
- import { z as z84 } from "zod";
28272
+ import { z as z85 } from "zod";
27612
28273
  function text2(content, isError = false) {
27613
28274
  return { content: [{ type: "text", text: content }], ...isError ? { isError: true } : {} };
27614
28275
  }
@@ -27617,15 +28278,15 @@ function registerRawData(server) {
27617
28278
  title: "Raw Data",
27618
28279
  description: "Read-only access to exe-db raw.raw_events landing pad. Actions: list_sources, query, get. Results are capped because raw payloads can consume many tokens.",
27619
28280
  inputSchema: {
27620
- action: z84.enum(["list_sources", "query", "get"]).describe("Raw data read operation"),
27621
- id: z84.string().optional().describe("raw.raw_events id for action=get"),
27622
- source: z84.string().optional().describe("Filter by raw source"),
27623
- event_type: z84.string().optional().describe("Filter by event_type"),
27624
- query: z84.string().optional().describe("Search payload/metadata text"),
27625
- processed: z84.boolean().optional().describe("Filter processed_at IS NULL/NOT NULL"),
27626
- limit: z84.coerce.number().int().min(1).max(50).optional().describe("Max rows, capped at 50"),
27627
- offset: z84.coerce.number().int().min(0).optional().describe("Rows to skip"),
27628
- include_payload: z84.boolean().optional().describe("Include full payload JSON. Default false to save tokens.")
28281
+ action: z85.enum(["list_sources", "query", "get"]).describe("Raw data read operation"),
28282
+ id: z85.string().optional().describe("raw.raw_events id for action=get"),
28283
+ source: z85.string().optional().describe("Filter by raw source"),
28284
+ event_type: z85.string().optional().describe("Filter by event_type"),
28285
+ query: z85.string().optional().describe("Search payload/metadata text"),
28286
+ processed: z85.boolean().optional().describe("Filter processed_at IS NULL/NOT NULL"),
28287
+ limit: z85.coerce.number().int().min(1).max(50).optional().describe("Max rows, capped at 50"),
28288
+ offset: z85.coerce.number().int().min(0).optional().describe("Rows to skip"),
28289
+ include_payload: z85.boolean().optional().describe("Include full payload JSON. Default false to save tokens.")
27629
28290
  }
27630
28291
  }, async ({ action, id, source, event_type, query, processed, limit, offset, include_payload }) => {
27631
28292
  try {
@@ -27693,10 +28354,10 @@ var init_raw_data = __esm({
27693
28354
  });
27694
28355
 
27695
28356
  // src/mcp/tools/create-bug-report.ts
27696
- import { z as z85 } from "zod";
28357
+ import { z as z86 } from "zod";
27697
28358
  import crypto19 from "crypto";
27698
28359
  import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
27699
- import path50 from "path";
28360
+ import path51 from "path";
27700
28361
  function slugify2(input) {
27701
28362
  return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "bug-report";
27702
28363
  }
@@ -27774,22 +28435,22 @@ function registerCreateBugReport(server) {
27774
28435
  title: "Create Bug Report",
27775
28436
  description: "Classify and file an exe-os issue as upstream_bug, customer_customization, emergency_hotfix, or unclear. Writes a local report, stores memory, and optionally sends to AskExe support when a support endpoint is configured.",
27776
28437
  inputSchema: {
27777
- title: z85.string().min(3).describe("Short descriptive title"),
28438
+ title: z86.string().min(3).describe("Short descriptive title"),
27778
28439
  classification: CLASSIFICATION.describe(
27779
28440
  "upstream_bug = platform defect; customer_customization = local preference; emergency_hotfix = temporary local patch; unclear = needs maintainer triage"
27780
28441
  ),
27781
28442
  severity: SEVERITY.default("p2").describe("p0 critical \u2192 p3 low"),
27782
- summary: z85.string().min(10).describe("What happened and why it matters"),
27783
- customer_impact: z85.string().optional().describe("How this affects the customer/founder"),
27784
- reproduction_steps: z85.array(z85.string()).optional().describe("Steps to reproduce"),
27785
- expected: z85.string().optional().describe("Expected behavior"),
27786
- actual: z85.string().optional().describe("Actual behavior"),
27787
- files_changed: z85.array(z85.string()).optional().describe("Files changed or suspected"),
27788
- workaround: z85.string().optional().describe("Temporary local workaround/hotfix, if any"),
27789
- local_patch_diff: z85.string().optional().describe("Small local diff or patch summary"),
27790
- package_version: z85.string().optional().describe("Installed @askexenow/exe-os version"),
27791
- project_name: z85.string().optional().describe("Project/customer context"),
27792
- send_upstream: z85.boolean().default(true).describe("Attempt to POST to configured AskExe support endpoint")
28443
+ summary: z86.string().min(10).describe("What happened and why it matters"),
28444
+ customer_impact: z86.string().optional().describe("How this affects the customer/founder"),
28445
+ reproduction_steps: z86.array(z86.string()).optional().describe("Steps to reproduce"),
28446
+ expected: z86.string().optional().describe("Expected behavior"),
28447
+ actual: z86.string().optional().describe("Actual behavior"),
28448
+ files_changed: z86.array(z86.string()).optional().describe("Files changed or suspected"),
28449
+ workaround: z86.string().optional().describe("Temporary local workaround/hotfix, if any"),
28450
+ local_patch_diff: z86.string().optional().describe("Small local diff or patch summary"),
28451
+ package_version: z86.string().optional().describe("Installed @askexenow/exe-os version"),
28452
+ project_name: z86.string().optional().describe("Project/customer context"),
28453
+ send_upstream: z86.boolean().default(true).describe("Attempt to POST to configured AskExe support endpoint")
27793
28454
  }
27794
28455
  },
27795
28456
  async ({
@@ -27829,9 +28490,9 @@ function registerCreateBugReport(server) {
27829
28490
  filesChanged: files_changed,
27830
28491
  projectName: project_name
27831
28492
  });
27832
- const outDir = path50.join(EXE_AI_DIR, "bug-reports");
28493
+ const outDir = path51.join(EXE_AI_DIR, "bug-reports");
27833
28494
  await mkdir6(outDir, { recursive: true });
27834
- const reportPath = path50.join(outDir, `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}-${slugify2(title)}-${id.slice(0, 8)}.md`);
28495
+ const reportPath = path51.join(outDir, `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}-${slugify2(title)}-${id.slice(0, 8)}.md`);
27835
28496
  await writeFile7(reportPath, markdown, "utf-8");
27836
28497
  let vector = null;
27837
28498
  try {
@@ -27905,18 +28566,18 @@ var init_create_bug_report = __esm({
27905
28566
  init_config();
27906
28567
  init_license();
27907
28568
  init_store();
27908
- CLASSIFICATION = z85.enum([
28569
+ CLASSIFICATION = z86.enum([
27909
28570
  "upstream_bug",
27910
28571
  "customer_customization",
27911
28572
  "emergency_hotfix",
27912
28573
  "unclear"
27913
28574
  ]);
27914
- SEVERITY = z85.enum(["p0", "p1", "p2", "p3"]);
28575
+ SEVERITY = z86.enum(["p0", "p1", "p2", "p3"]);
27915
28576
  }
27916
28577
  });
27917
28578
 
27918
28579
  // src/mcp/tools/support-inbox.ts
27919
- import { z as z86 } from "zod";
28580
+ import { z as z87 } from "zod";
27920
28581
  function adminToken() {
27921
28582
  return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN;
27922
28583
  }
@@ -27955,9 +28616,9 @@ function registerListBugReports(server) {
27955
28616
  title: "List Bug Reports",
27956
28617
  description: "AskExe-internal only: list incoming customer bug reports from the support inbox.",
27957
28618
  inputSchema: {
27958
- status: z86.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
27959
- severity: z86.enum(["p0", "p1", "p2", "p3"]).optional(),
27960
- limit: z86.number().int().min(1).max(100).default(25)
28619
+ status: z87.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
28620
+ severity: z87.enum(["p0", "p1", "p2", "p3"]).optional(),
28621
+ limit: z87.number().int().min(1).max(100).default(25)
27961
28622
  }
27962
28623
  },
27963
28624
  async ({ status, severity, limit }) => {
@@ -27976,7 +28637,7 @@ function registerGetBugReport(server) {
27976
28637
  {
27977
28638
  title: "Get Bug Report",
27978
28639
  description: "AskExe-internal only: fetch one customer bug report with full markdown payload.",
27979
- inputSchema: { id: z86.string().min(8) }
28640
+ inputSchema: { id: z87.string().min(8) }
27980
28641
  },
27981
28642
  async ({ id }) => {
27982
28643
  const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`);
@@ -27991,12 +28652,12 @@ function registerTriageBugReport(server) {
27991
28652
  title: "Triage Bug Report",
27992
28653
  description: "AskExe-internal only: update bug report status and link task/commit/release metadata.",
27993
28654
  inputSchema: {
27994
- id: z86.string().min(8),
28655
+ id: z87.string().min(8),
27995
28656
  status: STATUS.optional(),
27996
- triage_notes: z86.string().optional(),
27997
- linked_task_id: z86.string().optional(),
27998
- linked_commit: z86.string().optional(),
27999
- fixed_version: z86.string().optional()
28657
+ triage_notes: z87.string().optional(),
28658
+ linked_task_id: z87.string().optional(),
28659
+ linked_commit: z87.string().optional(),
28660
+ fixed_version: z87.string().optional()
28000
28661
  }
28001
28662
  },
28002
28663
  async ({ id, status, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
@@ -28013,7 +28674,7 @@ var init_support_inbox = __esm({
28013
28674
  "src/mcp/tools/support-inbox.ts"() {
28014
28675
  "use strict";
28015
28676
  DEFAULT_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
28016
- STATUS = z86.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
28677
+ STATUS = z87.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
28017
28678
  }
28018
28679
  });
28019
28680
 
@@ -28154,6 +28815,7 @@ var init_tool_gates = __esm({
28154
28815
  registerRunMemoryAudit: "admin",
28155
28816
  registerRunConsolidation: "admin",
28156
28817
  registerCloudSync: "admin",
28818
+ registerOrchestrationPhase: "admin",
28157
28819
  registerSetAgentConfig: "admin",
28158
28820
  registerListEmployees: "admin",
28159
28821
  registerGetAgentSpend: "admin",
@@ -28362,6 +29024,7 @@ function registerAllTools(server) {
28362
29024
  gate("registerRunMemoryAudit", registerRunMemoryAudit);
28363
29025
  gate("registerCloudSync", registerCloudSync);
28364
29026
  gate("registerBackupVps", registerBackupVps);
29027
+ gate("registerOrchestrationPhase", registerOrchestrationPhase);
28365
29028
  }
28366
29029
  if (exposeLegacyMemory) {
28367
29030
  gate("registerGetMemoryCardinality", registerGetMemoryCardinality);
@@ -28473,6 +29136,7 @@ var init_register_tools = __esm({
28473
29136
  init_raw_data();
28474
29137
  init_set_agent_config();
28475
29138
  init_list_employees();
29139
+ init_orchestration_phase2();
28476
29140
  init_create_license();
28477
29141
  init_list_licenses();
28478
29142
  init_activate_license();
@@ -28703,12 +29367,12 @@ __export(task_enforcement_exports, {
28703
29367
  runTaskEnforcementTick: () => runTaskEnforcementTick,
28704
29368
  sendNudge: () => sendNudge
28705
29369
  });
28706
- import { writeFileSync as writeFileSync22 } from "fs";
28707
- import path51 from "path";
29370
+ import { writeFileSync as writeFileSync23 } from "fs";
29371
+ import path52 from "path";
28708
29372
  function writeAuditEntry(entry) {
28709
29373
  try {
28710
29374
  const line = JSON.stringify(entry) + "\n";
28711
- writeFileSync22(AUDIT_LOG_PATH, line, { flag: "a" });
29375
+ writeFileSync23(AUDIT_LOG_PATH, line, { flag: "a" });
28712
29376
  } catch {
28713
29377
  }
28714
29378
  }
@@ -28879,7 +29543,7 @@ var init_task_enforcement = __esm({
28879
29543
  "What do you need?"
28880
29544
  ];
28881
29545
  MANAGER_ROLES = ["COO", "CTO"];
28882
- AUDIT_LOG_PATH = path51.join(
29546
+ AUDIT_LOG_PATH = path52.join(
28883
29547
  process.env.HOME ?? process.env.USERPROFILE ?? "/tmp",
28884
29548
  ".exe-os",
28885
29549
  "enforcement-audit.jsonl"
@@ -28896,11 +29560,11 @@ __export(update_check_exports, {
28896
29560
  getRemoteVersion: () => getRemoteVersion
28897
29561
  });
28898
29562
  import { execSync as execSync15 } from "child_process";
28899
- import { readFileSync as readFileSync32 } from "fs";
28900
- import path52 from "path";
29563
+ import { readFileSync as readFileSync33 } from "fs";
29564
+ import path53 from "path";
28901
29565
  function getLocalVersion(packageRoot) {
28902
- const pkgPath = path52.join(packageRoot, "package.json");
28903
- const pkg = JSON.parse(readFileSync32(pkgPath, "utf-8"));
29566
+ const pkgPath = path53.join(packageRoot, "package.json");
29567
+ const pkg = JSON.parse(readFileSync33(pkgPath, "utf-8"));
28904
29568
  return pkg.version;
28905
29569
  }
28906
29570
  function getRemoteVersion() {
@@ -28972,12 +29636,12 @@ __export(device_registry_exports, {
28972
29636
  });
28973
29637
  import crypto21 from "crypto";
28974
29638
  import os22 from "os";
28975
- import { readFileSync as readFileSync33, writeFileSync as writeFileSync23, mkdirSync as mkdirSync19, existsSync as existsSync40 } from "fs";
28976
- import path53 from "path";
29639
+ import { readFileSync as readFileSync34, writeFileSync as writeFileSync24, mkdirSync as mkdirSync20, existsSync as existsSync41 } from "fs";
29640
+ import path54 from "path";
28977
29641
  function getDeviceInfo() {
28978
- if (existsSync40(DEVICE_JSON_PATH)) {
29642
+ if (existsSync41(DEVICE_JSON_PATH)) {
28979
29643
  try {
28980
- const raw = readFileSync33(DEVICE_JSON_PATH, "utf8");
29644
+ const raw = readFileSync34(DEVICE_JSON_PATH, "utf8");
28981
29645
  const data = JSON.parse(raw);
28982
29646
  if (data.deviceId && data.friendlyName && data.hostname) {
28983
29647
  return data;
@@ -28991,14 +29655,14 @@ function getDeviceInfo() {
28991
29655
  friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
28992
29656
  hostname
28993
29657
  };
28994
- mkdirSync19(path53.dirname(DEVICE_JSON_PATH), { recursive: true });
28995
- writeFileSync23(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
29658
+ mkdirSync20(path54.dirname(DEVICE_JSON_PATH), { recursive: true });
29659
+ writeFileSync24(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
28996
29660
  return info;
28997
29661
  }
28998
29662
  function setFriendlyName(name) {
28999
29663
  const info = getDeviceInfo();
29000
29664
  info.friendlyName = name;
29001
- writeFileSync23(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
29665
+ writeFileSync24(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
29002
29666
  }
29003
29667
  async function resolveTargetDevice(targetAgent, targetProject) {
29004
29668
  const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
@@ -29032,7 +29696,7 @@ var init_device_registry = __esm({
29032
29696
  "src/lib/device-registry.ts"() {
29033
29697
  "use strict";
29034
29698
  init_config();
29035
- DEVICE_JSON_PATH = path53.join(EXE_AI_DIR, "device.json");
29699
+ DEVICE_JSON_PATH = path54.join(EXE_AI_DIR, "device.json");
29036
29700
  }
29037
29701
  });
29038
29702
 
@@ -29248,8 +29912,8 @@ import os23 from "os";
29248
29912
  import net2 from "net";
29249
29913
  import { createServer as createHttpServer } from "http";
29250
29914
  import { randomUUID as randomUUID9 } from "crypto";
29251
- import { writeFileSync as writeFileSync24, unlinkSync as unlinkSync13, mkdirSync as mkdirSync20, existsSync as existsSync41, readFileSync as readFileSync34, chmodSync as chmodSync2 } from "fs";
29252
- import path54 from "path";
29915
+ import { writeFileSync as writeFileSync25, unlinkSync as unlinkSync13, mkdirSync as mkdirSync21, existsSync as existsSync42, readFileSync as readFileSync35, chmodSync as chmodSync2 } from "fs";
29916
+ import path55 from "path";
29253
29917
 
29254
29918
  // src/lib/orchestration-metrics.ts
29255
29919
  init_config();
@@ -29321,8 +29985,8 @@ function initMetrics() {
29321
29985
 
29322
29986
  // src/lib/exe-daemon.ts
29323
29987
  init_memory_write_governor();
29324
- var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path54.join(EXE_AI_DIR, "exed.sock");
29325
- var PID_PATH3 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path54.join(EXE_AI_DIR, "exed.pid");
29988
+ var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path55.join(EXE_AI_DIR, "exed.sock");
29989
+ var PID_PATH3 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path55.join(EXE_AI_DIR, "exed.pid");
29326
29990
  var MODEL_FILE = "jina-embeddings-v5-small-q4_k_m.gguf";
29327
29991
  var IDLE_TIMEOUT_MS2 = 15 * 60 * 1e3;
29328
29992
  var REVIEW_POLL_INTERVAL_MS = 60 * 1e3;
@@ -29350,8 +30014,8 @@ function enqueue(queue, entry) {
29350
30014
  queue.push(entry);
29351
30015
  }
29352
30016
  async function loadModel() {
29353
- const modelPath = path54.join(MODELS_DIR, MODEL_FILE);
29354
- if (!existsSync41(modelPath)) {
30017
+ const modelPath = path55.join(MODELS_DIR, MODEL_FILE);
30018
+ if (!existsSync42(modelPath)) {
29355
30019
  process.stderr.write(`[exed] No model at ${modelPath} \u2014 running without embeddings (VPS mode).
29356
30020
  `);
29357
30021
  return;
@@ -29749,17 +30413,17 @@ function startMemoryQueueDrain() {
29749
30413
  `);
29750
30414
  }
29751
30415
  function startServer() {
29752
- mkdirSync20(path54.dirname(SOCKET_PATH2), { recursive: true });
30416
+ mkdirSync21(path55.dirname(SOCKET_PATH2), { recursive: true });
29753
30417
  try {
29754
- chmodSync2(path54.dirname(SOCKET_PATH2), 448);
30418
+ chmodSync2(path55.dirname(SOCKET_PATH2), 448);
29755
30419
  } catch {
29756
30420
  }
29757
30421
  _daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV2] ?? null);
29758
30422
  for (const oldFile of ["embed.sock", "embed.pid"]) {
29759
- const oldPath = path54.join(path54.dirname(SOCKET_PATH2), oldFile);
30423
+ const oldPath = path55.join(path55.dirname(SOCKET_PATH2), oldFile);
29760
30424
  try {
29761
30425
  if (oldFile.endsWith(".pid")) {
29762
- const pid = parseInt(readFileSync34(oldPath, "utf8").trim(), 10);
30426
+ const pid = parseInt(readFileSync35(oldPath, "utf8").trim(), 10);
29763
30427
  if (pid > 0) try {
29764
30428
  process.kill(pid, "SIGKILL");
29765
30429
  } catch {
@@ -30232,7 +30896,7 @@ function startGraphExtraction() {
30232
30896
  `);
30233
30897
  }
30234
30898
  var AGENT_STATS_INTERVAL_MS = 60 * 1e3;
30235
- var AGENT_STATS_PATH = path54.join(EXE_AI_DIR, "agent-stats.json");
30899
+ var AGENT_STATS_PATH = path55.join(EXE_AI_DIR, "agent-stats.json");
30236
30900
  async function writeAgentStats() {
30237
30901
  fired("agent_stats");
30238
30902
  if (!await ensureStoreForPolling()) return;
@@ -30292,7 +30956,7 @@ async function writeAgentStats() {
30292
30956
  pid: process.pid
30293
30957
  }
30294
30958
  };
30295
- writeFileSync24(AGENT_STATS_PATH, JSON.stringify(stats, null, 2), "utf8");
30959
+ writeFileSync25(AGENT_STATS_PATH, JSON.stringify(stats, null, 2), "utf8");
30296
30960
  } catch (err) {
30297
30961
  process.stderr.write(`[exed] Agent stats error: ${err instanceof Error ? err.message : String(err)}
30298
30962
  `);
@@ -30409,12 +31073,12 @@ function startIntercomQueueDrain() {
30409
31073
  const hasInProgressTask = (session) => {
30410
31074
  try {
30411
31075
  const { baseAgentName: ban } = (init_employees(), __toCommonJS(employees_exports));
30412
- const path55 = __require("path");
30413
- const { existsSync: existsSync42 } = __require("fs");
31076
+ const path56 = __require("path");
31077
+ const { existsSync: existsSync43 } = __require("fs");
30414
31078
  const os24 = __require("os");
30415
31079
  const agent = ban(session.split("-")[0] ?? session);
30416
- const markerPath = path55.join(os24.homedir(), ".exe-os", "session-cache", `current-task-${agent}.json`);
30417
- return existsSync42(markerPath);
31080
+ const markerPath = path56.join(os24.homedir(), ".exe-os", "session-cache", `current-task-${agent}.json`);
31081
+ return existsSync43(markerPath);
30418
31082
  } catch {
30419
31083
  return false;
30420
31084
  }
@@ -30578,8 +31242,8 @@ process.on("SIGINT", () => void shutdown());
30578
31242
  process.on("SIGTERM", () => void shutdown());
30579
31243
  function checkExistingDaemon() {
30580
31244
  try {
30581
- if (!existsSync41(PID_PATH3)) return false;
30582
- const pid = parseInt(readFileSync34(PID_PATH3, "utf8").trim(), 10);
31245
+ if (!existsSync42(PID_PATH3)) return false;
31246
+ const pid = parseInt(readFileSync35(PID_PATH3, "utf8").trim(), 10);
30583
31247
  if (!pid || isNaN(pid)) return false;
30584
31248
  process.kill(pid, 0);
30585
31249
  process.stderr.write(`[exed] Another daemon is already running (PID ${pid}). Exiting.
@@ -30649,7 +31313,7 @@ function startAutoUpdateCheck() {
30649
31313
  if (checkExistingDaemon()) {
30650
31314
  process.exit(0);
30651
31315
  }
30652
- writeFileSync24(PID_PATH3, String(process.pid));
31316
+ writeFileSync25(PID_PATH3, String(process.pid));
30653
31317
  try {
30654
31318
  chmodSync2(PID_PATH3, 384);
30655
31319
  } catch {