@askexenow/exe-os 0.9.0 → 0.9.2

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.
@@ -698,10 +698,10 @@ async function disposeEmbedder() {
698
698
  async function embedDirect(text) {
699
699
  const llamaCpp = await import("node-llama-cpp");
700
700
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
701
- const { existsSync: existsSync31 } = await import("fs");
702
- const path38 = await import("path");
703
- const modelPath = path38.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
704
- if (!existsSync31(modelPath)) {
701
+ const { existsSync: existsSync32 } = await import("fs");
702
+ const path40 = await import("path");
703
+ const modelPath = path40.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
704
+ if (!existsSync32(modelPath)) {
705
705
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
706
706
  }
707
707
  const llama = await llamaCpp.getLlama();
@@ -6108,7 +6108,7 @@ var init_agent_config = __esm({
6108
6108
  AGENT_CONFIG_PATH = path16.join(EXE_AI_DIR, "agent-config.json");
6109
6109
  KNOWN_RUNTIMES = {
6110
6110
  claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
6111
- codex: ["gpt-5.4", "gpt-5.5", "o3", "o4-mini"],
6111
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
6112
6112
  opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
6113
6113
  };
6114
6114
  RUNTIME_LABELS = {
@@ -6125,6 +6125,14 @@ var init_agent_config = __esm({
6125
6125
  });
6126
6126
 
6127
6127
  // src/lib/intercom-queue.ts
6128
+ var intercom_queue_exports = {};
6129
+ __export(intercom_queue_exports, {
6130
+ clearQueueForAgent: () => clearQueueForAgent,
6131
+ drainForSession: () => drainForSession,
6132
+ drainQueue: () => drainQueue,
6133
+ queueIntercom: () => queueIntercom,
6134
+ readQueue: () => readQueue
6135
+ });
6128
6136
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, renameSync as renameSync3, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
6129
6137
  import path17 from "path";
6130
6138
  import os7 from "os";
@@ -6163,11 +6171,80 @@ function queueIntercom(targetSession, reason) {
6163
6171
  }
6164
6172
  writeQueue(queue);
6165
6173
  }
6166
- var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
6174
+ function drainQueue(isSessionBusy2, sendKeys) {
6175
+ const queue = readQueue();
6176
+ if (queue.length === 0) return { drained: 0, failed: 0 };
6177
+ const remaining = [];
6178
+ let drained = 0;
6179
+ let failed = 0;
6180
+ for (const item of queue) {
6181
+ const age = Date.now() - new Date(item.queuedAt).getTime();
6182
+ if (age > TTL_MS) {
6183
+ logQueue(`EXPIRED \u2192 ${item.targetSession} (${Math.round(age / 6e4)}min old, reason: ${item.reason})`);
6184
+ failed++;
6185
+ continue;
6186
+ }
6187
+ try {
6188
+ if (!isSessionBusy2(item.targetSession)) {
6189
+ const success = sendKeys(item.targetSession);
6190
+ if (success) {
6191
+ logQueue(`DRAINED \u2192 ${item.targetSession} (after ${item.attempts} retries)`);
6192
+ drained++;
6193
+ continue;
6194
+ }
6195
+ }
6196
+ } catch {
6197
+ }
6198
+ item.attempts++;
6199
+ if (item.attempts >= MAX_RETRIES2) {
6200
+ logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES2} retries exhausted, reason: ${item.reason})`);
6201
+ failed++;
6202
+ continue;
6203
+ }
6204
+ remaining.push(item);
6205
+ }
6206
+ writeQueue(remaining);
6207
+ return { drained, failed };
6208
+ }
6209
+ function drainForSession(targetSession, sendKeys) {
6210
+ const queue = readQueue();
6211
+ const match = queue.findIndex((q) => q.targetSession === targetSession);
6212
+ if (match < 0) return false;
6213
+ const success = sendKeys(targetSession);
6214
+ if (success) {
6215
+ queue.splice(match, 1);
6216
+ writeQueue(queue);
6217
+ logQueue(`DRAINED \u2192 ${targetSession} (prompt-submit trigger)`);
6218
+ return true;
6219
+ }
6220
+ return false;
6221
+ }
6222
+ function clearQueueForAgent(agentName) {
6223
+ const queue = readQueue();
6224
+ const before = queue.length;
6225
+ const filtered = queue.filter((q) => !q.targetSession.startsWith(`${agentName}-`));
6226
+ if (filtered.length < before) {
6227
+ writeQueue(filtered);
6228
+ logQueue(`CLEARED ${before - filtered.length} stale item(s) for ${agentName}`);
6229
+ }
6230
+ }
6231
+ function logQueue(msg) {
6232
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [queue] ${msg}
6233
+ `;
6234
+ process.stderr.write(`[intercom-queue] ${msg}
6235
+ `);
6236
+ try {
6237
+ const { appendFileSync: appendFileSync3 } = __require("fs");
6238
+ appendFileSync3(INTERCOM_LOG, line);
6239
+ } catch {
6240
+ }
6241
+ }
6242
+ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
6167
6243
  var init_intercom_queue = __esm({
6168
6244
  "src/lib/intercom-queue.ts"() {
6169
6245
  "use strict";
6170
6246
  QUEUE_PATH = path17.join(os7.homedir(), ".exe-os", "intercom-queue.json");
6247
+ MAX_RETRIES2 = 5;
6171
6248
  TTL_MS = 60 * 60 * 1e3;
6172
6249
  INTERCOM_LOG = path17.join(os7.homedir(), ".exe-os", "intercom.log");
6173
6250
  }
@@ -7806,6 +7883,13 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
7806
7883
  await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
7807
7884
  } catch {
7808
7885
  }
7886
+ if (input.status === "done" || input.status === "cancelled") {
7887
+ try {
7888
+ const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
7889
+ clearQueueForAgent2(String(row.assigned_to));
7890
+ } catch {
7891
+ }
7892
+ }
7809
7893
  try {
7810
7894
  await writeCheckpoint({
7811
7895
  taskId,
@@ -9651,7 +9735,7 @@ async function deliverLocalMessage(messageId) {
9651
9735
  return true;
9652
9736
  } catch {
9653
9737
  const newRetryCount = msg.retryCount + 1;
9654
- if (newRetryCount >= MAX_RETRIES2) {
9738
+ if (newRetryCount >= MAX_RETRIES3) {
9655
9739
  await markFailed(messageId, "session unavailable after 10 retries");
9656
9740
  } else {
9657
9741
  await client.execute({
@@ -9669,13 +9753,13 @@ async function markFailed(messageId, reason) {
9669
9753
  args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId]
9670
9754
  });
9671
9755
  }
9672
- var MAX_RETRIES2, _wsClientSend;
9756
+ var MAX_RETRIES3, _wsClientSend;
9673
9757
  var init_messaging = __esm({
9674
9758
  "src/lib/messaging.ts"() {
9675
9759
  "use strict";
9676
9760
  init_database();
9677
9761
  init_tmux_routing();
9678
- MAX_RETRIES2 = 10;
9762
+ MAX_RETRIES3 = 10;
9679
9763
  _wsClientSend = null;
9680
9764
  }
9681
9765
  });
@@ -9733,7 +9817,7 @@ __export(consolidation_exports, {
9733
9817
  selectUnconsolidated: () => selectUnconsolidated,
9734
9818
  storeConsolidation: () => storeConsolidation
9735
9819
  });
9736
- import { randomUUID as randomUUID5 } from "crypto";
9820
+ import { randomUUID as randomUUID6 } from "crypto";
9737
9821
  async function selectUnconsolidated(client, limit = 200) {
9738
9822
  const result = await client.execute({
9739
9823
  sql: `SELECT id, agent_id, project_name, tool_name, raw_text, timestamp
@@ -9829,7 +9913,7 @@ async function consolidateCluster(cluster, model) {
9829
9913
  return textBlock?.text ?? "";
9830
9914
  }
9831
9915
  async function storeConsolidation(client, cluster, synthesisText, embedFn) {
9832
- const consolidatedId = randomUUID5();
9916
+ const consolidatedId = randomUUID6();
9833
9917
  const now = (/* @__PURE__ */ new Date()).toISOString();
9834
9918
  const rawText = `CONSOLIDATION [${cluster.dateRange}, ${cluster.projectName}]:
9835
9919
 
@@ -9854,7 +9938,7 @@ ${synthesisText}`;
9854
9938
  const linkStmts = sourceIds.map((sourceId) => ({
9855
9939
  sql: `INSERT INTO consolidations (id, consolidated_memory_id, source_memory_id, created_at)
9856
9940
  VALUES (?, ?, ?, ?)`,
9857
- args: [randomUUID5(), consolidatedId, sourceId, now]
9941
+ args: [randomUUID6(), consolidatedId, sourceId, now]
9858
9942
  }));
9859
9943
  const placeholders = sourceIds.map(() => "?").join(",");
9860
9944
  const markStmt = {
@@ -10100,14 +10184,14 @@ __export(wiki_client_exports, {
10100
10184
  listDocuments: () => listDocuments,
10101
10185
  listWorkspaces: () => listWorkspaces
10102
10186
  });
10103
- async function wikiFetch(config2, path38, method = "GET", body) {
10104
- const url = `${config2.baseUrl}/api/v1${path38}`;
10187
+ async function wikiFetch(config2, path40, method = "GET", body) {
10188
+ const url = `${config2.baseUrl}/api/v1${path40}`;
10105
10189
  const headers = {
10106
10190
  Authorization: `Bearer ${config2.apiKey}`,
10107
10191
  "Content-Type": "application/json"
10108
10192
  };
10109
10193
  const controller = new AbortController();
10110
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
10194
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS3);
10111
10195
  try {
10112
10196
  let response;
10113
10197
  try {
@@ -10120,7 +10204,7 @@ async function wikiFetch(config2, path38, method = "GET", body) {
10120
10204
  } catch {
10121
10205
  clearTimeout(timeout);
10122
10206
  const retryController = new AbortController();
10123
- const retryTimeout = setTimeout(() => retryController.abort(), REQUEST_TIMEOUT_MS2);
10207
+ const retryTimeout = setTimeout(() => retryController.abort(), REQUEST_TIMEOUT_MS3);
10124
10208
  try {
10125
10209
  await new Promise((r) => setTimeout(r, 500));
10126
10210
  response = await fetch(url, {
@@ -10134,7 +10218,7 @@ async function wikiFetch(config2, path38, method = "GET", body) {
10134
10218
  }
10135
10219
  }
10136
10220
  if (!response.ok) {
10137
- throw new Error(`Wiki API ${method} ${path38}: ${response.status} ${response.statusText}`);
10221
+ throw new Error(`Wiki API ${method} ${path40}: ${response.status} ${response.statusText}`);
10138
10222
  }
10139
10223
  return response.json();
10140
10224
  } finally {
@@ -10224,12 +10308,12 @@ async function getChatHistory(client, workspaceSlug, limit = 50) {
10224
10308
  sentAt: h.sentAt ?? 0
10225
10309
  }));
10226
10310
  }
10227
- var LOCAL_WIKI_URL, REQUEST_TIMEOUT_MS2;
10311
+ var LOCAL_WIKI_URL, REQUEST_TIMEOUT_MS3;
10228
10312
  var init_wiki_client = __esm({
10229
10313
  "src/lib/wiki-client.ts"() {
10230
10314
  "use strict";
10231
10315
  LOCAL_WIKI_URL = process.env.EXE_WIKI_URL || "http://localhost:3001";
10232
- REQUEST_TIMEOUT_MS2 = 8e3;
10316
+ REQUEST_TIMEOUT_MS3 = 8e3;
10233
10317
  }
10234
10318
  });
10235
10319
 
@@ -10243,14 +10327,14 @@ __export(worker_gate_exports, {
10243
10327
  tryAcquireBackfillLock: () => tryAcquireBackfillLock,
10244
10328
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
10245
10329
  });
10246
- import { readdirSync as readdirSync9, writeFileSync as writeFileSync15, unlinkSync as unlinkSync8, mkdirSync as mkdirSync12, existsSync as existsSync24 } from "fs";
10247
- import path31 from "path";
10330
+ import { readdirSync as readdirSync9, writeFileSync as writeFileSync17, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync25 } from "fs";
10331
+ import path33 from "path";
10248
10332
  function tryAcquireWorkerSlot() {
10249
10333
  try {
10250
- mkdirSync12(WORKER_PID_DIR, { recursive: true });
10334
+ mkdirSync14(WORKER_PID_DIR, { recursive: true });
10251
10335
  const reservationId = `res-${process.pid}-${Date.now()}`;
10252
- const reservationPath = path31.join(WORKER_PID_DIR, `${reservationId}.pid`);
10253
- writeFileSync15(reservationPath, String(process.pid));
10336
+ const reservationPath = path33.join(WORKER_PID_DIR, `${reservationId}.pid`);
10337
+ writeFileSync17(reservationPath, String(process.pid));
10254
10338
  const files = readdirSync9(WORKER_PID_DIR);
10255
10339
  let alive = 0;
10256
10340
  for (const f of files) {
@@ -10267,7 +10351,7 @@ function tryAcquireWorkerSlot() {
10267
10351
  alive++;
10268
10352
  } catch {
10269
10353
  try {
10270
- unlinkSync8(path31.join(WORKER_PID_DIR, f));
10354
+ unlinkSync8(path33.join(WORKER_PID_DIR, f));
10271
10355
  } catch {
10272
10356
  }
10273
10357
  }
@@ -10290,21 +10374,21 @@ function tryAcquireWorkerSlot() {
10290
10374
  }
10291
10375
  function registerWorkerPid(pid) {
10292
10376
  try {
10293
- mkdirSync12(WORKER_PID_DIR, { recursive: true });
10294
- writeFileSync15(path31.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
10377
+ mkdirSync14(WORKER_PID_DIR, { recursive: true });
10378
+ writeFileSync17(path33.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
10295
10379
  } catch {
10296
10380
  }
10297
10381
  }
10298
10382
  function cleanupWorkerPid() {
10299
10383
  try {
10300
- unlinkSync8(path31.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
10384
+ unlinkSync8(path33.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
10301
10385
  } catch {
10302
10386
  }
10303
10387
  }
10304
10388
  function tryAcquireBackfillLock() {
10305
10389
  try {
10306
- mkdirSync12(WORKER_PID_DIR, { recursive: true });
10307
- if (existsSync24(BACKFILL_LOCK)) {
10390
+ mkdirSync14(WORKER_PID_DIR, { recursive: true });
10391
+ if (existsSync25(BACKFILL_LOCK)) {
10308
10392
  try {
10309
10393
  const pid = parseInt(
10310
10394
  __require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
@@ -10320,7 +10404,7 @@ function tryAcquireBackfillLock() {
10320
10404
  } catch {
10321
10405
  }
10322
10406
  }
10323
- writeFileSync15(BACKFILL_LOCK, String(process.pid));
10407
+ writeFileSync17(BACKFILL_LOCK, String(process.pid));
10324
10408
  return true;
10325
10409
  } catch {
10326
10410
  return true;
@@ -10337,9 +10421,9 @@ var init_worker_gate = __esm({
10337
10421
  "src/lib/worker-gate.ts"() {
10338
10422
  "use strict";
10339
10423
  init_config();
10340
- WORKER_PID_DIR = path31.join(EXE_AI_DIR, "worker-pids");
10424
+ WORKER_PID_DIR = path33.join(EXE_AI_DIR, "worker-pids");
10341
10425
  MAX_CONCURRENT_WORKERS = 3;
10342
- BACKFILL_LOCK = path31.join(WORKER_PID_DIR, "backfill.lock");
10426
+ BACKFILL_LOCK = path33.join(WORKER_PID_DIR, "backfill.lock");
10343
10427
  }
10344
10428
  });
10345
10429
 
@@ -10362,8 +10446,8 @@ __export(crdt_sync_exports, {
10362
10446
  rebuildFromDb: () => rebuildFromDb
10363
10447
  });
10364
10448
  import * as Y from "yjs";
10365
- import { readFileSync as readFileSync21, writeFileSync as writeFileSync16, existsSync as existsSync27, mkdirSync as mkdirSync13, unlinkSync as unlinkSync9 } from "fs";
10366
- import path34 from "path";
10449
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync18, existsSync as existsSync28, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9 } from "fs";
10450
+ import path36 from "path";
10367
10451
  import { homedir as homedir5 } from "os";
10368
10452
  function getStatePath() {
10369
10453
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -10375,9 +10459,9 @@ function initCrdtDoc() {
10375
10459
  if (doc) return doc;
10376
10460
  doc = new Y.Doc();
10377
10461
  const sp = getStatePath();
10378
- if (existsSync27(sp)) {
10462
+ if (existsSync28(sp)) {
10379
10463
  try {
10380
- const state = readFileSync21(sp);
10464
+ const state = readFileSync24(sp);
10381
10465
  Y.applyUpdate(doc, new Uint8Array(state));
10382
10466
  } catch {
10383
10467
  console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
@@ -10519,10 +10603,10 @@ function persistState() {
10519
10603
  if (!doc) return;
10520
10604
  try {
10521
10605
  const sp = getStatePath();
10522
- const dir = path34.dirname(sp);
10523
- if (!existsSync27(dir)) mkdirSync13(dir, { recursive: true });
10606
+ const dir = path36.dirname(sp);
10607
+ if (!existsSync28(dir)) mkdirSync15(dir, { recursive: true });
10524
10608
  const state = Y.encodeStateAsUpdate(doc);
10525
- writeFileSync16(sp, Buffer.from(state));
10609
+ writeFileSync18(sp, Buffer.from(state));
10526
10610
  } catch {
10527
10611
  }
10528
10612
  }
@@ -10563,7 +10647,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
10563
10647
  var init_crdt_sync = __esm({
10564
10648
  "src/lib/crdt-sync.ts"() {
10565
10649
  "use strict";
10566
- DEFAULT_STATE_PATH = path34.join(homedir5(), ".exe-os", "crdt-state.bin");
10650
+ DEFAULT_STATE_PATH = path36.join(homedir5(), ".exe-os", "crdt-state.bin");
10567
10651
  _statePathOverride = null;
10568
10652
  doc = null;
10569
10653
  }
@@ -10576,8 +10660,8 @@ init_database();
10576
10660
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10577
10661
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10578
10662
  import { spawn as spawn3 } from "child_process";
10579
- import { existsSync as existsSync30, openSync as openSync3, mkdirSync as mkdirSync15, closeSync as closeSync3 } from "fs";
10580
- import path37 from "path";
10663
+ import { existsSync as existsSync31, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
10664
+ import path39 from "path";
10581
10665
  import { fileURLToPath as fileURLToPath5 } from "url";
10582
10666
 
10583
10667
  // src/mcp/tools/recall-my-memory.ts
@@ -11247,10 +11331,10 @@ function registerCreateTask(server2) {
11247
11331
  skipDispatch: true
11248
11332
  });
11249
11333
  try {
11250
- const { existsSync: existsSync31, mkdirSync: mkdirSync16, writeFileSync: writeFileSync18 } = await import("fs");
11334
+ const { existsSync: existsSync32, mkdirSync: mkdirSync18, writeFileSync: writeFileSync20 } = await import("fs");
11251
11335
  const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
11252
11336
  const idPath = identityPath2(assigned_to);
11253
- if (!existsSync31(idPath)) {
11337
+ if (!existsSync32(idPath)) {
11254
11338
  const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
11255
11339
  const employees = await loadEmployees2();
11256
11340
  const emp = employees.find((e) => e.name === assigned_to);
@@ -11259,8 +11343,8 @@ function registerCreateTask(server2) {
11259
11343
  const template = getTemplateForTitle2(emp.role);
11260
11344
  if (template) {
11261
11345
  const dir = (await import("path")).dirname(idPath);
11262
- if (!existsSync31(dir)) mkdirSync16(dir, { recursive: true });
11263
- writeFileSync18(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
11346
+ if (!existsSync32(dir)) mkdirSync18(dir, { recursive: true });
11347
+ writeFileSync20(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
11264
11348
  }
11265
11349
  }
11266
11350
  }
@@ -14605,9 +14689,9 @@ var HostingerApiClient = class {
14605
14689
  }
14606
14690
  this.lastRequestTime = Date.now();
14607
14691
  }
14608
- async request(method, path38, body) {
14692
+ async request(method, path40, body) {
14609
14693
  await this.rateLimit();
14610
- const url = `${this.baseUrl}${path38}`;
14694
+ const url = `${this.baseUrl}${path40}`;
14611
14695
  const headers = {
14612
14696
  Authorization: `Bearer ${this.apiKey}`,
14613
14697
  "Content-Type": "application/json",
@@ -14645,13 +14729,123 @@ var HostingerError = class extends Error {
14645
14729
  }
14646
14730
  };
14647
14731
 
14732
+ // src/lib/cloudflare-dns.ts
14733
+ var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
14734
+ var DNS_RECORD_TYPE_A = "A";
14735
+ var DEFAULT_PROXIED = true;
14736
+ var DEFAULT_TTL = 1;
14737
+ var REQUEST_TIMEOUT_MS2 = 3e4;
14738
+ var UNKNOWN_ERROR_CODE = "unknown";
14739
+ async function createARecord(cfApiToken, zoneId, domain, ip, opts) {
14740
+ const proxied = opts?.proxied ?? DEFAULT_PROXIED;
14741
+ const ttl = opts?.ttl ?? DEFAULT_TTL;
14742
+ const response = await requestCloudflare(cfApiToken, zoneId, {
14743
+ method: "POST",
14744
+ body: {
14745
+ type: DNS_RECORD_TYPE_A,
14746
+ name: domain,
14747
+ content: ip,
14748
+ proxied,
14749
+ ttl
14750
+ }
14751
+ });
14752
+ return mapDnsRecord(response);
14753
+ }
14754
+ async function requestCloudflare(cfApiToken, zoneId, options) {
14755
+ const response = await fetch(buildUrl(zoneId, options.path, options.query), {
14756
+ method: options.method,
14757
+ headers: buildHeaders(cfApiToken),
14758
+ body: options.body ? JSON.stringify(options.body) : void 0,
14759
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
14760
+ });
14761
+ const envelope = await parseEnvelope(response);
14762
+ if (!response.ok || !envelope.success) {
14763
+ throw toCloudflareError(response, envelope);
14764
+ }
14765
+ return envelope.result;
14766
+ }
14767
+ function buildUrl(zoneId, path40 = "/dns_records", query) {
14768
+ const normalizedPath = path40.startsWith("/") ? path40 : `/${path40}`;
14769
+ const url = new URL(
14770
+ `${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
14771
+ );
14772
+ if (query) {
14773
+ url.search = query.toString();
14774
+ }
14775
+ return url.toString();
14776
+ }
14777
+ function buildHeaders(cfApiToken) {
14778
+ return {
14779
+ Authorization: `Bearer ${cfApiToken}`,
14780
+ "Content-Type": "application/json",
14781
+ Accept: "application/json"
14782
+ };
14783
+ }
14784
+ async function parseEnvelope(response) {
14785
+ try {
14786
+ return await response.json();
14787
+ } catch {
14788
+ return {
14789
+ success: false,
14790
+ errors: [
14791
+ {
14792
+ code: UNKNOWN_ERROR_CODE,
14793
+ message: `HTTP ${response.status}: ${response.statusText}`
14794
+ }
14795
+ ],
14796
+ messages: [],
14797
+ result: null
14798
+ };
14799
+ }
14800
+ }
14801
+ function toCloudflareError(response, envelope) {
14802
+ const firstError = envelope.errors[0];
14803
+ const errorCode = String(firstError?.code ?? UNKNOWN_ERROR_CODE);
14804
+ const message = firstError?.message ?? `HTTP ${response.status}: ${response.statusText}`;
14805
+ return new CloudflareError(response.status, errorCode, message);
14806
+ }
14807
+ function mapDnsRecord(record) {
14808
+ return {
14809
+ recordId: record.id,
14810
+ name: record.name,
14811
+ ip: record.content,
14812
+ proxied: record.proxied
14813
+ };
14814
+ }
14815
+ var CloudflareError = class extends Error {
14816
+ status;
14817
+ errorCode;
14818
+ constructor(status, errorCode, message) {
14819
+ super(message);
14820
+ this.name = "CloudflareError";
14821
+ this.status = status;
14822
+ this.errorCode = errorCode;
14823
+ }
14824
+ };
14825
+
14648
14826
  // src/mcp/tools/deploy-client.ts
14649
14827
  init_config();
14650
14828
  var execFileAsync = promisify(execFile);
14651
14829
  var POLL_INTERVAL_MS = 1e4;
14652
14830
  var POLL_MAX_ATTEMPTS = 60;
14831
+ var DEFAULT_VPS_TEMPLATE = "ubuntu-24.04";
14832
+ var DNS_PROXIED_OPTIONS = { proxied: true };
14833
+ var DEPLOYED_STATUS = "deployed";
14834
+ var PLAYBOOK_COMPLETE_HEALTH_PENDING_STATUS = "playbook_complete_health_pending";
14835
+ var ACTIVE_INVENTORY_STATUS = "active";
14836
+ var PROVISIONING_INVENTORY_STATUS = "provisioning";
14653
14837
  async function executeDeployment(params, client) {
14654
- const { client_name, domain, region, plan, ssl_email, apiKey } = params;
14838
+ const {
14839
+ client_name,
14840
+ domain,
14841
+ region,
14842
+ plan,
14843
+ ssl_email,
14844
+ user_id,
14845
+ apiKey,
14846
+ cfApiToken,
14847
+ cfZoneId
14848
+ } = params;
14655
14849
  const hostinger = client ?? new HostingerApiClient(apiKey);
14656
14850
  const hostname = `exe-${client_name.toLowerCase().replace(/[^a-z0-9-]/g, "-")}`;
14657
14851
  process.stderr.write(`[deploy_client] Provisioning VPS: ${hostname} (${plan}, ${region})...
@@ -14660,7 +14854,7 @@ async function executeDeployment(params, client) {
14660
14854
  hostname,
14661
14855
  plan,
14662
14856
  region,
14663
- os_template: "ubuntu-24.04"
14857
+ os_template: DEFAULT_VPS_TEMPLATE
14664
14858
  });
14665
14859
  process.stderr.write(`[deploy_client] VPS created (ID: ${created.id}). Waiting for ready state...
14666
14860
  `);
@@ -14668,9 +14862,27 @@ async function executeDeployment(params, client) {
14668
14862
  process.stderr.write(`[deploy_client] VPS ready at ${vps.ip_address}. Running Ansible playbook...
14669
14863
  `);
14670
14864
  const playbookResult = await runAnsiblePlaybook(vps.ip_address, domain, ssl_email, client_name);
14865
+ const dnsRecordId = await createDnsRecordIfConfigured({
14866
+ domain,
14867
+ ipAddress: vps.ip_address,
14868
+ cfApiToken,
14869
+ cfZoneId
14870
+ });
14671
14871
  process.stderr.write(`[deploy_client] Playbook complete. Verifying health...
14672
14872
  `);
14673
14873
  const healthy = await verifyHealth(domain);
14874
+ const status = healthy ? DEPLOYED_STATUS : PLAYBOOK_COMPLETE_HEALTH_PENDING_STATUS;
14875
+ process.stderr.write("[deploy_client] Recording VPS inventory...\n");
14876
+ const inventory = buildInventoryRecord({
14877
+ userId: user_id,
14878
+ clientName: client_name,
14879
+ vpsId: vps.id,
14880
+ ipAddress: vps.ip_address,
14881
+ domain,
14882
+ region: vps.region,
14883
+ plan: vps.plan,
14884
+ healthy
14885
+ });
14674
14886
  return {
14675
14887
  vps_id: vps.id,
14676
14888
  hostname: vps.hostname,
@@ -14680,8 +14892,10 @@ async function executeDeployment(params, client) {
14680
14892
  plan: vps.plan,
14681
14893
  ssh_port: vps.ssh_port,
14682
14894
  ssh_access: `ssh exeai@${vps.ip_address}`,
14683
- status: healthy ? "deployed" : "playbook_complete_health_pending",
14684
- ansible_output: playbookResult
14895
+ status,
14896
+ ansible_output: playbookResult,
14897
+ dns_record_id: dnsRecordId,
14898
+ inventory
14685
14899
  };
14686
14900
  }
14687
14901
  function registerDeployClient(server2) {
@@ -14695,12 +14909,15 @@ function registerDeployClient(server2) {
14695
14909
  domain: z37.string().min(1).describe("Domain name for the deployment (e.g., client.exe.ai)"),
14696
14910
  region: z37.string().default("jakarta").describe("VPS region (default: jakarta)"),
14697
14911
  plan: z37.string().default("kvm-2").describe("Hostinger VPS plan (default: kvm-2)"),
14698
- ssl_email: z37.string().email().describe("Email for Let's Encrypt SSL certificate")
14912
+ ssl_email: z37.string().email().describe("Email for Let's Encrypt SSL certificate"),
14913
+ user_id: z37.string().min(1).describe("User/customer ID for inventory tracking")
14699
14914
  }
14700
14915
  },
14701
- async ({ client_name, domain, region, plan, ssl_email }) => {
14916
+ async ({ client_name, domain, region, plan, ssl_email, user_id }) => {
14702
14917
  const config2 = await loadConfig();
14703
14918
  const apiKey = config2.hostinger?.apiKey;
14919
+ const cfApiToken = config2.cloudflare?.apiToken;
14920
+ const cfZoneId = config2.cloudflare?.zoneId;
14704
14921
  if (!apiKey) {
14705
14922
  return {
14706
14923
  content: [
@@ -14718,7 +14935,10 @@ function registerDeployClient(server2) {
14718
14935
  region,
14719
14936
  plan,
14720
14937
  ssl_email,
14721
- apiKey
14938
+ user_id,
14939
+ apiKey,
14940
+ cfApiToken,
14941
+ cfZoneId
14722
14942
  });
14723
14943
  return {
14724
14944
  content: [
@@ -14816,10 +15036,467 @@ async function verifyHealth(domain) {
14816
15036
  return false;
14817
15037
  }
14818
15038
  }
15039
+ async function createDnsRecordIfConfigured(params) {
15040
+ const { domain, ipAddress, cfApiToken, cfZoneId } = params;
15041
+ if (!cfApiToken || !cfZoneId) {
15042
+ process.stderr.write(
15043
+ `[deploy_client] Warning: Cloudflare config missing. Skipping DNS setup for ${domain}; configure cloudflare.apiToken and cloudflare.zoneId to automate this step.
15044
+ `
15045
+ );
15046
+ return void 0;
15047
+ }
15048
+ process.stderr.write(`[deploy_client] Setting up DNS: ${domain} \u2192 ${ipAddress}...
15049
+ `);
15050
+ const dnsResult = await createARecord(
15051
+ cfApiToken,
15052
+ cfZoneId,
15053
+ domain,
15054
+ ipAddress,
15055
+ DNS_PROXIED_OPTIONS
15056
+ );
15057
+ return dnsResult.recordId;
15058
+ }
15059
+ function buildInventoryRecord(params) {
15060
+ const { userId, clientName, vpsId, ipAddress, domain, region, plan, healthy } = params;
15061
+ return {
15062
+ user_id: userId,
15063
+ client_name: clientName,
15064
+ hostinger_vps_id: vpsId,
15065
+ ip_address: ipAddress,
15066
+ domain,
15067
+ region,
15068
+ plan,
15069
+ status: healthy ? ACTIVE_INVENTORY_STATUS : PROVISIONING_INVENTORY_STATUS
15070
+ };
15071
+ }
15072
+
15073
+ // src/mcp/tools/export-orchestration.ts
15074
+ init_active_agent();
15075
+ import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
15076
+ import path29 from "path";
15077
+ import { z as z38 } from "zod";
15078
+
15079
+ // src/lib/orchestration-package.ts
15080
+ init_database();
15081
+ init_identity();
15082
+ init_platform_procedures();
15083
+ import { randomUUID as randomUUID5 } from "crypto";
15084
+ import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
15085
+ import os11 from "os";
15086
+ import path28 from "path";
15087
+ var PACKAGE_VERSION = "1.0";
15088
+ var ROSTER_FILENAME = "exe-employees.json";
15089
+ var ROSTER_BACKUP_FILENAME = "exe-employees.json.bak";
15090
+ var EXE_OS_DIRNAME = ".exe-os";
15091
+ var DEFAULT_BEHAVIOR_PRIORITY = "p1";
15092
+ var DEFAULT_PROCEDURE_PRIORITY = "p0";
15093
+ var DEFAULT_IDENTITY_UPDATED_BY = "orchestration-import";
15094
+ function ensureObject(value, label) {
15095
+ if (value == null || Array.isArray(value) || typeof value !== "object") {
15096
+ throw new Error(`${label} must be an object`);
15097
+ }
15098
+ return value;
15099
+ }
15100
+ function ensureString(value, label) {
15101
+ if (typeof value !== "string") {
15102
+ throw new Error(`${label} must be a string`);
15103
+ }
15104
+ return value;
15105
+ }
15106
+ function ensureOptionalString(value, label) {
15107
+ if (value == null) return void 0;
15108
+ return ensureString(value, label);
15109
+ }
15110
+ function ensureNullableString(value, label) {
15111
+ if (value == null) return null;
15112
+ return ensureString(value, label);
15113
+ }
15114
+ function ensurePriority(value, label, fallback) {
15115
+ if (value == null) return fallback;
15116
+ if (value === "p0" || value === "p1" || value === "p2") {
15117
+ return value;
15118
+ }
15119
+ throw new Error(`${label} must be one of: p0, p1, p2`);
15120
+ }
15121
+ function validateRosterEntry(value, index) {
15122
+ const record = ensureObject(value, `roster[${index}]`);
15123
+ return {
15124
+ name: ensureString(record.name, `roster[${index}].name`),
15125
+ role: ensureString(record.role, `roster[${index}].role`),
15126
+ systemPrompt: ensureString(record.systemPrompt, `roster[${index}].systemPrompt`),
15127
+ createdAt: typeof record.createdAt === "string" ? record.createdAt : "",
15128
+ templateName: ensureOptionalString(record.templateName, `roster[${index}].templateName`),
15129
+ templateVersion: typeof record.templateVersion === "number" ? record.templateVersion : void 0
15130
+ };
15131
+ }
15132
+ function validateBehaviorEntry(value, index) {
15133
+ const record = ensureObject(value, `behaviors[${index}]`);
15134
+ return {
15135
+ agent_id: ensureString(record.agent_id, `behaviors[${index}].agent_id`),
15136
+ project_name: ensureNullableString(record.project_name, `behaviors[${index}].project_name`),
15137
+ domain: ensureNullableString(record.domain, `behaviors[${index}].domain`),
15138
+ content: ensureString(record.content, `behaviors[${index}].content`),
15139
+ priority: ensurePriority(record.priority, `behaviors[${index}].priority`, DEFAULT_BEHAVIOR_PRIORITY)
15140
+ };
15141
+ }
15142
+ function validateProcedureEntry(value, index) {
15143
+ const record = ensureObject(value, `procedures[${index}]`);
15144
+ return {
15145
+ title: ensureString(record.title, `procedures[${index}].title`),
15146
+ content: ensureString(record.content, `procedures[${index}].content`),
15147
+ priority: ensurePriority(record.priority, `procedures[${index}].priority`, DEFAULT_PROCEDURE_PRIORITY),
15148
+ domain: ensureNullableString(record.domain, `procedures[${index}].domain`)
15149
+ };
15150
+ }
15151
+ function getRosterPath() {
15152
+ return path28.join(os11.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
15153
+ }
15154
+ function getBackupPath() {
15155
+ return path28.join(os11.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
15156
+ }
15157
+ function readRosterFile() {
15158
+ const rosterPath = getRosterPath();
15159
+ if (!existsSync22(rosterPath)) return [];
15160
+ const raw = readFileSync18(rosterPath, "utf-8");
15161
+ const parsed = JSON.parse(raw);
15162
+ if (!Array.isArray(parsed)) {
15163
+ throw new Error("Roster file must contain a JSON array");
15164
+ }
15165
+ return parsed.map((entry, index) => validateRosterEntry(entry, index));
15166
+ }
15167
+ function writeRosterFile(roster) {
15168
+ if (roster.length === 0) {
15169
+ throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
15170
+ }
15171
+ const rosterPath = getRosterPath();
15172
+ mkdirSync12(path28.dirname(rosterPath), { recursive: true });
15173
+ if (existsSync22(rosterPath)) {
15174
+ const currentRoster = readRosterFile();
15175
+ if (roster.length < currentRoster.length) {
15176
+ throw new Error(
15177
+ `Refusing to write roster with ${roster.length} entries \u2014 current roster has ${currentRoster.length}. Import would delete ${currentRoster.length - roster.length} employee(s). Use merge strategy instead, or add the missing entries to the package.`
15178
+ );
15179
+ }
15180
+ copyFileSync(rosterPath, getBackupPath());
15181
+ }
15182
+ writeFileSync14(rosterPath, `${JSON.stringify(roster, null, 2)}
15183
+ `, "utf-8");
15184
+ }
15185
+ function buildImportedRosterEntries(roster, timestamp) {
15186
+ return roster.map((entry) => ({
15187
+ name: entry.name,
15188
+ role: entry.role,
15189
+ systemPrompt: entry.systemPrompt,
15190
+ createdAt: timestamp,
15191
+ ...entry.templateName != null ? { templateName: entry.templateName } : {},
15192
+ ...entry.templateVersion != null ? { templateVersion: entry.templateVersion } : {}
15193
+ }));
15194
+ }
15195
+ async function insertBehaviors(behaviors, timestamp) {
15196
+ const client = getClient();
15197
+ for (const behavior of behaviors) {
15198
+ await client.execute({
15199
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
15200
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
15201
+ args: [
15202
+ randomUUID5(),
15203
+ behavior.agent_id,
15204
+ behavior.project_name,
15205
+ behavior.domain,
15206
+ behavior.priority,
15207
+ behavior.content,
15208
+ timestamp,
15209
+ timestamp
15210
+ ]
15211
+ });
15212
+ }
15213
+ return behaviors.length;
15214
+ }
15215
+ async function insertProcedures(procedures, timestamp, options) {
15216
+ const client = getClient();
15217
+ const skipTitles = options?.skipTitles ?? /* @__PURE__ */ new Set();
15218
+ let inserted = 0;
15219
+ for (const procedure of procedures) {
15220
+ if (skipTitles.has(procedure.title) || PLATFORM_PROCEDURE_TITLES.has(procedure.title)) {
15221
+ continue;
15222
+ }
15223
+ await client.execute({
15224
+ sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
15225
+ VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
15226
+ args: [
15227
+ randomUUID5(),
15228
+ procedure.title,
15229
+ procedure.content,
15230
+ procedure.priority,
15231
+ procedure.domain,
15232
+ timestamp,
15233
+ timestamp
15234
+ ]
15235
+ });
15236
+ skipTitles.add(procedure.title);
15237
+ inserted += 1;
15238
+ }
15239
+ return inserted;
15240
+ }
15241
+ async function replaceBehaviorsAndProcedures(behaviors, procedures, timestamp) {
15242
+ const client = getClient();
15243
+ await client.execute({
15244
+ sql: "UPDATE behaviors SET active = 0, updated_at = ? WHERE active = 1",
15245
+ args: [timestamp]
15246
+ });
15247
+ await client.execute({
15248
+ sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE active = 1",
15249
+ args: [timestamp]
15250
+ });
15251
+ return {
15252
+ behaviors: await insertBehaviors(behaviors, timestamp),
15253
+ procedures: await insertProcedures(procedures, timestamp)
15254
+ };
15255
+ }
15256
+ async function mergeRosterEntries(importedRoster, timestamp) {
15257
+ const currentRoster = readRosterFile();
15258
+ const existingNames = new Set(currentRoster.map((entry) => entry.name.toLowerCase()));
15259
+ const additions = buildImportedRosterEntries(
15260
+ importedRoster.filter((entry) => !existingNames.has(entry.name.toLowerCase())),
15261
+ timestamp
15262
+ );
15263
+ if (additions.length > 0) {
15264
+ writeRosterFile([...currentRoster, ...additions]);
15265
+ }
15266
+ return additions.length;
15267
+ }
15268
+ async function replaceRosterEntries(importedRoster, timestamp) {
15269
+ const currentRoster = readRosterFile();
15270
+ const importedByName = new Map(
15271
+ buildImportedRosterEntries(importedRoster, timestamp).map((entry) => [entry.name.toLowerCase(), entry])
15272
+ );
15273
+ const merged = currentRoster.map((existing) => {
15274
+ const imported = importedByName.get(existing.name.toLowerCase());
15275
+ if (imported) {
15276
+ importedByName.delete(existing.name.toLowerCase());
15277
+ return imported;
15278
+ }
15279
+ return existing;
15280
+ });
15281
+ for (const newEntry of importedByName.values()) {
15282
+ merged.push(newEntry);
15283
+ }
15284
+ writeRosterFile(merged);
15285
+ return importedRoster.length;
15286
+ }
15287
+ async function importIdentities(identities, updatedBy) {
15288
+ const entries = Object.entries(identities);
15289
+ for (const [agentId, content] of entries) {
15290
+ await updateIdentity(agentId, content, updatedBy);
15291
+ }
15292
+ return entries.length;
15293
+ }
15294
+ async function getActiveProcedureTitles() {
15295
+ const client = getClient();
15296
+ const result = await client.execute({
15297
+ sql: "SELECT title FROM global_procedures WHERE active = 1",
15298
+ args: []
15299
+ });
15300
+ return new Set(result.rows.map((row) => String(row.title)));
15301
+ }
15302
+ async function exportOrchestration(createdBy) {
15303
+ const client = getClient();
15304
+ const roster = readRosterFile().map((entry) => ({
15305
+ ...entry,
15306
+ createdAt: ""
15307
+ }));
15308
+ const identities = {};
15309
+ for (const identitySummary of listIdentities()) {
15310
+ const identity = getIdentity(identitySummary.agentId);
15311
+ if (identity?.raw) {
15312
+ identities[identitySummary.agentId] = identity.raw;
15313
+ }
15314
+ }
15315
+ const behaviorResult = await client.execute({
15316
+ sql: "SELECT agent_id, project_name, domain, content, priority FROM behaviors WHERE active = 1",
15317
+ args: []
15318
+ });
15319
+ const procedureResult = await client.execute({
15320
+ sql: "SELECT title, content, priority, domain FROM global_procedures WHERE active = 1",
15321
+ args: []
15322
+ });
15323
+ const behaviors = behaviorResult.rows.map((row) => ({
15324
+ agent_id: String(row.agent_id),
15325
+ project_name: row.project_name == null ? null : String(row.project_name),
15326
+ domain: row.domain == null ? null : String(row.domain),
15327
+ content: String(row.content),
15328
+ priority: ensurePriority(row.priority, "behaviors.priority", DEFAULT_BEHAVIOR_PRIORITY)
15329
+ }));
15330
+ const procedures = procedureResult.rows.map((row) => ({
15331
+ title: String(row.title),
15332
+ content: String(row.content),
15333
+ priority: ensurePriority(row.priority, "procedures.priority", DEFAULT_PROCEDURE_PRIORITY),
15334
+ domain: row.domain == null ? null : String(row.domain)
15335
+ })).filter((procedure) => !PLATFORM_PROCEDURE_TITLES.has(procedure.title));
15336
+ return {
15337
+ version: PACKAGE_VERSION,
15338
+ created_by: createdBy,
15339
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
15340
+ roster,
15341
+ identities,
15342
+ behaviors,
15343
+ procedures
15344
+ };
15345
+ }
15346
+ async function importOrchestration(pkg, strategy) {
15347
+ const validated = validatePackage(pkg);
15348
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
15349
+ const updatedBy = validated.created_by || DEFAULT_IDENTITY_UPDATED_BY;
15350
+ const rosterCount = strategy === "replace" ? await replaceRosterEntries(validated.roster, timestamp) : await mergeRosterEntries(validated.roster, timestamp);
15351
+ const identityCount = await importIdentities(validated.identities, updatedBy);
15352
+ if (strategy === "replace") {
15353
+ const replaced = await replaceBehaviorsAndProcedures(
15354
+ validated.behaviors,
15355
+ validated.procedures,
15356
+ timestamp
15357
+ );
15358
+ return {
15359
+ imported: {
15360
+ roster: rosterCount,
15361
+ identities: identityCount,
15362
+ behaviors: replaced.behaviors,
15363
+ procedures: replaced.procedures
15364
+ }
15365
+ };
15366
+ }
15367
+ const existingProcedureTitles = await getActiveProcedureTitles();
15368
+ const behaviorCount = await insertBehaviors(validated.behaviors, timestamp);
15369
+ const procedureCount = await insertProcedures(
15370
+ validated.procedures,
15371
+ timestamp,
15372
+ { skipTitles: existingProcedureTitles }
15373
+ );
15374
+ return {
15375
+ imported: {
15376
+ roster: rosterCount,
15377
+ identities: identityCount,
15378
+ behaviors: behaviorCount,
15379
+ procedures: procedureCount
15380
+ }
15381
+ };
15382
+ }
15383
+ function validatePackage(data) {
15384
+ const record = ensureObject(data, "package");
15385
+ const version = ensureString(record.version, "version");
15386
+ if (version !== PACKAGE_VERSION) {
15387
+ throw new Error(`Unsupported orchestration package version: ${version}`);
15388
+ }
15389
+ if (!Array.isArray(record.roster)) {
15390
+ throw new Error("roster is required and must be an array");
15391
+ }
15392
+ if (record.identities == null || Array.isArray(record.identities) || typeof record.identities !== "object") {
15393
+ throw new Error("identities is required and must be an object");
15394
+ }
15395
+ if (!Array.isArray(record.behaviors)) {
15396
+ throw new Error("behaviors is required and must be an array");
15397
+ }
15398
+ if (!Array.isArray(record.procedures)) {
15399
+ throw new Error("procedures is required and must be an array");
15400
+ }
15401
+ const identities = Object.entries(record.identities).reduce(
15402
+ (acc, [agentId, content]) => {
15403
+ acc[agentId] = ensureString(content, `identities.${agentId}`);
15404
+ return acc;
15405
+ },
15406
+ {}
15407
+ );
15408
+ return {
15409
+ version: PACKAGE_VERSION,
15410
+ created_by: ensureString(record.created_by, "created_by"),
15411
+ created_at: ensureString(record.created_at, "created_at"),
15412
+ roster: record.roster.map((entry, index) => validateRosterEntry(entry, index)),
15413
+ identities,
15414
+ behaviors: record.behaviors.map((entry, index) => validateBehaviorEntry(entry, index)),
15415
+ procedures: record.procedures.map((entry, index) => validateProcedureEntry(entry, index))
15416
+ };
15417
+ }
15418
+
15419
+ // src/mcp/tools/export-orchestration.ts
15420
+ init_store();
15421
+ function registerExportOrchestration(server2) {
15422
+ server2.registerTool(
15423
+ "export_orchestration",
15424
+ {
15425
+ title: "Export Orchestration",
15426
+ description: "Export roster, identities, behaviors, and customer procedures to a JSON package.",
15427
+ inputSchema: {
15428
+ output_path: z38.string().describe("File path to write the JSON package")
15429
+ }
15430
+ },
15431
+ async ({ output_path }) => {
15432
+ try {
15433
+ await initStore();
15434
+ const pkg = await exportOrchestration(getActiveAgent().agentId);
15435
+ mkdirSync13(path29.dirname(output_path), { recursive: true });
15436
+ writeFileSync15(output_path, `${JSON.stringify(pkg, null, 2)}
15437
+ `, "utf-8");
15438
+ return {
15439
+ content: [{
15440
+ type: "text",
15441
+ text: `Exported ${pkg.roster.length} roster entries, ${Object.keys(pkg.identities).length} identities, ${pkg.behaviors.length} behaviors, ${pkg.procedures.length} procedures to ${output_path}`
15442
+ }]
15443
+ };
15444
+ } catch (err) {
15445
+ return {
15446
+ content: [{
15447
+ type: "text",
15448
+ text: `Failed to export orchestration: ${err instanceof Error ? err.message : String(err)}`
15449
+ }],
15450
+ isError: true
15451
+ };
15452
+ }
15453
+ }
15454
+ );
15455
+ }
15456
+
15457
+ // src/mcp/tools/import-orchestration.ts
15458
+ import { readFileSync as readFileSync19 } from "fs";
15459
+ import { z as z39 } from "zod";
15460
+ init_store();
15461
+ function registerImportOrchestration(server2) {
15462
+ server2.registerTool(
15463
+ "import_orchestration",
15464
+ {
15465
+ title: "Import Orchestration",
15466
+ description: "Import roster, identities, behaviors, and procedures from an orchestration package.",
15467
+ inputSchema: {
15468
+ package_path: z39.string().describe("Path to the orchestration package JSON file"),
15469
+ merge_strategy: z39.enum(["replace", "merge"]).default("merge").describe("How to apply the package: replace existing data or merge additively")
15470
+ }
15471
+ },
15472
+ async ({ package_path, merge_strategy }) => {
15473
+ try {
15474
+ await initStore();
15475
+ const raw = readFileSync19(package_path, "utf-8");
15476
+ const pkg = validatePackage(JSON.parse(raw));
15477
+ const result = await importOrchestration(pkg, merge_strategy);
15478
+ return {
15479
+ content: [{
15480
+ type: "text",
15481
+ text: `Imported ${result.imported.roster} roster entries, ${result.imported.identities} identities, ${result.imported.behaviors} behaviors, ${result.imported.procedures} procedures using ${merge_strategy} strategy`
15482
+ }]
15483
+ };
15484
+ } catch (err) {
15485
+ return {
15486
+ content: [{
15487
+ type: "text",
15488
+ text: `Failed to import orchestration: ${err instanceof Error ? err.message : String(err)}`
15489
+ }],
15490
+ isError: true
15491
+ };
15492
+ }
15493
+ }
15494
+ );
15495
+ }
14819
15496
 
14820
15497
  // src/mcp/tools/query-conversations.ts
14821
15498
  init_database();
14822
- import { z as z38 } from "zod";
15499
+ import { z as z40 } from "zod";
14823
15500
  function registerQueryConversations(server2) {
14824
15501
  server2.registerTool(
14825
15502
  "query_conversations",
@@ -14827,14 +15504,14 @@ function registerQueryConversations(server2) {
14827
15504
  title: "Query Conversations",
14828
15505
  description: "Search across all adapter messages by content, sender, date range, or platform. Returns messages with source attribution (platform, sender, timestamp).",
14829
15506
  inputSchema: {
14830
- query: z38.string().optional().describe("Full-text search query across message content and responses"),
14831
- sender: z38.string().optional().describe("Filter by sender ID or name (partial match)"),
14832
- platform: z38.string().optional().describe("Filter by platform: whatsapp, signal, telegram, discord, imessage, slack, email, webchat"),
14833
- after: z38.string().optional().describe("Filter messages after this ISO 8601 date (e.g. 2026-04-01)"),
14834
- before: z38.string().optional().describe("Filter messages before this ISO 8601 date (e.g. 2026-04-15)"),
14835
- channel_id: z38.string().optional().describe("Filter by channel/chat ID"),
14836
- thread_id: z38.string().optional().describe("Filter by conversation thread ID"),
14837
- limit: z38.number().int().min(1).max(100).optional().default(25).describe("Max results to return (default 25, max 100)")
15507
+ query: z40.string().optional().describe("Full-text search query across message content and responses"),
15508
+ sender: z40.string().optional().describe("Filter by sender ID or name (partial match)"),
15509
+ platform: z40.string().optional().describe("Filter by platform: whatsapp, signal, telegram, discord, imessage, slack, email, webchat"),
15510
+ after: z40.string().optional().describe("Filter messages after this ISO 8601 date (e.g. 2026-04-01)"),
15511
+ before: z40.string().optional().describe("Filter messages before this ISO 8601 date (e.g. 2026-04-15)"),
15512
+ channel_id: z40.string().optional().describe("Filter by channel/chat ID"),
15513
+ thread_id: z40.string().optional().describe("Filter by conversation thread ID"),
15514
+ limit: z40.number().int().min(1).max(100).optional().default(25).describe("Max results to return (default 25, max 100)")
14838
15515
  }
14839
15516
  },
14840
15517
  async (params) => {
@@ -14941,19 +15618,19 @@ function registerQueryConversations(server2) {
14941
15618
  }
14942
15619
 
14943
15620
  // src/mcp/tools/load-skill.ts
14944
- import { z as z39 } from "zod";
14945
- import { readFileSync as readFileSync18, readdirSync as readdirSync8, statSync as statSync3 } from "fs";
14946
- import path28 from "path";
15621
+ import { z as z41 } from "zod";
15622
+ import { readFileSync as readFileSync20, readdirSync as readdirSync8, statSync as statSync3 } from "fs";
15623
+ import path30 from "path";
14947
15624
  import { homedir as homedir2 } from "os";
14948
- var SKILLS_DIR = path28.join(homedir2(), ".claude", "skills");
15625
+ var SKILLS_DIR = path30.join(homedir2(), ".claude", "skills");
14949
15626
  function listAvailableSkills() {
14950
15627
  try {
14951
15628
  const entries = readdirSync8(SKILLS_DIR);
14952
15629
  return entries.filter((entry) => {
14953
15630
  try {
14954
- const entryPath = path28.join(SKILLS_DIR, entry);
15631
+ const entryPath = path30.join(SKILLS_DIR, entry);
14955
15632
  if (!statSync3(entryPath).isDirectory()) return false;
14956
- const skillFile = path28.join(entryPath, "SKILL.md");
15633
+ const skillFile = path30.join(entryPath, "SKILL.md");
14957
15634
  statSync3(skillFile);
14958
15635
  return true;
14959
15636
  } catch {
@@ -14971,7 +15648,7 @@ function registerLoadSkill(server2) {
14971
15648
  title: "Load Skill",
14972
15649
  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.",
14973
15650
  inputSchema: {
14974
- skill_name: z39.string().describe(
15651
+ skill_name: z41.string().describe(
14975
15652
  "Skill to load (e.g. 'seo', 'code-reviewer', 'frontend-design'). Pass 'list' to see all available skills."
14976
15653
  )
14977
15654
  }
@@ -14996,10 +15673,10 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
14996
15673
  }]
14997
15674
  };
14998
15675
  }
14999
- const sanitized = path28.basename(skill_name);
15000
- const skillFile = path28.join(SKILLS_DIR, sanitized, "SKILL.md");
15676
+ const sanitized = path30.basename(skill_name);
15677
+ const skillFile = path30.join(SKILLS_DIR, sanitized, "SKILL.md");
15001
15678
  try {
15002
- const content = readFileSync18(skillFile, "utf-8");
15679
+ const content = readFileSync20(skillFile, "utf-8");
15003
15680
  return {
15004
15681
  content: [{
15005
15682
  type: "text",
@@ -15028,7 +15705,7 @@ ${available.map((s) => `- ${s}`).join("\n")}` : "\n\nNo skills found in ~/.claud
15028
15705
  // src/mcp/tools/consolidate-memories.ts
15029
15706
  init_database();
15030
15707
  init_active_agent();
15031
- import { z as z40 } from "zod";
15708
+ import { z as z42 } from "zod";
15032
15709
  function registerConsolidateMemories(server2) {
15033
15710
  server2.registerTool(
15034
15711
  "consolidate_memories",
@@ -15036,8 +15713,8 @@ function registerConsolidateMemories(server2) {
15036
15713
  title: "Consolidate Memories",
15037
15714
  description: "Trigger content-aware memory consolidation. Groups recent memories by project, uses LLM to extract key facts, decisions, and patterns, then stores a high-importance summary and archives the originals. Runs automatically during idle time, but can be triggered manually when you want to compress your working memory.",
15038
15715
  inputSchema: {
15039
- max_clusters: z40.coerce.number().optional().default(5).describe("Maximum number of clusters to consolidate (default 5)"),
15040
- model: z40.string().optional().describe("LLM model for summarization (defaults to config value)")
15716
+ max_clusters: z42.coerce.number().optional().default(5).describe("Maximum number of clusters to consolidate (default 5)"),
15717
+ model: z42.string().optional().describe("LLM model for summarization (defaults to config value)")
15041
15718
  }
15042
15719
  },
15043
15720
  async ({ max_clusters, model }) => {
@@ -15093,7 +15770,7 @@ Consolidated summaries stored as tier-1 (importance=9) memories.`
15093
15770
  init_global_procedures();
15094
15771
  init_active_agent();
15095
15772
  init_employees();
15096
- import { z as z41 } from "zod";
15773
+ import { z as z43 } from "zod";
15097
15774
  function registerStoreGlobalProcedure(server2) {
15098
15775
  server2.registerTool(
15099
15776
  "store_global_procedure",
@@ -15101,10 +15778,10 @@ function registerStoreGlobalProcedure(server2) {
15101
15778
  title: "Store Global Procedure",
15102
15779
  description: "Create an organization-wide procedure (Layer 0) that supersedes identity, expertise, and experience. Use for hard rules that every employee must follow. RESTRICTED: only coordinator or founder sessions.",
15103
15780
  inputSchema: {
15104
- title: z41.string().describe("Short title for the procedure"),
15105
- content: z41.string().max(500).describe("The procedure content \u2014 clear, actionable instruction"),
15106
- priority: z41.enum(["p0", "p1", "p2"]).optional().describe("Priority tier. p0 = always (default). p1 = standard. p2 = nice-to-have."),
15107
- domain: z41.string().optional().describe("Category: workflow, code-style, communication, architecture, testing, security")
15781
+ title: z43.string().describe("Short title for the procedure"),
15782
+ content: z43.string().max(500).describe("The procedure content \u2014 clear, actionable instruction"),
15783
+ priority: z43.enum(["p0", "p1", "p2"]).optional().describe("Priority tier. p0 = always (default). p1 = standard. p2 = nice-to-have."),
15784
+ domain: z43.string().optional().describe("Category: workflow, code-style, communication, architecture, testing, security")
15108
15785
  }
15109
15786
  },
15110
15787
  async ({ title, content, priority, domain }) => {
@@ -15182,7 +15859,7 @@ init_global_procedures();
15182
15859
  init_active_agent();
15183
15860
  init_database();
15184
15861
  init_employees();
15185
- import { z as z42 } from "zod";
15862
+ import { z as z44 } from "zod";
15186
15863
  function registerDeactivateGlobalProcedure(server2) {
15187
15864
  server2.registerTool(
15188
15865
  "deactivate_global_procedure",
@@ -15190,7 +15867,7 @@ function registerDeactivateGlobalProcedure(server2) {
15190
15867
  title: "Deactivate Global Procedure",
15191
15868
  description: "Soft-delete a global procedure by setting active = 0. RESTRICTED: only coordinator or founder sessions. Use list_global_procedures to find the procedure ID first.",
15192
15869
  inputSchema: {
15193
- procedure_id: z42.string().describe("UUID of the global procedure to deactivate")
15870
+ procedure_id: z44.string().describe("UUID of the global procedure to deactivate")
15194
15871
  }
15195
15872
  },
15196
15873
  async ({ procedure_id }) => {
@@ -15245,7 +15922,7 @@ Content: ${row.content}`
15245
15922
  }
15246
15923
 
15247
15924
  // src/mcp/tools/search-everything.ts
15248
- import { z as z43 } from "zod";
15925
+ import { z as z45 } from "zod";
15249
15926
 
15250
15927
  // src/lib/unified-search.ts
15251
15928
  var DEFAULT_LIMIT = 10;
@@ -15412,11 +16089,11 @@ function registerSearchEverything(server2) {
15412
16089
  title: "Search Everything",
15413
16090
  description: "Search across all data stores \u2014 memories, conversations, and wiki \u2014 in one query. Returns merged results sorted by relevance with entity links.",
15414
16091
  inputSchema: {
15415
- query: z43.string().describe("What to search for across all data stores"),
15416
- sources: z43.array(z43.enum(["memory", "conversations", "wiki"])).optional().describe(
16092
+ query: z45.string().describe("What to search for across all data stores"),
16093
+ sources: z45.array(z45.enum(["memory", "conversations", "wiki"])).optional().describe(
15417
16094
  "Which sources to search (default: all). Options: memory, conversations, wiki"
15418
16095
  ),
15419
- limit: z43.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return (default 10, max 50)")
16096
+ limit: z45.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results to return (default 10, max 50)")
15420
16097
  }
15421
16098
  },
15422
16099
  async (params) => {
@@ -15477,7 +16154,7 @@ init_store();
15477
16154
  init_active_agent();
15478
16155
  init_database();
15479
16156
  init_plan_limits();
15480
- import { z as z44 } from "zod";
16157
+ import { z as z46 } from "zod";
15481
16158
  import crypto14 from "crypto";
15482
16159
  function registerStoreDecision(server2) {
15483
16160
  server2.registerTool(
@@ -15486,13 +16163,13 @@ function registerStoreDecision(server2) {
15486
16163
  title: "Store Decision",
15487
16164
  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.",
15488
16165
  inputSchema: {
15489
- domain: z44.string().describe(
16166
+ domain: z46.string().describe(
15490
16167
  "Domain key, e.g. 'auth-strategy', 'db-migration-approach', 'api-versioning'"
15491
16168
  ),
15492
- decision: z44.string().describe("The decision text \u2014 what was decided"),
15493
- rationale: z44.string().optional().describe("Why this decision was made \u2014 constraints, trade-offs, context"),
15494
- supersedes: z44.string().optional().describe("UUID of the decision this supersedes (previous decision for this domain)"),
15495
- project_name: z44.string().optional().describe("Project name")
16169
+ decision: z46.string().describe("The decision text \u2014 what was decided"),
16170
+ rationale: z46.string().optional().describe("Why this decision was made \u2014 constraints, trade-offs, context"),
16171
+ supersedes: z46.string().optional().describe("UUID of the decision this supersedes (previous decision for this domain)"),
16172
+ project_name: z46.string().optional().describe("Project name")
15496
16173
  }
15497
16174
  },
15498
16175
  async ({ domain, decision, rationale, supersedes, project_name }) => {
@@ -15560,7 +16237,7 @@ Supersedes: ${supersedes}` : ""}`
15560
16237
 
15561
16238
  // src/mcp/tools/get-decision.ts
15562
16239
  init_database();
15563
- import { z as z45 } from "zod";
16240
+ import { z as z47 } from "zod";
15564
16241
  function registerGetDecision(server2) {
15565
16242
  server2.registerTool(
15566
16243
  "get_decision",
@@ -15568,7 +16245,7 @@ function registerGetDecision(server2) {
15568
16245
  title: "Get Decision",
15569
16246
  description: "Retrieve the latest authoritative decision for a domain. Returns the current active decision and the supersession history.",
15570
16247
  inputSchema: {
15571
- domain: z45.string().describe(
16248
+ domain: z47.string().describe(
15572
16249
  "Domain key to look up, e.g. 'auth-strategy', 'db-migration-approach'"
15573
16250
  )
15574
16251
  }
@@ -15631,15 +16308,15 @@ function registerGetDecision(server2) {
15631
16308
  }
15632
16309
 
15633
16310
  // src/mcp/tools/get-agent-spend.ts
15634
- import { z as z46 } from "zod";
16311
+ import { z as z48 } from "zod";
15635
16312
 
15636
16313
  // src/lib/token-spend.ts
15637
16314
  init_database();
15638
16315
  import { readdir } from "fs/promises";
15639
16316
  import { createReadStream } from "fs";
15640
16317
  import { createInterface } from "readline";
15641
- import path29 from "path";
15642
- import os11 from "os";
16318
+ import path31 from "path";
16319
+ import os12 from "os";
15643
16320
  var MODEL_PRICING = {
15644
16321
  // Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
15645
16322
  "claude-opus-4-7": { input: 5 / 1e6, output: 25 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 6.25 / 1e6 },
@@ -15687,18 +16364,18 @@ async function getAgentSpend(period = "7d") {
15687
16364
  for (const row of result.rows) {
15688
16365
  sessionAgent.set(row.session_uuid, row.agent_id);
15689
16366
  }
15690
- const claudeDir = path29.join(os11.homedir(), ".claude", "projects");
16367
+ const claudeDir = path31.join(os12.homedir(), ".claude", "projects");
15691
16368
  let projectDirs = [];
15692
16369
  try {
15693
16370
  const entries = await readdir(claudeDir);
15694
- projectDirs = entries.map((e) => path29.join(claudeDir, e));
16371
+ projectDirs = entries.map((e) => path31.join(claudeDir, e));
15695
16372
  } catch {
15696
16373
  return [];
15697
16374
  }
15698
16375
  const agentTotals = /* @__PURE__ */ new Map();
15699
16376
  for (const [sessionUuid, agentId] of sessionAgent) {
15700
16377
  for (const dir of projectDirs) {
15701
- const jsonlPath = path29.join(dir, `${sessionUuid}.jsonl`);
16378
+ const jsonlPath = path31.join(dir, `${sessionUuid}.jsonl`);
15702
16379
  try {
15703
16380
  const usage = await extractSessionUsage(jsonlPath);
15704
16381
  if (usage.input === 0 && usage.output === 0) continue;
@@ -15793,8 +16470,8 @@ function registerGetAgentSpend(server2) {
15793
16470
  title: "Get Agent Spend",
15794
16471
  description: "Get per-agent token spend attribution. Shows input, output, and cache tokens consumed by each agent over a time period, with estimated cost.",
15795
16472
  inputSchema: {
15796
- period: z46.enum(["24h", "7d", "30d"]).default("7d").describe("Time period to query"),
15797
- agent_id: z46.string().optional().describe("Filter to a specific agent (e.g. 'tom', 'yoshi')")
16473
+ period: z48.enum(["24h", "7d", "30d"]).default("7d").describe("Time period to query"),
16474
+ agent_id: z48.string().optional().describe("Filter to a specific agent (e.g. 'tom', 'yoshi')")
15798
16475
  }
15799
16476
  },
15800
16477
  async ({ period, agent_id }) => {
@@ -15860,7 +16537,7 @@ function registerGetAgentSpend(server2) {
15860
16537
  // src/mcp/tools/get-graph-stats.ts
15861
16538
  init_database();
15862
16539
  init_graph_query();
15863
- import { z as z47 } from "zod";
16540
+ import { z as z49 } from "zod";
15864
16541
  function registerGetGraphStats(server2) {
15865
16542
  server2.registerTool(
15866
16543
  "get_graph_stats",
@@ -15868,7 +16545,7 @@ function registerGetGraphStats(server2) {
15868
16545
  title: "Get Graph Stats",
15869
16546
  description: "Get knowledge graph summary statistics: entity count, relationship count, and entity type breakdown.",
15870
16547
  inputSchema: {
15871
- _dummy: z47.string().optional().describe("Unused \u2014 no input required")
16548
+ _dummy: z49.string().optional().describe("Unused \u2014 no input required")
15872
16549
  }
15873
16550
  },
15874
16551
  async () => {
@@ -15910,7 +16587,7 @@ function registerGetGraphStats(server2) {
15910
16587
  // src/mcp/tools/get-entity-neighbors.ts
15911
16588
  init_database();
15912
16589
  init_graph_query();
15913
- import { z as z48 } from "zod";
16590
+ import { z as z50 } from "zod";
15914
16591
  function registerGetEntityNeighbors(server2) {
15915
16592
  server2.registerTool(
15916
16593
  "get_entity_neighbors",
@@ -15918,7 +16595,7 @@ function registerGetEntityNeighbors(server2) {
15918
16595
  title: "Get Entity Neighbors",
15919
16596
  description: "Get connected entities for a given entity name. Returns neighbors with relationship types, directions, and weights.",
15920
16597
  inputSchema: {
15921
- entity_name: z48.string().describe("Name of the entity to find neighbors for")
16598
+ entity_name: z50.string().describe("Name of the entity to find neighbors for")
15922
16599
  }
15923
16600
  },
15924
16601
  async ({ entity_name }) => {
@@ -15987,7 +16664,7 @@ function registerGetEntityNeighbors(server2) {
15987
16664
  // src/mcp/tools/get-hot-entities.ts
15988
16665
  init_database();
15989
16666
  init_graph_query();
15990
- import { z as z49 } from "zod";
16667
+ import { z as z51 } from "zod";
15991
16668
  var PERIOD_MS = {
15992
16669
  "24h": 24 * 60 * 60 * 1e3,
15993
16670
  "7d": 7 * 24 * 60 * 60 * 1e3,
@@ -16000,8 +16677,8 @@ function registerGetHotEntities(server2) {
16000
16677
  title: "Get Hot Entities",
16001
16678
  description: "Get trending entities \u2014 those with the most new relationships in a time period. Surfaces what's most active in the knowledge graph.",
16002
16679
  inputSchema: {
16003
- period: z49.enum(["24h", "7d", "30d"]).default("7d").describe("Time period to look back"),
16004
- limit: z49.number().int().min(1).max(50).default(10).describe("Max entities to return")
16680
+ period: z51.enum(["24h", "7d", "30d"]).default("7d").describe("Time period to look back"),
16681
+ limit: z51.number().int().min(1).max(50).default(10).describe("Max entities to return")
16005
16682
  }
16006
16683
  },
16007
16684
  async ({ period, limit }) => {
@@ -16049,7 +16726,7 @@ function registerGetHotEntities(server2) {
16049
16726
 
16050
16727
  // src/mcp/tools/export-graph.ts
16051
16728
  init_database();
16052
- import { z as z50 } from "zod";
16729
+ import { z as z52 } from "zod";
16053
16730
 
16054
16731
  // src/lib/graph-export.ts
16055
16732
  async function loadGraphData(client) {
@@ -16262,7 +16939,7 @@ function registerExportGraph(server2) {
16262
16939
  title: "Export Graph",
16263
16940
  description: "Export the knowledge graph as a markdown report (inline) or an interactive HTML visualization (writes file, returns path).",
16264
16941
  inputSchema: {
16265
- format: z50.enum(["html", "markdown"]).default("markdown").describe("Export format: markdown (inline) or html (file)")
16942
+ format: z52.enum(["html", "markdown"]).default("markdown").describe("Export format: markdown (inline) or html (file)")
16266
16943
  }
16267
16944
  },
16268
16945
  async ({ format }) => {
@@ -16276,12 +16953,12 @@ function registerExportGraph(server2) {
16276
16953
  }
16277
16954
  const html = await exportGraphHTML(client);
16278
16955
  const fs = await import("fs");
16279
- const path38 = await import("path");
16280
- const os13 = await import("os");
16281
- const outDir = path38.join(os13.homedir(), ".exe-os", "exports");
16956
+ const path40 = await import("path");
16957
+ const os14 = await import("os");
16958
+ const outDir = path40.join(os14.homedir(), ".exe-os", "exports");
16282
16959
  fs.mkdirSync(outDir, { recursive: true });
16283
16960
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
16284
- const filePath = path38.join(outDir, `graph-${timestamp}.html`);
16961
+ const filePath = path40.join(outDir, `graph-${timestamp}.html`);
16285
16962
  fs.writeFileSync(filePath, html, "utf-8");
16286
16963
  return {
16287
16964
  content: [
@@ -16312,7 +16989,7 @@ Open in a browser to explore interactively.`
16312
16989
 
16313
16990
  // src/mcp/tools/find-similar-trajectories.ts
16314
16991
  init_skill_learning();
16315
- import { z as z51 } from "zod";
16992
+ import { z as z53 } from "zod";
16316
16993
  function registerFindSimilarTrajectories(server2) {
16317
16994
  server2.registerTool(
16318
16995
  "find_similar_trajectories",
@@ -16320,7 +16997,7 @@ function registerFindSimilarTrajectories(server2) {
16320
16997
  title: "Find Similar Trajectories",
16321
16998
  description: 'Find past task trajectories that match a described tool sequence. Input a comma-separated tool sequence (e.g. "Read, Edit, Bash, Read") to find similar past workflows.',
16322
16999
  inputSchema: {
16323
- description: z51.string().describe(
17000
+ description: z53.string().describe(
16324
17001
  'Tool sequence to match \u2014 comma-separated tool names (e.g. "Read, Grep, Edit, Bash:npm, Bash:git")'
16325
17002
  )
16326
17003
  }
@@ -16391,7 +17068,7 @@ ${withSkill.length} trajectory(s) already extracted into skill(s).`
16391
17068
 
16392
17069
  // src/mcp/tools/get-session-kills.ts
16393
17070
  init_session_kill_telemetry();
16394
- import { z as z52 } from "zod";
17071
+ import { z as z54 } from "zod";
16395
17072
  var PERIOD_MS2 = {
16396
17073
  "24h": 24 * 60 * 60 * 1e3,
16397
17074
  "7d": 7 * 24 * 60 * 60 * 1e3,
@@ -16407,7 +17084,7 @@ function registerGetSessionKills(server2) {
16407
17084
  title: "Get Session Kills",
16408
17085
  description: "Get session kill telemetry: how many idle/TTL sessions were killed, tokens saved, and estimated cost savings over a time period.",
16409
17086
  inputSchema: {
16410
- period: z52.enum(["24h", "7d", "30d"]).default("7d").describe("Time period to query")
17087
+ period: z54.enum(["24h", "7d", "30d"]).default("7d").describe("Time period to query")
16411
17088
  }
16412
17089
  },
16413
17090
  async ({ period }) => {
@@ -16447,7 +17124,7 @@ function registerGetSessionKills(server2) {
16447
17124
 
16448
17125
  // src/mcp/tools/list-agent-sessions.ts
16449
17126
  init_session_registry();
16450
- import { z as z53 } from "zod";
17127
+ import { z as z55 } from "zod";
16451
17128
  function registerListAgentSessions(server2) {
16452
17129
  server2.registerTool(
16453
17130
  "list_agent_sessions",
@@ -16455,7 +17132,7 @@ function registerListAgentSessions(server2) {
16455
17132
  title: "List Agent Sessions",
16456
17133
  description: "List all registered agent sessions with their agent ID, project, PID, and registration time.",
16457
17134
  inputSchema: {
16458
- _placeholder: z53.string().optional().describe("No input required")
17135
+ _placeholder: z55.string().optional().describe("No input required")
16459
17136
  }
16460
17137
  },
16461
17138
  async () => {
@@ -16501,11 +17178,11 @@ function registerListAgentSessions(server2) {
16501
17178
  }
16502
17179
 
16503
17180
  // src/mcp/tools/get-daemon-health.ts
16504
- import { z as z54 } from "zod";
16505
- import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
16506
- import path30 from "path";
17181
+ import { z as z56 } from "zod";
17182
+ import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
17183
+ import path32 from "path";
16507
17184
  import { homedir as homedir3 } from "os";
16508
- var PID_PATH2 = path30.join(homedir3(), ".exe-os", "exed.pid");
17185
+ var PID_PATH2 = path32.join(homedir3(), ".exe-os", "exed.pid");
16509
17186
  function formatUptime(seconds) {
16510
17187
  const h = Math.floor(seconds / 3600);
16511
17188
  const m = Math.floor(seconds % 3600 / 60);
@@ -16516,8 +17193,8 @@ function formatUptime(seconds) {
16516
17193
  }
16517
17194
  function isDaemonAlive() {
16518
17195
  try {
16519
- if (!existsSync22(PID_PATH2)) return { alive: false, pid: null };
16520
- const pid = parseInt(readFileSync19(PID_PATH2, "utf8").trim(), 10);
17196
+ if (!existsSync23(PID_PATH2)) return { alive: false, pid: null };
17197
+ const pid = parseInt(readFileSync21(PID_PATH2, "utf8").trim(), 10);
16521
17198
  if (isNaN(pid) || pid <= 0) return { alive: false, pid: null };
16522
17199
  process.kill(pid, 0);
16523
17200
  return { alive: true, pid };
@@ -16532,7 +17209,7 @@ function registerGetDaemonHealth(server2) {
16532
17209
  title: "Get Daemon Health",
16533
17210
  description: "Check the embedding daemon (exed) health: whether it's running, its PID, uptime, and requests served.",
16534
17211
  inputSchema: {
16535
- _placeholder: z54.string().optional().describe("No input required")
17212
+ _placeholder: z56.string().optional().describe("No input required")
16536
17213
  }
16537
17214
  },
16538
17215
  async () => {
@@ -16584,14 +17261,14 @@ function registerGetDaemonHealth(server2) {
16584
17261
  // src/mcp/tools/get-auto-wake-status.ts
16585
17262
  init_database();
16586
17263
  init_session_registry();
16587
- import { z as z55 } from "zod";
17264
+ import { z as z57 } from "zod";
16588
17265
 
16589
17266
  // src/lib/daemon-orchestration.ts
16590
17267
  init_tmux_routing();
16591
17268
  init_task_scope();
16592
17269
  init_employees();
16593
17270
  import { execSync as execSync10 } from "child_process";
16594
- import { existsSync as existsSync23, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "fs";
17271
+ import { existsSync as existsSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync16 } from "fs";
16595
17272
  import { homedir as homedir4 } from "os";
16596
17273
  import { join as join2 } from "path";
16597
17274
  var IDLE_NUDGE_DEDUP_MS = Number(process.env.EXE_NUDGE_INTERVAL_MS) || 6e4;
@@ -16612,7 +17289,7 @@ function registerGetAutoWakeStatus(server2) {
16612
17289
  title: "Get Auto-Wake Status",
16613
17290
  description: "Check auto-wake status: orphaned tasks (assigned agent has no running session), tasks blocked by auto-wake retry limit, and session activity.",
16614
17291
  inputSchema: {
16615
- _placeholder: z55.string().optional().describe("No input required")
17292
+ _placeholder: z57.string().optional().describe("No input required")
16616
17293
  }
16617
17294
  },
16618
17295
  async () => {
@@ -16693,16 +17370,16 @@ function registerGetAutoWakeStatus(server2) {
16693
17370
  // src/mcp/tools/get-worker-gate.ts
16694
17371
  init_worker_gate();
16695
17372
  init_config();
16696
- import { z as z56 } from "zod";
16697
- import { readdirSync as readdirSync10, existsSync as existsSync25 } from "fs";
16698
- import path32 from "path";
16699
- var WORKER_PID_DIR2 = path32.join(EXE_AI_DIR, "worker-pids");
17373
+ import { z as z58 } from "zod";
17374
+ import { readdirSync as readdirSync10, existsSync as existsSync26 } from "fs";
17375
+ import path34 from "path";
17376
+ var WORKER_PID_DIR2 = path34.join(EXE_AI_DIR, "worker-pids");
16700
17377
  function countAliveWorkers() {
16701
17378
  let alive = 0;
16702
17379
  let stale = 0;
16703
17380
  let reservations = 0;
16704
17381
  try {
16705
- if (!existsSync25(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
17382
+ if (!existsSync26(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
16706
17383
  const files = readdirSync10(WORKER_PID_DIR2);
16707
17384
  for (const f of files) {
16708
17385
  if (!f.endsWith(".pid")) continue;
@@ -16731,7 +17408,7 @@ function registerGetWorkerGate(server2) {
16731
17408
  title: "Get Worker Gate",
16732
17409
  description: "Check worker concurrency gate status: how many worker slots are in use, the maximum allowed, and whether new workers can spawn.",
16733
17410
  inputSchema: {
16734
- _placeholder: z56.string().optional().describe("No input required")
17411
+ _placeholder: z58.string().optional().describe("No input required")
16735
17412
  }
16736
17413
  },
16737
17414
  async () => {
@@ -16767,12 +17444,12 @@ function registerGetWorkerGate(server2) {
16767
17444
 
16768
17445
  // src/mcp/tools/run-memory-audit.ts
16769
17446
  init_database();
16770
- import { z as z57 } from "zod";
17447
+ import { z as z59 } from "zod";
16771
17448
 
16772
17449
  // src/bin/exe-doctor.ts
16773
17450
  init_store();
16774
17451
  init_database();
16775
- import os12 from "os";
17452
+ import os13 from "os";
16776
17453
 
16777
17454
  // src/lib/is-main.ts
16778
17455
  import { realpathSync } from "fs";
@@ -16790,10 +17467,10 @@ function isMainModule(importMetaUrl) {
16790
17467
  }
16791
17468
 
16792
17469
  // src/bin/exe-doctor.ts
16793
- import { existsSync as existsSync26 } from "fs";
17470
+ import { existsSync as existsSync27, readFileSync as readFileSync23 } from "fs";
16794
17471
  import { spawn as spawn2 } from "child_process";
16795
- import path33 from "path";
16796
- import { randomUUID as randomUUID6 } from "crypto";
17472
+ import path35 from "path";
17473
+ import { randomUUID as randomUUID7 } from "crypto";
16797
17474
 
16798
17475
  // src/lib/conflict-detector.ts
16799
17476
  var DEFAULT_MAX_PAIRS = 100;
@@ -17195,13 +17872,53 @@ async function auditOrphanedProjects(client) {
17195
17872
  for (const row of result.rows) {
17196
17873
  const name = row.project_name;
17197
17874
  const count = Number(row.cnt);
17198
- const exists = existsSync26(path33.join(home, name)) || existsSync26(path33.join(home, "..", name)) || existsSync26(path33.join(process.cwd(), "..", name));
17875
+ const exists = existsSync27(path35.join(home, name)) || existsSync27(path35.join(home, "..", name)) || existsSync27(path35.join(process.cwd(), "..", name));
17199
17876
  if (!exists) {
17200
17877
  orphans.push({ project_name: name, count });
17201
17878
  }
17202
17879
  }
17203
17880
  return orphans;
17204
17881
  }
17882
+ function auditHookHealth() {
17883
+ const logPath = path35.join(
17884
+ process.env.HOME ?? process.env.USERPROFILE ?? "",
17885
+ ".exe-os",
17886
+ "logs",
17887
+ "hooks.log"
17888
+ );
17889
+ if (!existsSync27(logPath)) {
17890
+ return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
17891
+ }
17892
+ let content;
17893
+ try {
17894
+ content = readFileSync23(logPath, "utf-8");
17895
+ } catch {
17896
+ return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
17897
+ }
17898
+ const lines = content.split("\n").filter(Boolean);
17899
+ const totalLines = lines.length;
17900
+ const recent = lines.slice(-200);
17901
+ const oneHourAgo = new Date(Date.now() - 36e5).toISOString();
17902
+ let errorsLastHour = 0;
17903
+ const patternCounts = /* @__PURE__ */ new Map();
17904
+ for (const line of recent) {
17905
+ const isError = /error|Error|ERR|FAIL|throw|exception|TypeError|ReferenceError|SyntaxError/i.test(line);
17906
+ if (!isError) continue;
17907
+ const tsMatch = line.match(/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})/);
17908
+ if (tsMatch && tsMatch[1] >= oneHourAgo) {
17909
+ errorsLastHour++;
17910
+ } else if (!tsMatch) {
17911
+ errorsLastHour++;
17912
+ }
17913
+ const patternMatch = line.match(/((?:TypeError|ReferenceError|SyntaxError|Error):[^\n]{0,80})/);
17914
+ if (patternMatch) {
17915
+ const p = patternMatch[1];
17916
+ patternCounts.set(p, (patternCounts.get(p) ?? 0) + 1);
17917
+ }
17918
+ }
17919
+ const topPatterns = [...patternCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => ({ pattern, count }));
17920
+ return { logExists: true, totalLines, errorsLastHour, topPatterns };
17921
+ }
17205
17922
  async function runAudit(client, flags) {
17206
17923
  const [stats, nullVectors, duplicates, bloated, fts, orphanedProjects, conflicts] = await Promise.all([
17207
17924
  auditStats(client, flags),
@@ -17213,7 +17930,8 @@ async function runAudit(client, flags) {
17213
17930
  detectConflicts(client, flags.project, flags.agent)
17214
17931
  ]);
17215
17932
  const duplicateCount = duplicates.reduce((sum, d) => sum + d.delete_ids.length, 0);
17216
- return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts };
17933
+ const hookHealth = auditHookHealth();
17934
+ return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth };
17217
17935
  }
17218
17936
  function indicator(value, warn) {
17219
17937
  if (value === 0) return "\u{1F7E2}";
@@ -17248,7 +17966,7 @@ function formatReport(report, flags) {
17248
17966
  }
17249
17967
  lines.push("");
17250
17968
  }
17251
- const totalMemGB = os12.totalmem() / (1024 * 1024 * 1024);
17969
+ const totalMemGB = os13.totalmem() / (1024 * 1024 * 1024);
17252
17970
  const isLowMemSystem = totalMemGB <= 8;
17253
17971
  if (isLowMemSystem && report.nullVectors > 0) {
17254
17972
  lines.push(`\u{1F7E2} Null vectors: ${fmtNum(report.nullVectors)} / ${fmtNum(s.total)} (expected \u2014 8GB system, keyword search mode)`);
@@ -17281,6 +17999,17 @@ function formatReport(report, flags) {
17281
17999
  } else {
17282
18000
  lines.push("\u{1F7E2} Conflicts: none detected");
17283
18001
  }
18002
+ const hh = report.hookHealth;
18003
+ if (!hh.logExists) {
18004
+ lines.push("\u{1F7E0} Hook logs: no log file (run installer to enable stderr capture)");
18005
+ } else if (hh.errorsLastHour === 0) {
18006
+ lines.push(`\u{1F7E2} Hook logs: ${fmtNum(hh.totalLines)} lines, 0 errors in last hour`);
18007
+ } else {
18008
+ lines.push(`\u{1F534} Hook logs: ${fmtNum(hh.errorsLastHour)} errors in last hour (${fmtNum(hh.totalLines)} total lines)`);
18009
+ for (const p of hh.topPatterns) {
18010
+ lines.push(` ${p.count}x: ${p.pattern}`);
18011
+ }
18012
+ }
17284
18013
  lines.push("");
17285
18014
  if (flags.verbose) {
17286
18015
  if (report.duplicates.length > 0) {
@@ -17359,7 +18088,7 @@ async function fixNullVectors() {
17359
18088
  }
17360
18089
  }
17361
18090
  const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
17362
- const backfillPath = path33.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
18091
+ const backfillPath = path35.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
17363
18092
  return new Promise((resolve, reject) => {
17364
18093
  const child = spawn2("node", [backfillPath], { stdio: "inherit" });
17365
18094
  if (child.pid) registerWorkerPid2(child.pid);
@@ -17415,7 +18144,7 @@ async function fixBloated(client, bloated, dryRun) {
17415
18144
  tool_name, project_name, has_error, raw_text, vector, version, consolidated)
17416
18145
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, 0, 0)`,
17417
18146
  args: [
17418
- randomUUID6(),
18147
+ randomUUID7(),
17419
18148
  row.agent_id,
17420
18149
  row.agent_role,
17421
18150
  row.session_id,
@@ -17519,8 +18248,8 @@ function registerRunMemoryAudit(server2) {
17519
18248
  title: "Run Memory Audit",
17520
18249
  description: "Run a memory health audit: total counts, per-agent breakdown, null vectors, duplicates, FTS sync status, bloated records, and conflict detection (contradictory/superseded memories).",
17521
18250
  inputSchema: {
17522
- agent_id: z57.string().optional().describe("Filter audit to a specific agent"),
17523
- project_name: z57.string().optional().describe("Filter audit to a specific project")
18251
+ agent_id: z59.string().optional().describe("Filter audit to a specific agent"),
18252
+ project_name: z59.string().optional().describe("Filter audit to a specific project")
17524
18253
  }
17525
18254
  },
17526
18255
  async ({ agent_id, project_name }) => {
@@ -17553,13 +18282,13 @@ function registerRunMemoryAudit(server2) {
17553
18282
  }
17554
18283
 
17555
18284
  // src/mcp/tools/cloud-sync.ts
17556
- import { z as z58 } from "zod";
18285
+ import { z as z60 } from "zod";
17557
18286
 
17558
18287
  // src/lib/cloud-sync.ts
17559
18288
  init_database();
17560
- import { readFileSync as readFileSync22, writeFileSync as writeFileSync17, existsSync as existsSync28, readdirSync as readdirSync11, mkdirSync as mkdirSync14, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
18289
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync19, existsSync as existsSync29, readdirSync as readdirSync11, mkdirSync as mkdirSync16, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
17561
18290
  import crypto16 from "crypto";
17562
- import path35 from "path";
18291
+ import path37 from "path";
17563
18292
  import { homedir as homedir6 } from "os";
17564
18293
 
17565
18294
  // src/lib/crypto.ts
@@ -17633,7 +18362,7 @@ function sqlSafe(v) {
17633
18362
  }
17634
18363
  function logError(msg) {
17635
18364
  try {
17636
- const logPath = path35.join(homedir6(), ".exe-os", "workers.log");
18365
+ const logPath = path37.join(homedir6(), ".exe-os", "workers.log");
17637
18366
  appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
17638
18367
  `);
17639
18368
  } catch {
@@ -17642,24 +18371,24 @@ function logError(msg) {
17642
18371
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
17643
18372
  var FETCH_TIMEOUT_MS4 = 3e4;
17644
18373
  var PUSH_BATCH_SIZE = 5e3;
17645
- var ROSTER_LOCK_PATH = path35.join(EXE_AI_DIR, "roster-merge.lock");
18374
+ var ROSTER_LOCK_PATH = path37.join(EXE_AI_DIR, "roster-merge.lock");
17646
18375
  var LOCK_STALE_MS = 3e4;
17647
18376
  async function withRosterLock(fn) {
17648
18377
  try {
17649
18378
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
17650
18379
  closeSync2(fd);
17651
- writeFileSync17(ROSTER_LOCK_PATH, String(Date.now()));
18380
+ writeFileSync19(ROSTER_LOCK_PATH, String(Date.now()));
17652
18381
  } catch (err) {
17653
18382
  if (err.code === "EEXIST") {
17654
18383
  try {
17655
- const ts2 = parseInt(readFileSync22(ROSTER_LOCK_PATH, "utf-8"), 10);
18384
+ const ts2 = parseInt(readFileSync25(ROSTER_LOCK_PATH, "utf-8"), 10);
17656
18385
  if (Date.now() - ts2 < LOCK_STALE_MS) {
17657
18386
  throw new Error("Roster merge already in progress \u2014 another sync is running");
17658
18387
  }
17659
18388
  unlinkSync10(ROSTER_LOCK_PATH);
17660
18389
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
17661
18390
  closeSync2(fd);
17662
- writeFileSync17(ROSTER_LOCK_PATH, String(Date.now()));
18391
+ writeFileSync19(ROSTER_LOCK_PATH, String(Date.now()));
17663
18392
  } catch (retryErr) {
17664
18393
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
17665
18394
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -17678,21 +18407,21 @@ async function withRosterLock(fn) {
17678
18407
  }
17679
18408
  }
17680
18409
  async function fetchWithRetry(url, init) {
17681
- const MAX_RETRIES3 = 3;
18410
+ const MAX_RETRIES4 = 3;
17682
18411
  const BASE_DELAY_MS2 = 200;
17683
18412
  let lastError;
17684
- for (let attempt = 0; attempt <= MAX_RETRIES3; attempt++) {
18413
+ for (let attempt = 0; attempt <= MAX_RETRIES4; attempt++) {
17685
18414
  try {
17686
18415
  const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS4);
17687
18416
  const resp = await fetch(url, { ...init, signal });
17688
- if (resp && resp.status >= 500 && attempt < MAX_RETRIES3) {
18417
+ if (resp && resp.status >= 500 && attempt < MAX_RETRIES4) {
17689
18418
  await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
17690
18419
  continue;
17691
18420
  }
17692
18421
  return resp;
17693
18422
  } catch (err) {
17694
18423
  lastError = err;
17695
- if (attempt === MAX_RETRIES3) throw err;
18424
+ if (attempt === MAX_RETRIES4) throw err;
17696
18425
  await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
17697
18426
  }
17698
18427
  }
@@ -18033,8 +18762,8 @@ async function cloudSync(config2) {
18033
18762
  try {
18034
18763
  const employees = await loadEmployees();
18035
18764
  rosterResult.employees = employees.length;
18036
- const idDir = path35.join(EXE_AI_DIR, "identity");
18037
- if (existsSync28(idDir)) {
18765
+ const idDir = path37.join(EXE_AI_DIR, "identity");
18766
+ if (existsSync29(idDir)) {
18038
18767
  rosterResult.identities = readdirSync11(idDir).filter((f) => f.endsWith(".md")).length;
18039
18768
  }
18040
18769
  } catch {
@@ -18052,49 +18781,49 @@ async function cloudSync(config2) {
18052
18781
  roster: rosterResult
18053
18782
  };
18054
18783
  }
18055
- var ROSTER_DELETIONS_PATH = path35.join(EXE_AI_DIR, "roster-deletions.json");
18784
+ var ROSTER_DELETIONS_PATH = path37.join(EXE_AI_DIR, "roster-deletions.json");
18056
18785
  function consumeRosterDeletions() {
18057
18786
  try {
18058
- if (!existsSync28(ROSTER_DELETIONS_PATH)) return [];
18059
- const deletions = JSON.parse(readFileSync22(ROSTER_DELETIONS_PATH, "utf-8"));
18060
- writeFileSync17(ROSTER_DELETIONS_PATH, "[]");
18787
+ if (!existsSync29(ROSTER_DELETIONS_PATH)) return [];
18788
+ const deletions = JSON.parse(readFileSync25(ROSTER_DELETIONS_PATH, "utf-8"));
18789
+ writeFileSync19(ROSTER_DELETIONS_PATH, "[]");
18061
18790
  return deletions;
18062
18791
  } catch {
18063
18792
  return [];
18064
18793
  }
18065
18794
  }
18066
18795
  function buildRosterBlob(paths) {
18067
- const rosterPath = paths?.rosterPath ?? path35.join(EXE_AI_DIR, "exe-employees.json");
18068
- const identityDir = paths?.identityDir ?? path35.join(EXE_AI_DIR, "identity");
18069
- const configPath = paths?.configPath ?? path35.join(EXE_AI_DIR, "config.json");
18796
+ const rosterPath = paths?.rosterPath ?? path37.join(EXE_AI_DIR, "exe-employees.json");
18797
+ const identityDir = paths?.identityDir ?? path37.join(EXE_AI_DIR, "identity");
18798
+ const configPath = paths?.configPath ?? path37.join(EXE_AI_DIR, "config.json");
18070
18799
  let roster = [];
18071
- if (existsSync28(rosterPath)) {
18800
+ if (existsSync29(rosterPath)) {
18072
18801
  try {
18073
- roster = JSON.parse(readFileSync22(rosterPath, "utf-8"));
18802
+ roster = JSON.parse(readFileSync25(rosterPath, "utf-8"));
18074
18803
  } catch {
18075
18804
  }
18076
18805
  }
18077
18806
  const identities = {};
18078
- if (existsSync28(identityDir)) {
18807
+ if (existsSync29(identityDir)) {
18079
18808
  for (const file of readdirSync11(identityDir).filter((f) => f.endsWith(".md"))) {
18080
18809
  try {
18081
- identities[file] = readFileSync22(path35.join(identityDir, file), "utf-8");
18810
+ identities[file] = readFileSync25(path37.join(identityDir, file), "utf-8");
18082
18811
  } catch {
18083
18812
  }
18084
18813
  }
18085
18814
  }
18086
18815
  let config2;
18087
- if (existsSync28(configPath)) {
18816
+ if (existsSync29(configPath)) {
18088
18817
  try {
18089
- config2 = JSON.parse(readFileSync22(configPath, "utf-8"));
18818
+ config2 = JSON.parse(readFileSync25(configPath, "utf-8"));
18090
18819
  } catch {
18091
18820
  }
18092
18821
  }
18093
18822
  let agentConfig;
18094
- const agentConfigPath = path35.join(EXE_AI_DIR, "agent-config.json");
18095
- if (existsSync28(agentConfigPath)) {
18823
+ const agentConfigPath = path37.join(EXE_AI_DIR, "agent-config.json");
18824
+ if (existsSync29(agentConfigPath)) {
18096
18825
  try {
18097
- agentConfig = JSON.parse(readFileSync22(agentConfigPath, "utf-8"));
18826
+ agentConfig = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
18098
18827
  } catch {
18099
18828
  }
18100
18829
  }
@@ -18170,23 +18899,23 @@ async function cloudPullRoster(config2) {
18170
18899
  }
18171
18900
  }
18172
18901
  function mergeConfig(remoteConfig, configPath) {
18173
- const cfgPath = configPath ?? path35.join(EXE_AI_DIR, "config.json");
18902
+ const cfgPath = configPath ?? path37.join(EXE_AI_DIR, "config.json");
18174
18903
  let local = {};
18175
- if (existsSync28(cfgPath)) {
18904
+ if (existsSync29(cfgPath)) {
18176
18905
  try {
18177
- local = JSON.parse(readFileSync22(cfgPath, "utf-8"));
18906
+ local = JSON.parse(readFileSync25(cfgPath, "utf-8"));
18178
18907
  } catch {
18179
18908
  }
18180
18909
  }
18181
18910
  const merged = { ...remoteConfig, ...local };
18182
- const dir = path35.dirname(cfgPath);
18183
- if (!existsSync28(dir)) mkdirSync14(dir, { recursive: true });
18184
- writeFileSync17(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
18911
+ const dir = path37.dirname(cfgPath);
18912
+ if (!existsSync29(dir)) mkdirSync16(dir, { recursive: true });
18913
+ writeFileSync19(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
18185
18914
  }
18186
18915
  async function mergeRosterFromRemote(remote, paths) {
18187
18916
  return withRosterLock(async () => {
18188
18917
  const rosterPath = paths?.rosterPath ?? void 0;
18189
- const identityDir = paths?.identityDir ?? path35.join(EXE_AI_DIR, "identity");
18918
+ const identityDir = paths?.identityDir ?? path37.join(EXE_AI_DIR, "identity");
18190
18919
  const localEmployees = await loadEmployees(rosterPath);
18191
18920
  const localNames = new Set(localEmployees.map((e) => e.name));
18192
18921
  let added = 0;
@@ -18207,15 +18936,15 @@ async function mergeRosterFromRemote(remote, paths) {
18207
18936
  ) ?? lookupKey;
18208
18937
  const remoteIdentity = remote.identities[matchedKey];
18209
18938
  if (remoteIdentity) {
18210
- if (!existsSync28(identityDir)) mkdirSync14(identityDir, { recursive: true });
18211
- const idPath = path35.join(identityDir, `${remoteEmp.name}.md`);
18939
+ if (!existsSync29(identityDir)) mkdirSync16(identityDir, { recursive: true });
18940
+ const idPath = path37.join(identityDir, `${remoteEmp.name}.md`);
18212
18941
  let localIdentity = null;
18213
18942
  try {
18214
- localIdentity = existsSync28(idPath) ? readFileSync22(idPath, "utf-8") : null;
18943
+ localIdentity = existsSync29(idPath) ? readFileSync25(idPath, "utf-8") : null;
18215
18944
  } catch {
18216
18945
  }
18217
18946
  if (localIdentity !== remoteIdentity) {
18218
- writeFileSync17(idPath, remoteIdentity, "utf-8");
18947
+ writeFileSync19(idPath, remoteIdentity, "utf-8");
18219
18948
  identitiesUpdated++;
18220
18949
  }
18221
18950
  }
@@ -18241,16 +18970,16 @@ async function mergeRosterFromRemote(remote, paths) {
18241
18970
  }
18242
18971
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
18243
18972
  try {
18244
- const agentConfigPath = path35.join(EXE_AI_DIR, "agent-config.json");
18973
+ const agentConfigPath = path37.join(EXE_AI_DIR, "agent-config.json");
18245
18974
  let local = {};
18246
- if (existsSync28(agentConfigPath)) {
18975
+ if (existsSync29(agentConfigPath)) {
18247
18976
  try {
18248
- local = JSON.parse(readFileSync22(agentConfigPath, "utf-8"));
18977
+ local = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
18249
18978
  } catch {
18250
18979
  }
18251
18980
  }
18252
18981
  const merged = { ...remote.agentConfig, ...local };
18253
- writeFileSync17(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
18982
+ writeFileSync19(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
18254
18983
  } catch {
18255
18984
  }
18256
18985
  }
@@ -18684,7 +19413,7 @@ function registerCloudSync(server2) {
18684
19413
  title: "Cloud Sync",
18685
19414
  description: "Trigger a cloud sync cycle \u2014 pulls remote changes then pushes local. Reports pushed/pulled counts for memories, behaviors, graph, tasks, and more.",
18686
19415
  inputSchema: {
18687
- force: z58.boolean().default(false).describe("Force sync even if recently synced")
19416
+ force: z60.boolean().default(false).describe("Force sync even if recently synced")
18688
19417
  }
18689
19418
  },
18690
19419
  async () => {
@@ -18737,7 +19466,7 @@ function registerCloudSync(server2) {
18737
19466
 
18738
19467
  // src/mcp/tools/get-memory-cardinality.ts
18739
19468
  init_database();
18740
- import { z as z59 } from "zod";
19469
+ import { z as z61 } from "zod";
18741
19470
  function registerGetMemoryCardinality(server2) {
18742
19471
  server2.registerTool(
18743
19472
  "get_memory_cardinality",
@@ -18745,7 +19474,7 @@ function registerGetMemoryCardinality(server2) {
18745
19474
  title: "Get Memory Cardinality",
18746
19475
  description: "Get memory counts: total active memories and per-project breakdown. Optionally filter by agent.",
18747
19476
  inputSchema: {
18748
- agent_id: z59.string().optional().describe("Filter to a specific agent (e.g. 'tom', 'exe')")
19477
+ agent_id: z61.string().optional().describe("Filter to a specific agent (e.g. 'tom', 'exe')")
18749
19478
  }
18750
19479
  },
18751
19480
  async ({ agent_id }) => {
@@ -18828,7 +19557,7 @@ function registerGetMemoryCardinality(server2) {
18828
19557
  init_database();
18829
19558
  init_consolidation();
18830
19559
  init_config();
18831
- import { z as z60 } from "zod";
19560
+ import { z as z62 } from "zod";
18832
19561
  function registerRunConsolidation(server2) {
18833
19562
  server2.registerTool(
18834
19563
  "run_consolidation",
@@ -18836,7 +19565,7 @@ function registerRunConsolidation(server2) {
18836
19565
  title: "Run Consolidation",
18837
19566
  description: "Run memory consolidation \u2014 synthesizes unconsolidated memories into meta-insights. Use dry_run=true (default) to preview without executing.",
18838
19567
  inputSchema: {
18839
- dry_run: z60.boolean().default(true).describe("Preview mode \u2014 show what would be consolidated without doing it")
19568
+ dry_run: z62.boolean().default(true).describe("Preview mode \u2014 show what would be consolidated without doing it")
18840
19569
  }
18841
19570
  },
18842
19571
  async ({ dry_run }) => {
@@ -18905,7 +19634,7 @@ function registerRunConsolidation(server2) {
18905
19634
 
18906
19635
  // src/mcp/tools/get-license-status.ts
18907
19636
  init_license();
18908
- import { z as z61 } from "zod";
19637
+ import { z as z63 } from "zod";
18909
19638
  var FEATURES = [
18910
19639
  "cloud_sync",
18911
19640
  "external_agents",
@@ -18919,7 +19648,7 @@ function registerGetLicenseStatus(server2) {
18919
19648
  title: "Get License Status",
18920
19649
  description: "Get current license status: plan, validity, feature gates, limits, and expiry.",
18921
19650
  inputSchema: {
18922
- _dummy: z61.string().optional().describe("Unused \u2014 no input required")
19651
+ _dummy: z63.string().optional().describe("Unused \u2014 no input required")
18923
19652
  }
18924
19653
  },
18925
19654
  async () => {
@@ -18971,16 +19700,16 @@ function registerGetLicenseStatus(server2) {
18971
19700
  }
18972
19701
 
18973
19702
  // src/mcp/tools/people-roster.ts
18974
- import { z as z62 } from "zod";
19703
+ import { z as z64 } from "zod";
18975
19704
 
18976
19705
  // src/lib/people.ts
18977
19706
  init_config();
18978
19707
  import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
18979
- import { existsSync as existsSync29, readFileSync as readFileSync23 } from "fs";
18980
- import path36 from "path";
18981
- var PEOPLE_PATH = path36.join(EXE_AI_DIR, "people.json");
19708
+ import { existsSync as existsSync30, readFileSync as readFileSync26 } from "fs";
19709
+ import path38 from "path";
19710
+ var PEOPLE_PATH = path38.join(EXE_AI_DIR, "people.json");
18982
19711
  async function loadPeople() {
18983
- if (!existsSync29(PEOPLE_PATH)) return [];
19712
+ if (!existsSync30(PEOPLE_PATH)) return [];
18984
19713
  try {
18985
19714
  const raw = await readFile5(PEOPLE_PATH, "utf-8");
18986
19715
  return JSON.parse(raw);
@@ -18989,7 +19718,7 @@ async function loadPeople() {
18989
19718
  }
18990
19719
  }
18991
19720
  async function savePeople(people) {
18992
- await mkdir5(path36.dirname(PEOPLE_PATH), { recursive: true });
19721
+ await mkdir5(path38.dirname(PEOPLE_PATH), { recursive: true });
18993
19722
  await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
18994
19723
  }
18995
19724
  async function addPerson(person) {
@@ -19019,10 +19748,10 @@ function registerAddPerson(server2) {
19019
19748
  title: "Add Person",
19020
19749
  description: "Add or update a key human in the people roster. Used for co-founders, partners, customers \u2014 anyone agents need to know about.",
19021
19750
  inputSchema: {
19022
- name: z62.string().describe("Person's name"),
19023
- role: z62.string().describe("Their role (e.g. co-founder, customer, partner)"),
19024
- relationship: z62.string().describe("Relationship to the organization (e.g. co-founder, early adopter, investor)"),
19025
- notes: z62.string().optional().describe("Additional context about this person")
19751
+ name: z64.string().describe("Person's name"),
19752
+ role: z64.string().describe("Their role (e.g. co-founder, customer, partner)"),
19753
+ relationship: z64.string().describe("Relationship to the organization (e.g. co-founder, early adopter, investor)"),
19754
+ notes: z64.string().optional().describe("Additional context about this person")
19026
19755
  }
19027
19756
  },
19028
19757
  async ({ name, role, relationship, notes }) => {
@@ -19068,7 +19797,7 @@ function registerGetPerson(server2) {
19068
19797
  title: "Get Person",
19069
19798
  description: "Look up a specific person by name from the people roster.",
19070
19799
  inputSchema: {
19071
- name: z62.string().describe("Person's name to look up")
19800
+ name: z64.string().describe("Person's name to look up")
19072
19801
  }
19073
19802
  },
19074
19803
  async ({ name }) => {
@@ -19096,7 +19825,7 @@ init_active_agent();
19096
19825
  init_agent_config();
19097
19826
  init_runtime_table();
19098
19827
  init_employees();
19099
- import { z as z63 } from "zod";
19828
+ import { z as z65 } from "zod";
19100
19829
  function registerSetAgentConfig(server2) {
19101
19830
  server2.registerTool(
19102
19831
  "set_agent_config",
@@ -19104,9 +19833,9 @@ function registerSetAgentConfig(server2) {
19104
19833
  title: "Set Agent Config",
19105
19834
  description: "Set or view per-agent runtime + model configuration. Controls which runtime (claude, codex, opencode) and model each agent uses when dispatched. COO-only. Omit runtime/model to view current config for an agent or all agents.",
19106
19835
  inputSchema: {
19107
- agent_id: z63.string().optional().describe("Agent name, or 'default' for org-wide default. Omit to view all agents."),
19108
- runtime: z63.string().optional().describe(`Runtime: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`),
19109
- model: z63.string().optional().describe("Model name (e.g. claude-opus-4, gpt-5.4, anthropic/claude-sonnet-4-6)")
19836
+ agent_id: z65.string().optional().describe("Agent name, or 'default' for org-wide default. Omit to view all agents."),
19837
+ runtime: z65.string().optional().describe(`Runtime: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`),
19838
+ model: z65.string().optional().describe("Model name (e.g. claude-opus-4, gpt-5.4, anthropic/claude-sonnet-4-6)")
19110
19839
  }
19111
19840
  },
19112
19841
  async ({ agent_id, runtime, model }) => {
@@ -19299,6 +20028,8 @@ registerListWikiPages(server);
19299
20028
  registerGetWikiPage(server);
19300
20029
  registerUpdateWikiPage(server);
19301
20030
  registerDeployClient(server);
20031
+ registerExportOrchestration(server);
20032
+ registerImportOrchestration(server);
19302
20033
  registerQueryConversations(server);
19303
20034
  registerLoadSkill(server);
19304
20035
  registerConsolidateMemories(server);
@@ -19390,14 +20121,14 @@ try {
19390
20121
  `
19391
20122
  );
19392
20123
  const thisFile = fileURLToPath5(import.meta.url);
19393
- const backfillPath = path37.resolve(
19394
- path37.dirname(thisFile),
20124
+ const backfillPath = path39.resolve(
20125
+ path39.dirname(thisFile),
19395
20126
  "../bin/backfill-vectors.js"
19396
20127
  );
19397
- if (existsSync30(backfillPath)) {
20128
+ if (existsSync31(backfillPath)) {
19398
20129
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
19399
- const logPath = path37.join(exeDir, "workers.log");
19400
- mkdirSync15(path37.dirname(logPath), { recursive: true });
20130
+ const logPath = path39.join(exeDir, "workers.log");
20131
+ mkdirSync17(path39.dirname(logPath), { recursive: true });
19401
20132
  let logFd = "ignore";
19402
20133
  try {
19403
20134
  logFd = openSync3(logPath, "a");