@askexenow/exe-os 0.9.87 → 0.9.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/deploy/compose/docker-compose.yml +3 -3
  2. package/dist/bin/age-ontology-load.js +8 -2
  3. package/dist/bin/agentic-ontology-backfill.js +11 -0
  4. package/dist/bin/agentic-reflection-backfill.js +11 -0
  5. package/dist/bin/agentic-semantic-label.js +11 -0
  6. package/dist/bin/backfill-conversations.js +12 -0
  7. package/dist/bin/backfill-responses.js +12 -0
  8. package/dist/bin/backfill-vectors.js +12 -0
  9. package/dist/bin/bulk-sync-postgres.js +29 -1
  10. package/dist/bin/cc-doctor.js +3 -2
  11. package/dist/bin/cleanup-stale-review-tasks.js +12 -0
  12. package/dist/bin/cli.js +170 -15
  13. package/dist/bin/exe-agent.js +1 -0
  14. package/dist/bin/exe-assign.js +12 -0
  15. package/dist/bin/exe-boot.js +139 -4
  16. package/dist/bin/exe-call.js +2 -0
  17. package/dist/bin/exe-cloud.js +138 -3
  18. package/dist/bin/exe-dispatch.js +12 -1
  19. package/dist/bin/exe-doctor.js +12 -0
  20. package/dist/bin/exe-export-behaviors.js +11 -0
  21. package/dist/bin/exe-forget.js +12 -0
  22. package/dist/bin/exe-gateway.js +132 -35
  23. package/dist/bin/exe-healthcheck.js +2 -1
  24. package/dist/bin/exe-heartbeat.js +12 -0
  25. package/dist/bin/exe-kill.js +11 -0
  26. package/dist/bin/exe-launch-agent.js +11 -0
  27. package/dist/bin/exe-new-employee.js +4 -2
  28. package/dist/bin/exe-pending-messages.js +11 -0
  29. package/dist/bin/exe-pending-notifications.js +12 -0
  30. package/dist/bin/exe-pending-reviews.js +12 -0
  31. package/dist/bin/exe-rename.js +12 -0
  32. package/dist/bin/exe-review.js +12 -0
  33. package/dist/bin/exe-search.js +12 -0
  34. package/dist/bin/exe-session-cleanup.js +12 -1
  35. package/dist/bin/exe-settings.js +3 -0
  36. package/dist/bin/exe-start-codex.js +13 -2
  37. package/dist/bin/exe-start-opencode.js +13 -2
  38. package/dist/bin/exe-status.js +12 -0
  39. package/dist/bin/exe-team.js +12 -0
  40. package/dist/bin/git-sweep.js +12 -1
  41. package/dist/bin/graph-backfill.js +11 -0
  42. package/dist/bin/graph-export.js +11 -0
  43. package/dist/bin/graph-layer-benchmark.js +9 -1
  44. package/dist/bin/intercom-check.js +13 -1
  45. package/dist/bin/list-providers.js +1 -0
  46. package/dist/bin/postgres-agentic-reflection-backfill.js +7 -1
  47. package/dist/bin/postgres-agentic-semantic-backfill.js +7 -1
  48. package/dist/bin/registry-proxy.js +1 -0
  49. package/dist/bin/scan-tasks.js +13 -1
  50. package/dist/bin/setup.js +141 -7
  51. package/dist/bin/shard-migrate.js +11 -0
  52. package/dist/bin/stack-update.js +24 -7
  53. package/dist/bin/update.js +5 -0
  54. package/dist/gateway/index.js +12 -1
  55. package/dist/hooks/bug-report-worker.js +12 -1
  56. package/dist/hooks/codex-stop-task-finalizer.js +12 -1
  57. package/dist/hooks/commit-complete.js +12 -1
  58. package/dist/hooks/error-recall.js +11 -0
  59. package/dist/hooks/ingest.js +11 -0
  60. package/dist/hooks/instructions-loaded.js +11 -0
  61. package/dist/hooks/notification.js +11 -0
  62. package/dist/hooks/post-compact.js +11 -0
  63. package/dist/hooks/post-tool-combined.js +11 -0
  64. package/dist/hooks/pre-compact.js +12 -1
  65. package/dist/hooks/pre-tool-use.js +11 -0
  66. package/dist/hooks/prompt-submit.js +12 -1
  67. package/dist/hooks/session-end.js +12 -1
  68. package/dist/hooks/session-start.js +11 -0
  69. package/dist/hooks/stop.js +11 -0
  70. package/dist/hooks/subagent-stop.js +11 -0
  71. package/dist/hooks/summary-worker.js +137 -3
  72. package/dist/index.js +12 -1
  73. package/dist/lib/cloud-sync.js +136 -2
  74. package/dist/lib/consolidation.js +1 -0
  75. package/dist/lib/database.js +11 -0
  76. package/dist/lib/db.js +11 -0
  77. package/dist/lib/device-registry.js +11 -0
  78. package/dist/lib/employee-templates.js +1 -0
  79. package/dist/lib/exe-daemon.js +771 -49
  80. package/dist/lib/hybrid-search.js +11 -0
  81. package/dist/lib/identity.js +1 -0
  82. package/dist/lib/messaging.js +2 -1
  83. package/dist/lib/reminders.js +1 -0
  84. package/dist/lib/schedules.js +11 -0
  85. package/dist/lib/skill-learning.js +1 -0
  86. package/dist/lib/store.js +11 -0
  87. package/dist/lib/tasks.js +2 -1
  88. package/dist/lib/tmux-routing.js +2 -1
  89. package/dist/lib/token-spend.js +1 -0
  90. package/dist/lib/ws-client.js +8 -0
  91. package/dist/mcp/server.js +613 -27
  92. package/dist/mcp/tools/complete-reminder.js +1 -0
  93. package/dist/mcp/tools/create-reminder.js +1 -0
  94. package/dist/mcp/tools/create-task.js +2 -1
  95. package/dist/mcp/tools/deactivate-behavior.js +1 -0
  96. package/dist/mcp/tools/list-reminders.js +1 -0
  97. package/dist/mcp/tools/list-tasks.js +1 -0
  98. package/dist/mcp/tools/send-message.js +2 -1
  99. package/dist/mcp/tools/update-task.js +2 -1
  100. package/dist/runtime/index.js +12 -1
  101. package/dist/tui/App.js +12 -1
  102. package/package.json +2 -2
@@ -2180,6 +2180,7 @@ __export(database_exports, {
2180
2180
  isInitialized: () => isInitialized,
2181
2181
  setExternalClient: () => setExternalClient
2182
2182
  });
2183
+ import { chmodSync as chmodSync2 } from "fs";
2183
2184
  import { createClient } from "@libsql/client";
2184
2185
  async function initDatabase(config2) {
2185
2186
  if (_walCheckpointTimer) {
@@ -2221,6 +2222,16 @@ async function initDatabase(config2) {
2221
2222
  if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
2222
2223
  _adapterClient = await createPrismaDbAdapter(_resilientClient);
2223
2224
  }
2225
+ try {
2226
+ chmodSync2(config2.dbPath, 384);
2227
+ for (const suffix of ["-wal", "-shm"]) {
2228
+ try {
2229
+ chmodSync2(config2.dbPath + suffix, 384);
2230
+ } catch {
2231
+ }
2232
+ }
2233
+ } catch {
2234
+ }
2224
2235
  }
2225
2236
  function isInitialized() {
2226
2237
  return _adapterClient !== null || _client !== null;
@@ -9426,7 +9437,7 @@ function readQueue() {
9426
9437
  function writeQueue(queue) {
9427
9438
  ensureDir();
9428
9439
  const tmp = `${QUEUE_PATH}.tmp`;
9429
- writeFileSync9(tmp, JSON.stringify(queue, null, 2));
9440
+ writeFileSync9(tmp, JSON.stringify(queue, null, 2), { mode: 384 });
9430
9441
  renameSync4(tmp, QUEUE_PATH);
9431
9442
  }
9432
9443
  function queueIntercom(targetSession, reason) {
@@ -13996,6 +14007,21 @@ var init_crdt_sync = __esm({
13996
14007
  }
13997
14008
  });
13998
14009
 
14010
+ // src/lib/pg-ssl.ts
14011
+ var pg_ssl_exports = {};
14012
+ __export(pg_ssl_exports, {
14013
+ pgSslConfig: () => pgSslConfig
14014
+ });
14015
+ function pgSslConfig() {
14016
+ if (process.env.EXE_DB_SSL_DISABLED === "true") return {};
14017
+ return { ssl: { rejectUnauthorized: process.env.EXE_DB_SSL_ALLOW_SELFSIGNED !== "true" } };
14018
+ }
14019
+ var init_pg_ssl = __esm({
14020
+ "src/lib/pg-ssl.ts"() {
14021
+ "use strict";
14022
+ }
14023
+ });
14024
+
13999
14025
  // src/lib/tmux-status.ts
14000
14026
  import { execSync as execSync13 } from "child_process";
14001
14027
  function inTmux() {
@@ -19940,6 +19966,130 @@ import { existsSync as existsSync24, readFileSync as readFileSync20 } from "fs";
19940
19966
  import path30 from "path";
19941
19967
  import { homedir as homedir4 } from "os";
19942
19968
  import { z as z54 } from "zod";
19969
+
19970
+ // src/mcp/license-gate.ts
19971
+ init_license();
19972
+ var _cachedLicense = null;
19973
+ var _hasLicenseKey = false;
19974
+ async function initLicenseGate() {
19975
+ const key = loadLicense();
19976
+ _hasLicenseKey = key !== null && key.length > 0;
19977
+ if (_hasLicenseKey) {
19978
+ try {
19979
+ _cachedLicense = await checkLicense();
19980
+ } catch {
19981
+ _cachedLicense = null;
19982
+ }
19983
+ return { license: _cachedLicense, hasKey: true };
19984
+ }
19985
+ return { license: null, hasKey: false };
19986
+ }
19987
+ function getCachedLicenseGate() {
19988
+ return {
19989
+ license: _cachedLicense,
19990
+ hasKey: _hasLicenseKey
19991
+ };
19992
+ }
19993
+
19994
+ // src/mcp/tool-telemetry.ts
19995
+ var _toolCalls = /* @__PURE__ */ new Map();
19996
+ var _sessionStartedAt = Date.now();
19997
+ var _flushTimer2 = null;
19998
+ function recordCall(toolName, action, isError) {
19999
+ let record = _toolCalls.get(toolName);
20000
+ if (!record) {
20001
+ record = { count: 0, actions: /* @__PURE__ */ new Map(), lastCalledAt: 0, errors: 0 };
20002
+ _toolCalls.set(toolName, record);
20003
+ }
20004
+ record.count++;
20005
+ record.lastCalledAt = Date.now();
20006
+ if (isError) record.errors++;
20007
+ if (action) {
20008
+ record.actions.set(action, (record.actions.get(action) ?? 0) + 1);
20009
+ }
20010
+ }
20011
+ function wrapServerWithTelemetry(server2) {
20012
+ const originalRegisterTool = server2.registerTool.bind(server2);
20013
+ server2.registerTool = (name, config2, handler) => {
20014
+ const wrappedHandler = async (input, extra) => {
20015
+ const action = input.action;
20016
+ try {
20017
+ const result3 = await handler(input, extra);
20018
+ const isError = result3?.isError === true;
20019
+ recordCall(name, action, isError);
20020
+ return result3;
20021
+ } catch (err) {
20022
+ recordCall(name, action, true);
20023
+ throw err;
20024
+ }
20025
+ };
20026
+ return originalRegisterTool(name, config2, wrappedHandler);
20027
+ };
20028
+ return server2;
20029
+ }
20030
+ function getToolUsageStats() {
20031
+ let totalCalls = 0;
20032
+ let totalErrors = 0;
20033
+ const tools = {};
20034
+ for (const [name, record] of _toolCalls) {
20035
+ totalCalls += record.count;
20036
+ totalErrors += record.errors;
20037
+ const entry = {
20038
+ calls: record.count,
20039
+ errors: record.errors,
20040
+ lastCalledAt: new Date(record.lastCalledAt).toISOString()
20041
+ };
20042
+ if (record.actions.size > 0) {
20043
+ entry.actions = Object.fromEntries(record.actions);
20044
+ }
20045
+ tools[name] = entry;
20046
+ }
20047
+ return {
20048
+ sessionStartedAt: new Date(_sessionStartedAt).toISOString(),
20049
+ uptimeMs: Date.now() - _sessionStartedAt,
20050
+ totalCalls,
20051
+ totalErrors,
20052
+ tools
20053
+ };
20054
+ }
20055
+ var FLUSH_INTERVAL_MS = 5 * 60 * 1e3;
20056
+ var _lastFlushedAt = 0;
20057
+ async function flushToMemory() {
20058
+ const stats = getToolUsageStats();
20059
+ if (stats.totalCalls === 0) return;
20060
+ if (stats.totalCalls === _lastFlushedAt) return;
20061
+ try {
20062
+ const { getClient: getClient2, isInitialized: isInitialized2 } = await Promise.resolve().then(() => (init_database(), database_exports));
20063
+ if (!isInitialized2()) return;
20064
+ const client = getClient2();
20065
+ const toolSummary = Object.entries(stats.tools).sort((a, b) => b[1].calls - a[1].calls).map(([name, t]) => {
20066
+ const actionStr = t.actions ? ` (${Object.entries(t.actions).sort((a, b) => b[1] - a[1]).map(([a, c]) => `${a}:${c}`).join(", ")})` : "";
20067
+ return `${name}: ${t.calls} calls${t.errors > 0 ? ` (${t.errors} errors)` : ""}${actionStr}`;
20068
+ }).join("\n");
20069
+ const raw_text = `Tool usage since ${stats.sessionStartedAt} (${Math.round(stats.uptimeMs / 6e4)}min):
20070
+ Total: ${stats.totalCalls} calls, ${stats.totalErrors} errors
20071
+
20072
+ ${toolSummary}`;
20073
+ await client.execute({
20074
+ sql: `INSERT INTO memories (id, agent_id, raw_text, memory_type, project_name, importance, created_at, updated_at)
20075
+ VALUES (?, 'system', ?, 'telemetry', 'exe-os', 2, datetime('now'), datetime('now'))`,
20076
+ args: [
20077
+ `telemetry-tools-${Date.now()}`,
20078
+ raw_text
20079
+ ]
20080
+ });
20081
+ _lastFlushedAt = stats.totalCalls;
20082
+ } catch {
20083
+ }
20084
+ }
20085
+ function startToolTelemetryFlush() {
20086
+ if (_flushTimer2) return;
20087
+ _sessionStartedAt = Date.now();
20088
+ _flushTimer2 = setInterval(() => void flushToMemory(), FLUSH_INTERVAL_MS);
20089
+ _flushTimer2.unref();
20090
+ }
20091
+
20092
+ // src/mcp/tools/mcp-ping.ts
19943
20093
  var PID_PATH3 = path30.join(homedir4(), ".exe-os", "exed.pid");
19944
20094
  function isDaemonAlive2() {
19945
20095
  try {
@@ -19966,6 +20116,7 @@ function registerMcpPing(server2) {
19966
20116
  const daemon = isDaemonAlive2();
19967
20117
  const summary = summarizeMcpTransport();
19968
20118
  writeMcpTransportSummary();
20119
+ const gate = getCachedLicenseGate();
19969
20120
  const status2 = daemon.alive ? "ok" : "degraded";
19970
20121
  return {
19971
20122
  content: [
@@ -19974,8 +20125,14 @@ function registerMcpPing(server2) {
19974
20125
  text: JSON.stringify({
19975
20126
  status: status2,
19976
20127
  daemon,
20128
+ licenseGate: {
20129
+ hasKey: gate.hasKey,
20130
+ plan: gate.license?.plan ?? "none",
20131
+ valid: gate.license?.valid ?? false
20132
+ },
20133
+ toolUsage: getToolUsageStats(),
19977
20134
  mcpTransport: summary,
19978
- remediation: daemon.alive ? "MCP transport probe reached the server. If tools still fail, reconnect the MCP client." : "Daemon is offline. Restart exe-os/exed; do not bypass MCP or access the DB directly."
20135
+ remediation: daemon.alive ? gate.hasKey ? "MCP transport probe reached the server. If tools still fail, reconnect the MCP client." : "No license key found. Run: echo 'exe_sk_YOUR_KEY' > ~/.exe-os/license.key then reconnect MCP." : "Daemon is offline. Restart exe-os/exed; do not bypass MCP or access the DB directly."
19979
20136
  }, null, 2)
19980
20137
  }
19981
20138
  ]
@@ -20181,6 +20338,7 @@ import { fileURLToPath as fileURLToPath4 } from "url";
20181
20338
  function isMainModule(importMetaUrl) {
20182
20339
  if (process.argv[1] == null) return false;
20183
20340
  if (process.argv[1].includes("mcp/server")) return false;
20341
+ if (process.argv[1].includes("exe-daemon")) return false;
20184
20342
  try {
20185
20343
  const scriptPath = realpathSync(process.argv[1]);
20186
20344
  const modulePath = realpathSync(fileURLToPath4(importMetaUrl));
@@ -21597,7 +21755,8 @@ function loadPgClient() {
21597
21755
  return new Ctor();
21598
21756
  }
21599
21757
  const { Pool } = await import("pg");
21600
- const pool = new Pool({ connectionString: process.env.DATABASE_URL });
21758
+ const { pgSslConfig: pgSslConfig2 } = await Promise.resolve().then(() => (init_pg_ssl(), pg_ssl_exports));
21759
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL, ...pgSslConfig2() });
21601
21760
  return {
21602
21761
  async $queryRawUnsafe(query, ...values) {
21603
21762
  const result3 = await pool.query(query, values);
@@ -22126,6 +22285,17 @@ async function cloudSync(config2) {
22126
22285
  } catch (err) {
22127
22286
  logError(`[cloud-sync] DB backup upload error: ${err instanceof Error ? err.message : String(err)}`);
22128
22287
  }
22288
+ let codeContextResult = { pushed: 0, pulled: 0 };
22289
+ try {
22290
+ codeContextResult.pushed = await cloudPushCodeContext(config2);
22291
+ } catch (err) {
22292
+ logError(`[cloud-sync] Code context push: ${err instanceof Error ? err.message : String(err)}`);
22293
+ }
22294
+ try {
22295
+ codeContextResult.pulled = await cloudPullCodeContext(config2);
22296
+ } catch (err) {
22297
+ logError(`[cloud-sync] Code context pull: ${err instanceof Error ? err.message : String(err)}`);
22298
+ }
22129
22299
  return {
22130
22300
  pushed,
22131
22301
  pulled,
@@ -22135,7 +22305,8 @@ async function cloudSync(config2) {
22135
22305
  tasks: tasksResult,
22136
22306
  conversations: conversationsResult,
22137
22307
  documents: documentsResult,
22138
- roster: rosterResult
22308
+ roster: rosterResult,
22309
+ codeContext: codeContextResult
22139
22310
  };
22140
22311
  }
22141
22312
  var ROSTER_DELETIONS_PATH = path37.join(EXE_AI_DIR, "roster-deletions.json");
@@ -22763,6 +22934,99 @@ async function cloudPullDocuments(config2) {
22763
22934
  }
22764
22935
  return { pulled };
22765
22936
  }
22937
+ var CODE_CONTEXT_DIR = path37.join(EXE_AI_DIR, "code-context");
22938
+ async function cloudPushCodeContext(config2) {
22939
+ assertSecureEndpoint(config2.endpoint);
22940
+ if (!existsSync32(CODE_CONTEXT_DIR)) return 0;
22941
+ const files = readdirSync11(CODE_CONTEXT_DIR).filter(
22942
+ (f) => f.endsWith(".json") && !f.endsWith(".vectors.json") && !f.startsWith(".")
22943
+ );
22944
+ if (files.length === 0) return 0;
22945
+ const metaPath = path37.join(CODE_CONTEXT_DIR, ".sync-meta.json");
22946
+ let syncMeta = {};
22947
+ if (existsSync32(metaPath)) {
22948
+ try {
22949
+ syncMeta = JSON.parse(readFileSync25(metaPath, "utf-8"));
22950
+ } catch {
22951
+ }
22952
+ }
22953
+ let pushed = 0;
22954
+ for (const file of files) {
22955
+ const filePath = path37.join(CODE_CONTEXT_DIR, file);
22956
+ try {
22957
+ const stat = statSync7(filePath);
22958
+ const lastPushed = syncMeta[file] ?? 0;
22959
+ if (stat.mtimeMs <= lastPushed) continue;
22960
+ const content = readFileSync25(filePath, "utf-8");
22961
+ const header = content.substring(0, 300);
22962
+ if (header.includes("/tmp") || header.includes("/var/folders") || header.includes(".worktrees/")) continue;
22963
+ const compressed = compress(Buffer.from(content, "utf8"));
22964
+ const encrypted = encryptSyncBlob(compressed);
22965
+ const resp = await fetchWithRetry(`${config2.endpoint}/sync/push-code-context`, {
22966
+ method: "POST",
22967
+ headers: {
22968
+ Authorization: `Bearer ${config2.apiKey}`,
22969
+ "Content-Type": "application/json",
22970
+ "X-Device-Id": loadDeviceId()
22971
+ },
22972
+ body: JSON.stringify({ key: file, blob: encrypted })
22973
+ });
22974
+ if (resp.ok) {
22975
+ syncMeta[file] = stat.mtimeMs;
22976
+ pushed++;
22977
+ }
22978
+ } catch {
22979
+ }
22980
+ }
22981
+ if (pushed > 0) {
22982
+ try {
22983
+ writeFileSync18(metaPath, JSON.stringify(syncMeta));
22984
+ } catch {
22985
+ }
22986
+ }
22987
+ return pushed;
22988
+ }
22989
+ async function cloudPullCodeContext(config2) {
22990
+ assertSecureEndpoint(config2.endpoint);
22991
+ try {
22992
+ const resp = await fetchWithRetry(`${config2.endpoint}/sync/pull-code-context`, {
22993
+ method: "GET",
22994
+ headers: {
22995
+ Authorization: `Bearer ${config2.apiKey}`,
22996
+ "X-Device-Id": loadDeviceId()
22997
+ }
22998
+ });
22999
+ if (!resp.ok) return 0;
23000
+ const data = await resp.json();
23001
+ if (!data.indexes || data.indexes.length === 0) return 0;
23002
+ mkdirSync16(CODE_CONTEXT_DIR, { recursive: true });
23003
+ let pulled = 0;
23004
+ for (const { key, blob } of data.indexes) {
23005
+ try {
23006
+ if (key.endsWith(".vectors.json")) continue;
23007
+ const localPath = path37.join(CODE_CONTEXT_DIR, key);
23008
+ const compressed = decryptSyncBlob(blob);
23009
+ const content = decompress(compressed).toString("utf8");
23010
+ if (!existsSync32(localPath)) {
23011
+ writeFileSync18(localPath, content, "utf-8");
23012
+ pulled++;
23013
+ } else {
23014
+ const localContent = readFileSync25(localPath, "utf-8");
23015
+ if (localContent.length !== content.length) {
23016
+ writeFileSync18(localPath, content, "utf-8");
23017
+ pulled++;
23018
+ }
23019
+ }
23020
+ } catch {
23021
+ }
23022
+ }
23023
+ return pulled;
23024
+ } catch (err) {
23025
+ process.stderr.write(`[cloud-sync] Code context pull failed: ${err instanceof Error ? err.message : String(err)}
23026
+ `);
23027
+ return 0;
23028
+ }
23029
+ }
22766
23030
 
22767
23031
  // src/mcp/tools/cloud-sync.ts
22768
23032
  init_config();
@@ -26921,6 +27185,22 @@ async function getExeDbReadClient() {
26921
27185
  }
26922
27186
  if (!prismaPromise3) {
26923
27187
  prismaPromise3 = (async () => {
27188
+ const readonlyUrl = process.env.MCP_READONLY_DATABASE_URL;
27189
+ if (readonlyUrl) {
27190
+ const { Pool } = await import("pg");
27191
+ const { pgSslConfig: pgSslConfig2 } = await Promise.resolve().then(() => (init_pg_ssl(), pg_ssl_exports));
27192
+ const pool = new Pool({ connectionString: readonlyUrl, ...pgSslConfig2() });
27193
+ return {
27194
+ async $queryRawUnsafe(query, ...values) {
27195
+ const result3 = await pool.query(query, values);
27196
+ return result3.rows;
27197
+ },
27198
+ async $disconnect() {
27199
+ await pool.end();
27200
+ }
27201
+ };
27202
+ }
27203
+ console.warn("[exe-db-read] WARNING: MCP_READONLY_DATABASE_URL not set \u2014 falling back to admin role");
26924
27204
  const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
26925
27205
  if (explicitPath) {
26926
27206
  const mod2 = await import(pathToFileURL5(explicitPath).href);
@@ -27881,7 +28161,10 @@ function registerSupportTools(server2) {
27881
28161
  endpoint2.searchParams.set("status", status2);
27882
28162
  endpoint2.searchParams.set("limit", String(limit));
27883
28163
  const headers = { "content-type": "application/json" };
27884
- if (licenseKey) headers["x-exe-license-key"] = licenseKey;
28164
+ if (licenseKey) {
28165
+ headers["authorization"] = `Bearer ${licenseKey}`;
28166
+ headers["x-exe-license-key"] = licenseKey;
28167
+ }
27885
28168
  if (licenseToken) headers["x-exe-license-token"] = licenseToken;
27886
28169
  try {
27887
28170
  const res = await fetch(endpoint2.toString(), { method: "GET", headers, signal: AbortSignal.timeout(15e3) });
@@ -27944,7 +28227,10 @@ function registerSupportTools(server2) {
27944
28227
  endpoint2.searchParams.set("status", status2);
27945
28228
  endpoint2.searchParams.set("limit", String(limit));
27946
28229
  const headers = { "content-type": "application/json" };
27947
- if (licenseKey) headers["x-exe-license-key"] = licenseKey;
28230
+ if (licenseKey) {
28231
+ headers["authorization"] = `Bearer ${licenseKey}`;
28232
+ headers["x-exe-license-key"] = licenseKey;
28233
+ }
27948
28234
  if (licenseToken) headers["x-exe-license-token"] = licenseToken;
27949
28235
  try {
27950
28236
  const res = await fetch(endpoint2.toString(), { method: "GET", headers, signal: AbortSignal.timeout(15e3) });
@@ -28409,7 +28695,7 @@ function runHealthCheck() {
28409
28695
  const failed = results.filter((r) => !r.pass).length;
28410
28696
  return { results, passed, failed };
28411
28697
  }
28412
- if (isMainModule(import.meta.url)) {
28698
+ if (isMainModule(import.meta.url) && (process.argv[1] ?? "").includes("exe-healthcheck")) {
28413
28699
  const { results, passed, failed } = runHealthCheck();
28414
28700
  console.log("\n exe-os Health Check\n");
28415
28701
  for (const r of results) {
@@ -29610,6 +29896,231 @@ function registerTriageFeatureRequest(server2) {
29610
29896
  );
29611
29897
  }
29612
29898
 
29899
+ // src/mcp/tools/decision.ts
29900
+ import { z as z95 } from "zod";
29901
+ function buildHandlers6() {
29902
+ const tools = /* @__PURE__ */ new Map();
29903
+ const fake = {
29904
+ registerTool(name, _config, handler) {
29905
+ tools.set(name, handler);
29906
+ }
29907
+ };
29908
+ registerStoreDecision(fake);
29909
+ registerGetDecision(fake);
29910
+ return tools;
29911
+ }
29912
+ var ACTION_TO_TOOL3 = {
29913
+ store: "store_decision",
29914
+ get: "get_decision"
29915
+ };
29916
+ function registerDecision(server2) {
29917
+ const handlers = buildHandlers6();
29918
+ server2.registerTool(
29919
+ "decision",
29920
+ {
29921
+ title: "Decision",
29922
+ description: "Store or retrieve authoritative decisions. Actions: store (persist a decision by domain), get (look up a decision by domain/query).",
29923
+ inputSchema: {
29924
+ action: z95.enum(["store", "get"]).describe("Decision operation"),
29925
+ domain: z95.string().optional().describe("Decision domain (e.g. 'architecture', 'hiring')"),
29926
+ decision: z95.string().optional().describe("Decision text for action=store"),
29927
+ rationale: z95.string().optional().describe("Why this decision was made (action=store)"),
29928
+ query: z95.string().optional().describe("Search query for action=get"),
29929
+ limit: z95.coerce.number().optional().describe("Max results for action=get")
29930
+ }
29931
+ },
29932
+ async (input, extra) => {
29933
+ const action = input.action;
29934
+ const toolName = ACTION_TO_TOOL3[action];
29935
+ if (!toolName) return { content: [{ type: "text", text: `Unknown decision action: ${action}` }], isError: true };
29936
+ const handler = handlers.get(toolName);
29937
+ if (!handler) return { content: [{ type: "text", text: `Handler not found: ${toolName}` }], isError: true };
29938
+ const { action: _, ...args } = input;
29939
+ return handler(args, extra);
29940
+ }
29941
+ );
29942
+ }
29943
+
29944
+ // src/mcp/tools/session.ts
29945
+ import { z as z96 } from "zod";
29946
+ function buildHandlers7() {
29947
+ const tools = /* @__PURE__ */ new Map();
29948
+ const fake = {
29949
+ registerTool(name, _config, handler) {
29950
+ tools.set(name, handler);
29951
+ }
29952
+ };
29953
+ registerGetSessionEvents(fake);
29954
+ registerGetLastAssistantResponse(fake);
29955
+ return tools;
29956
+ }
29957
+ var ACTION_TO_TOOL4 = {
29958
+ events: "get_session_events",
29959
+ last_response: "get_last_assistant_response"
29960
+ };
29961
+ function registerSession2(server2) {
29962
+ const handlers = buildHandlers7();
29963
+ server2.registerTool(
29964
+ "session",
29965
+ {
29966
+ title: "Session",
29967
+ description: "Session inspection tools. Actions: events (get session event log), last_response (get the most recent assistant response text).",
29968
+ inputSchema: {
29969
+ action: z96.enum(["events", "last_response"]).describe("Session operation"),
29970
+ session_id: z96.string().optional().describe("Session ID for action=events"),
29971
+ limit: z96.coerce.number().optional().describe("Max events for action=events")
29972
+ }
29973
+ },
29974
+ async (input, extra) => {
29975
+ const action = input.action;
29976
+ const toolName = ACTION_TO_TOOL4[action];
29977
+ if (!toolName) return { content: [{ type: "text", text: `Unknown session action: ${action}` }], isError: true };
29978
+ const handler = handlers.get(toolName);
29979
+ if (!handler) return { content: [{ type: "text", text: `Handler not found: ${toolName}` }], isError: true };
29980
+ const { action: _, ...args } = input;
29981
+ return handler(args, extra);
29982
+ }
29983
+ );
29984
+ }
29985
+
29986
+ // src/mcp/tools/support-consolidated.ts
29987
+ import { z as z97 } from "zod";
29988
+ function buildHandlers8() {
29989
+ const tools = /* @__PURE__ */ new Map();
29990
+ const fake = {
29991
+ registerTool(name, _config, handler) {
29992
+ tools.set(name, handler);
29993
+ }
29994
+ };
29995
+ registerCreateBugReport(fake);
29996
+ registerCreateFeatureRequest(fake);
29997
+ registerSupportTools(fake);
29998
+ registerListBugReports(fake);
29999
+ registerGetBugReport(fake);
30000
+ registerTriageBugReport(fake);
30001
+ registerListFeatureRequests(fake);
30002
+ registerGetFeatureRequest(fake);
30003
+ registerTriageFeatureRequest(fake);
30004
+ return tools;
30005
+ }
30006
+ var ACTION_TO_TOOL5 = {
30007
+ create_bug: "create_bug_report",
30008
+ create_feature: "create_feature_request",
30009
+ list_my_bugs: "list_my_bug_reports",
30010
+ list_my_features: "list_my_feature_requests",
30011
+ list_bugs: "list_bug_reports",
30012
+ get_bug: "get_bug_report",
30013
+ triage_bug: "triage_bug_report",
30014
+ list_features: "list_feature_requests",
30015
+ get_feature: "get_feature_request",
30016
+ triage_feature: "triage_feature_request",
30017
+ health: "support_health",
30018
+ test: "support_test"
30019
+ };
30020
+ function registerSupportConsolidated(server2) {
30021
+ const handlers = buildHandlers8();
30022
+ server2.registerTool(
30023
+ "support",
30024
+ {
30025
+ title: "Support",
30026
+ description: "Bug reports, feature requests, and support system. Actions: create_bug, create_feature, list_my_bugs, list_my_features, list_bugs, get_bug, triage_bug, list_features, get_feature, triage_feature, health, test.",
30027
+ inputSchema: {
30028
+ action: z97.enum([
30029
+ "create_bug",
30030
+ "create_feature",
30031
+ "list_my_bugs",
30032
+ "list_my_features",
30033
+ "list_bugs",
30034
+ "get_bug",
30035
+ "triage_bug",
30036
+ "list_features",
30037
+ "get_feature",
30038
+ "triage_feature",
30039
+ "health",
30040
+ "test"
30041
+ ]).describe("Support operation"),
30042
+ title: z97.string().optional().describe("Bug/feature title"),
30043
+ description: z97.string().optional().describe("Bug/feature description"),
30044
+ steps_to_reproduce: z97.string().optional().describe("Steps to reproduce (bugs)"),
30045
+ expected_behavior: z97.string().optional().describe("Expected behavior (bugs)"),
30046
+ actual_behavior: z97.string().optional().describe("Actual behavior (bugs)"),
30047
+ severity: z97.string().optional().describe("Severity: p0/p1/p2/p3"),
30048
+ use_case: z97.string().optional().describe("Use case (features)"),
30049
+ proposed_solution: z97.string().optional().describe("Proposed solution (features)"),
30050
+ priority: z97.string().optional().describe("Priority (features)"),
30051
+ id: z97.string().optional().describe("Bug/feature ID for get/triage"),
30052
+ status: z97.string().optional().describe("Filter by status"),
30053
+ classification: z97.string().optional().describe("Triage classification"),
30054
+ resolution: z97.string().optional().describe("Triage resolution"),
30055
+ notes: z97.string().optional().describe("Triage notes (mapped to triage_notes for bug/feature triage)"),
30056
+ triage_notes: z97.string().optional().describe("Triage notes (legacy field name)"),
30057
+ linked_task_id: z97.string().optional().describe("Linked task ID for triage"),
30058
+ linked_commit: z97.string().optional().describe("Linked commit hash for triage"),
30059
+ fixed_version: z97.string().optional().describe("Version that fixes this bug"),
30060
+ limit: z97.coerce.number().optional().describe("Max results")
30061
+ }
30062
+ },
30063
+ async (input, extra) => {
30064
+ const action = input.action;
30065
+ const toolName = ACTION_TO_TOOL5[action];
30066
+ if (!toolName) return { content: [{ type: "text", text: `Unknown support action: ${action}` }], isError: true };
30067
+ const handler = handlers.get(toolName);
30068
+ if (!handler) return { content: [{ type: "text", text: `Handler not found: ${toolName}` }], isError: true };
30069
+ const { action: _, ...args } = input;
30070
+ if ((action === "triage_bug" || action === "triage_feature") && args.notes && !args.triage_notes) {
30071
+ args.triage_notes = args.notes;
30072
+ delete args.notes;
30073
+ }
30074
+ return handler(args, extra);
30075
+ }
30076
+ );
30077
+ }
30078
+
30079
+ // src/mcp/tools/diagnostics.ts
30080
+ import { z as z98 } from "zod";
30081
+ function buildHandlers9() {
30082
+ const tools = /* @__PURE__ */ new Map();
30083
+ const fake = {
30084
+ registerTool(name, _config, handler) {
30085
+ tools.set(name, handler);
30086
+ }
30087
+ };
30088
+ registerCliParityTools(fake);
30089
+ return tools;
30090
+ }
30091
+ function registerDiagnostics(server2) {
30092
+ const handlers = buildHandlers9();
30093
+ const toolNames = Array.from(handlers.keys());
30094
+ void (toolNames.length > 0 ? toolNames : ["healthcheck"]);
30095
+ server2.registerTool(
30096
+ "diagnostics",
30097
+ {
30098
+ title: "Diagnostics",
30099
+ description: `System diagnostics and admin operations. Actions: ${toolNames.join(", ")}. Covers health checks, update status, cloud status, key management, and employee rename.`,
30100
+ inputSchema: {
30101
+ action: z98.string().describe(`Diagnostic operation: ${toolNames.join(", ")}`),
30102
+ // Pass-through params used by various sub-tools
30103
+ name: z98.string().optional().describe("Employee name (rename_employee)"),
30104
+ new_name: z98.string().optional().describe("New employee name (rename_employee)"),
30105
+ query: z98.string().optional().describe("Search/filter query"),
30106
+ format: z98.string().optional().describe("Output format")
30107
+ }
30108
+ },
30109
+ async (input, extra) => {
30110
+ const action = input.action;
30111
+ const handler = handlers.get(action);
30112
+ if (!handler) {
30113
+ return {
30114
+ content: [{ type: "text", text: `Unknown diagnostics action: ${action}. Available: ${toolNames.join(", ")}` }],
30115
+ isError: true
30116
+ };
30117
+ }
30118
+ const { action: _, ...args } = input;
30119
+ return handler(args, extra);
30120
+ }
30121
+ );
30122
+ }
30123
+
29613
30124
  // src/mcp/tool-gates.ts
29614
30125
  var TOOL_GATES = {
29615
30126
  core: [],
@@ -29665,6 +30176,11 @@ var TOOL_CATEGORIES = {
29665
30176
  registerGetSessionEvents: "core",
29666
30177
  registerGetLastAssistantResponse: "core",
29667
30178
  registerCodeContext: "graph-read",
30179
+ // consolidated tools
30180
+ registerDecision: "core",
30181
+ registerSession: "core",
30182
+ registerSupportConsolidated: "core",
30183
+ registerDiagnostics: "admin",
29668
30184
  registerListBugReports: "admin",
29669
30185
  registerGetBugReport: "admin",
29670
30186
  registerTriageBugReport: "admin",
@@ -29758,6 +30274,36 @@ var TOOL_CATEGORIES = {
29758
30274
  registerListGlobalProcedures: "orchestration",
29759
30275
  registerDeactivateGlobalProcedure: "orchestration"
29760
30276
  };
30277
+ var PLAN_TIERS = {
30278
+ free: 0,
30279
+ pro: 1,
30280
+ team: 2,
30281
+ agency: 3,
30282
+ enterprise: 4
30283
+ };
30284
+ var PLAN_GATES = {
30285
+ core: "free",
30286
+ tasks: "free",
30287
+ comms: "free",
30288
+ reminders: "free",
30289
+ "graph-read": "free",
30290
+ licensing: "free",
30291
+ // always allow checking/managing own license
30292
+ "graph-write": "pro",
30293
+ wiki: "pro",
30294
+ crm: "pro",
30295
+ "raw-data": "pro",
30296
+ documents: "pro",
30297
+ gateway: "pro",
30298
+ admin: "pro",
30299
+ orchestration: "pro"
30300
+ };
30301
+ function isToolAllowedForPlan(registerFnName, plan) {
30302
+ const category = TOOL_CATEGORIES[registerFnName];
30303
+ if (!category) return true;
30304
+ const requiredPlan = PLAN_GATES[category] ?? "free";
30305
+ return PLAN_TIERS[plan] >= PLAN_TIERS[requiredPlan];
30306
+ }
29761
30307
  var CHIEF_OF_STAFF_READ_TOOLS = /* @__PURE__ */ new Set([
29762
30308
  "registerRecallMyMemory",
29763
30309
  "registerAskTeamMemory",
@@ -29793,9 +30339,17 @@ function isToolAllowed(registerFnName) {
29793
30339
  }
29794
30340
 
29795
30341
  // src/mcp/register-tools.ts
29796
- function registerAllTools(server2) {
30342
+ var ALWAYS_ALLOWED = /* @__PURE__ */ new Set([
30343
+ "registerMcpPing",
30344
+ "registerGetLicenseStatus",
30345
+ "registerActivateLicense"
30346
+ ]);
30347
+ function registerAllTools(server2, license, hasKey) {
29797
30348
  const gate = (name, fn) => {
29798
- if (isToolAllowed(name)) fn(server2);
30349
+ if (!isToolAllowed(name)) return;
30350
+ if (hasKey === false && !ALWAYS_ALLOWED.has(name)) return;
30351
+ if (license && !isToolAllowedForPlan(name, license.plan)) return;
30352
+ fn(server2);
29799
30353
  };
29800
30354
  const mcpToolSurface = process.env.EXE_MCP_TOOL_SURFACE ?? "legacy";
29801
30355
  const exposeConsolidatedTasks = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
@@ -29816,6 +30370,14 @@ function registerAllTools(server2) {
29816
30370
  const exposeLegacyGraph = mcpToolSurface !== "consolidated";
29817
30371
  const exposeConsolidatedConfig = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
29818
30372
  const exposeLegacyConfig = mcpToolSurface !== "consolidated";
30373
+ const exposeConsolidatedDecisions = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
30374
+ const exposeLegacyDecisions = mcpToolSurface !== "consolidated";
30375
+ const exposeConsolidatedSession = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
30376
+ const exposeLegacySession = mcpToolSurface !== "consolidated";
30377
+ const exposeConsolidatedSupport = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
30378
+ const exposeLegacySupport = mcpToolSurface !== "consolidated";
30379
+ const exposeConsolidatedDiagnostics = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
30380
+ const exposeLegacyDiagnostics = mcpToolSurface !== "consolidated";
29819
30381
  if (exposeConsolidatedMemory) {
29820
30382
  gate("registerMemory", registerMemory);
29821
30383
  }
@@ -29929,23 +30491,43 @@ function registerAllTools(server2) {
29929
30491
  if (exposeLegacyMemory) {
29930
30492
  gate("registerSearchEverything", registerSearchEverything);
29931
30493
  }
29932
- gate("registerStoreDecision", registerStoreDecision);
29933
- gate("registerGetDecision", registerGetDecision);
29934
- gate("registerCreateBugReport", registerCreateBugReport);
29935
- gate("registerCreateFeatureRequest", registerCreateFeatureRequest);
29936
- gate("registerSupportTools", registerSupportTools);
29937
- gate("registerCliParityTools", registerCliParityTools);
29938
- gate("registerGetSessionEvents", registerGetSessionEvents);
29939
- gate("registerGetLastAssistantResponse", registerGetLastAssistantResponse);
29940
- gate("registerCodeContext", registerCodeContext);
29941
- if (process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN) {
29942
- gate("registerListBugReports", registerListBugReports);
29943
- gate("registerGetBugReport", registerGetBugReport);
29944
- gate("registerTriageBugReport", registerTriageBugReport);
29945
- gate("registerListFeatureRequests", registerListFeatureRequests);
29946
- gate("registerGetFeatureRequest", registerGetFeatureRequest);
29947
- gate("registerTriageFeatureRequest", registerTriageFeatureRequest);
30494
+ if (exposeConsolidatedDecisions) {
30495
+ gate("registerDecision", registerDecision);
30496
+ }
30497
+ if (exposeLegacyDecisions) {
30498
+ gate("registerStoreDecision", registerStoreDecision);
30499
+ gate("registerGetDecision", registerGetDecision);
30500
+ }
30501
+ if (exposeConsolidatedSupport) {
30502
+ gate("registerSupportConsolidated", registerSupportConsolidated);
30503
+ }
30504
+ if (exposeLegacySupport) {
30505
+ gate("registerCreateBugReport", registerCreateBugReport);
30506
+ gate("registerCreateFeatureRequest", registerCreateFeatureRequest);
30507
+ gate("registerSupportTools", registerSupportTools);
30508
+ if (process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN) {
30509
+ gate("registerListBugReports", registerListBugReports);
30510
+ gate("registerGetBugReport", registerGetBugReport);
30511
+ gate("registerTriageBugReport", registerTriageBugReport);
30512
+ gate("registerListFeatureRequests", registerListFeatureRequests);
30513
+ gate("registerGetFeatureRequest", registerGetFeatureRequest);
30514
+ gate("registerTriageFeatureRequest", registerTriageFeatureRequest);
30515
+ }
30516
+ }
30517
+ if (exposeConsolidatedDiagnostics) {
30518
+ gate("registerDiagnostics", registerDiagnostics);
30519
+ }
30520
+ if (exposeLegacyDiagnostics) {
30521
+ gate("registerCliParityTools", registerCliParityTools);
29948
30522
  }
30523
+ if (exposeConsolidatedSession) {
30524
+ gate("registerSession", registerSession2);
30525
+ }
30526
+ if (exposeLegacySession) {
30527
+ gate("registerGetSessionEvents", registerGetSessionEvents);
30528
+ gate("registerGetLastAssistantResponse", registerGetLastAssistantResponse);
30529
+ }
30530
+ gate("registerCodeContext", registerCodeContext);
29949
30531
  if (exposeLegacyConfig) {
29950
30532
  gate("registerGetAgentSpend", registerGetAgentSpend);
29951
30533
  }
@@ -30060,9 +30642,13 @@ var _backfillTimer = null;
30060
30642
  var _ppidWatchdog = null;
30061
30643
  var _shuttingDown = false;
30062
30644
  instrumentServer(server);
30063
- registerAllTools(server);
30645
+ var wrappedServer = wrapServerWithTelemetry(server);
30646
+ var _licenseGate = await initLicenseGate();
30647
+ registerAllTools(wrappedServer, _licenseGate.license, _licenseGate.hasKey);
30648
+ startToolTelemetryFlush();
30064
30649
  var _gatedRole = process.env.AGENT_ROLE ?? "standalone";
30065
- process.stderr.write(`[exe-os] Tool gating: role=${_gatedRole}
30650
+ var _gatedPlan = _licenseGate.license?.plan ?? (_licenseGate.hasKey ? "validating" : "no-key");
30651
+ process.stderr.write(`[exe-os] Tool gating: role=${_gatedRole} plan=${_gatedPlan}
30066
30652
  `);
30067
30653
  try {
30068
30654
  await initStore();