@askexenow/exe-os 0.9.87 → 0.9.88

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 (101) 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 +749 -45
  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/mcp/server.js +613 -27
  91. package/dist/mcp/tools/complete-reminder.js +1 -0
  92. package/dist/mcp/tools/create-reminder.js +1 -0
  93. package/dist/mcp/tools/create-task.js +2 -1
  94. package/dist/mcp/tools/deactivate-behavior.js +1 -0
  95. package/dist/mcp/tools/list-reminders.js +1 -0
  96. package/dist/mcp/tools/list-tasks.js +1 -0
  97. package/dist/mcp/tools/send-message.js +2 -1
  98. package/dist/mcp/tools/update-task.js +2 -1
  99. package/dist/runtime/index.js +12 -1
  100. package/dist/tui/App.js +12 -1
  101. package/package.json +2 -2
@@ -2112,6 +2112,7 @@ __export(database_exports, {
2112
2112
  isInitialized: () => isInitialized,
2113
2113
  setExternalClient: () => setExternalClient
2114
2114
  });
2115
+ import { chmodSync as chmodSync2 } from "fs";
2115
2116
  import { createClient } from "@libsql/client";
2116
2117
  async function initDatabase(config2) {
2117
2118
  if (_walCheckpointTimer) {
@@ -2153,6 +2154,16 @@ async function initDatabase(config2) {
2153
2154
  if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
2154
2155
  _adapterClient = await createPrismaDbAdapter(_resilientClient);
2155
2156
  }
2157
+ try {
2158
+ chmodSync2(config2.dbPath, 384);
2159
+ for (const suffix of ["-wal", "-shm"]) {
2160
+ try {
2161
+ chmodSync2(config2.dbPath + suffix, 384);
2162
+ } catch {
2163
+ }
2164
+ }
2165
+ } catch {
2166
+ }
2156
2167
  }
2157
2168
  function isInitialized() {
2158
2169
  return _adapterClient !== null || _client !== null;
@@ -4135,6 +4146,21 @@ var init_agentic_ontology = __esm({
4135
4146
  }
4136
4147
  });
4137
4148
 
4149
+ // src/lib/pg-ssl.ts
4150
+ var pg_ssl_exports = {};
4151
+ __export(pg_ssl_exports, {
4152
+ pgSslConfig: () => pgSslConfig
4153
+ });
4154
+ function pgSslConfig() {
4155
+ if (process.env.EXE_DB_SSL_DISABLED === "true") return {};
4156
+ return { ssl: { rejectUnauthorized: process.env.EXE_DB_SSL_ALLOW_SELFSIGNED !== "true" } };
4157
+ }
4158
+ var init_pg_ssl = __esm({
4159
+ "src/lib/pg-ssl.ts"() {
4160
+ "use strict";
4161
+ }
4162
+ });
4163
+
4138
4164
  // src/lib/projection-worker.ts
4139
4165
  var projection_worker_exports = {};
4140
4166
  __export(projection_worker_exports, {
@@ -4173,7 +4199,8 @@ function loadPrisma() {
4173
4199
  return new Ctor();
4174
4200
  }
4175
4201
  const { Pool } = await import("pg");
4176
- const pool = new Pool({ connectionString: process.env.DATABASE_URL });
4202
+ const { pgSslConfig: pgSslConfig2 } = await Promise.resolve().then(() => (init_pg_ssl(), pg_ssl_exports));
4203
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL, ...pgSslConfig2() });
4177
4204
  return {
4178
4205
  async $queryRawUnsafe(query, ...values) {
4179
4206
  const result3 = await pool.query(query, values);
@@ -11631,7 +11658,7 @@ function readQueue() {
11631
11658
  function writeQueue(queue) {
11632
11659
  ensureDir();
11633
11660
  const tmp = `${QUEUE_PATH2}.tmp`;
11634
- writeFileSync11(tmp, JSON.stringify(queue, null, 2));
11661
+ writeFileSync11(tmp, JSON.stringify(queue, null, 2), { mode: 384 });
11635
11662
  renameSync5(tmp, QUEUE_PATH2);
11636
11663
  }
11637
11664
  function queueIntercom(targetSession, reason) {
@@ -20832,6 +20859,166 @@ var init_get_daemon_health = __esm({
20832
20859
  }
20833
20860
  });
20834
20861
 
20862
+ // src/mcp/license-gate.ts
20863
+ var license_gate_exports = {};
20864
+ __export(license_gate_exports, {
20865
+ NO_LICENSE_MESSAGE: () => NO_LICENSE_MESSAGE,
20866
+ getCachedLicenseGate: () => getCachedLicenseGate,
20867
+ initLicenseGate: () => initLicenseGate
20868
+ });
20869
+ async function initLicenseGate() {
20870
+ const key = loadLicense();
20871
+ _hasLicenseKey = key !== null && key.length > 0;
20872
+ if (_hasLicenseKey) {
20873
+ try {
20874
+ _cachedLicense = await checkLicense();
20875
+ } catch {
20876
+ _cachedLicense = null;
20877
+ }
20878
+ return { license: _cachedLicense, hasKey: true };
20879
+ }
20880
+ return { license: null, hasKey: false };
20881
+ }
20882
+ function getCachedLicenseGate() {
20883
+ return {
20884
+ license: _cachedLicense,
20885
+ hasKey: _hasLicenseKey
20886
+ };
20887
+ }
20888
+ var _cachedLicense, _hasLicenseKey, NO_LICENSE_MESSAGE;
20889
+ var init_license_gate = __esm({
20890
+ "src/mcp/license-gate.ts"() {
20891
+ "use strict";
20892
+ init_license();
20893
+ _cachedLicense = null;
20894
+ _hasLicenseKey = false;
20895
+ NO_LICENSE_MESSAGE = "License key required. Get your key from the Exe OS team, then:\n\n echo 'exe_sk_YOUR_KEY' > ~/.exe-os/license.key\n\nThen run /mcp to reconnect, or restart your session.";
20896
+ }
20897
+ });
20898
+
20899
+ // src/mcp/tool-telemetry.ts
20900
+ var tool_telemetry_exports = {};
20901
+ __export(tool_telemetry_exports, {
20902
+ getToolUsageStats: () => getToolUsageStats,
20903
+ resetToolUsageStats: () => resetToolUsageStats,
20904
+ startToolTelemetryFlush: () => startToolTelemetryFlush,
20905
+ stopToolTelemetryFlush: () => stopToolTelemetryFlush,
20906
+ wrapServerWithTelemetry: () => wrapServerWithTelemetry
20907
+ });
20908
+ function recordCall(toolName, action, isError) {
20909
+ let record = _toolCalls.get(toolName);
20910
+ if (!record) {
20911
+ record = { count: 0, actions: /* @__PURE__ */ new Map(), lastCalledAt: 0, errors: 0 };
20912
+ _toolCalls.set(toolName, record);
20913
+ }
20914
+ record.count++;
20915
+ record.lastCalledAt = Date.now();
20916
+ if (isError) record.errors++;
20917
+ if (action) {
20918
+ record.actions.set(action, (record.actions.get(action) ?? 0) + 1);
20919
+ }
20920
+ }
20921
+ function wrapServerWithTelemetry(server) {
20922
+ const originalRegisterTool = server.registerTool.bind(server);
20923
+ server.registerTool = (name, config2, handler) => {
20924
+ const wrappedHandler = async (input, extra) => {
20925
+ const action = input.action;
20926
+ try {
20927
+ const result3 = await handler(input, extra);
20928
+ const isError = result3?.isError === true;
20929
+ recordCall(name, action, isError);
20930
+ return result3;
20931
+ } catch (err) {
20932
+ recordCall(name, action, true);
20933
+ throw err;
20934
+ }
20935
+ };
20936
+ return originalRegisterTool(name, config2, wrappedHandler);
20937
+ };
20938
+ return server;
20939
+ }
20940
+ function getToolUsageStats() {
20941
+ let totalCalls = 0;
20942
+ let totalErrors = 0;
20943
+ const tools = {};
20944
+ for (const [name, record] of _toolCalls) {
20945
+ totalCalls += record.count;
20946
+ totalErrors += record.errors;
20947
+ const entry = {
20948
+ calls: record.count,
20949
+ errors: record.errors,
20950
+ lastCalledAt: new Date(record.lastCalledAt).toISOString()
20951
+ };
20952
+ if (record.actions.size > 0) {
20953
+ entry.actions = Object.fromEntries(record.actions);
20954
+ }
20955
+ tools[name] = entry;
20956
+ }
20957
+ return {
20958
+ sessionStartedAt: new Date(_sessionStartedAt).toISOString(),
20959
+ uptimeMs: Date.now() - _sessionStartedAt,
20960
+ totalCalls,
20961
+ totalErrors,
20962
+ tools
20963
+ };
20964
+ }
20965
+ function resetToolUsageStats() {
20966
+ _toolCalls.clear();
20967
+ _sessionStartedAt = Date.now();
20968
+ }
20969
+ async function flushToMemory() {
20970
+ const stats = getToolUsageStats();
20971
+ if (stats.totalCalls === 0) return;
20972
+ if (stats.totalCalls === _lastFlushedAt) return;
20973
+ try {
20974
+ const { getClient: getClient2, isInitialized: isInitialized2 } = await Promise.resolve().then(() => (init_database(), database_exports));
20975
+ if (!isInitialized2()) return;
20976
+ const client = getClient2();
20977
+ const toolSummary = Object.entries(stats.tools).sort((a, b) => b[1].calls - a[1].calls).map(([name, t]) => {
20978
+ const actionStr = t.actions ? ` (${Object.entries(t.actions).sort((a, b) => b[1] - a[1]).map(([a, c]) => `${a}:${c}`).join(", ")})` : "";
20979
+ return `${name}: ${t.calls} calls${t.errors > 0 ? ` (${t.errors} errors)` : ""}${actionStr}`;
20980
+ }).join("\n");
20981
+ const raw_text = `Tool usage since ${stats.sessionStartedAt} (${Math.round(stats.uptimeMs / 6e4)}min):
20982
+ Total: ${stats.totalCalls} calls, ${stats.totalErrors} errors
20983
+
20984
+ ${toolSummary}`;
20985
+ await client.execute({
20986
+ sql: `INSERT INTO memories (id, agent_id, raw_text, memory_type, project_name, importance, created_at, updated_at)
20987
+ VALUES (?, 'system', ?, 'telemetry', 'exe-os', 2, datetime('now'), datetime('now'))`,
20988
+ args: [
20989
+ `telemetry-tools-${Date.now()}`,
20990
+ raw_text
20991
+ ]
20992
+ });
20993
+ _lastFlushedAt = stats.totalCalls;
20994
+ } catch {
20995
+ }
20996
+ }
20997
+ function startToolTelemetryFlush() {
20998
+ if (_flushTimer3) return;
20999
+ _sessionStartedAt = Date.now();
21000
+ _flushTimer3 = setInterval(() => void flushToMemory(), FLUSH_INTERVAL_MS2);
21001
+ _flushTimer3.unref();
21002
+ }
21003
+ async function stopToolTelemetryFlush() {
21004
+ if (_flushTimer3) {
21005
+ clearInterval(_flushTimer3);
21006
+ _flushTimer3 = null;
21007
+ }
21008
+ await flushToMemory();
21009
+ }
21010
+ var _toolCalls, _sessionStartedAt, _flushTimer3, FLUSH_INTERVAL_MS2, _lastFlushedAt;
21011
+ var init_tool_telemetry = __esm({
21012
+ "src/mcp/tool-telemetry.ts"() {
21013
+ "use strict";
21014
+ _toolCalls = /* @__PURE__ */ new Map();
21015
+ _sessionStartedAt = Date.now();
21016
+ _flushTimer3 = null;
21017
+ FLUSH_INTERVAL_MS2 = 5 * 60 * 1e3;
21018
+ _lastFlushedAt = 0;
21019
+ }
21020
+ });
21021
+
20835
21022
  // src/mcp/tools/mcp-ping.ts
20836
21023
  import { existsSync as existsSync27, readFileSync as readFileSync22 } from "fs";
20837
21024
  import path33 from "path";
@@ -20862,6 +21049,7 @@ function registerMcpPing(server) {
20862
21049
  const daemon = isDaemonAlive2();
20863
21050
  const summary = summarizeMcpTransport();
20864
21051
  writeMcpTransportSummary();
21052
+ const gate = getCachedLicenseGate();
20865
21053
  const status2 = daemon.alive ? "ok" : "degraded";
20866
21054
  return {
20867
21055
  content: [
@@ -20870,8 +21058,14 @@ function registerMcpPing(server) {
20870
21058
  text: JSON.stringify({
20871
21059
  status: status2,
20872
21060
  daemon,
21061
+ licenseGate: {
21062
+ hasKey: gate.hasKey,
21063
+ plan: gate.license?.plan ?? "none",
21064
+ valid: gate.license?.valid ?? false
21065
+ },
21066
+ toolUsage: getToolUsageStats(),
20873
21067
  mcpTransport: summary,
20874
- 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."
21068
+ 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."
20875
21069
  }, null, 2)
20876
21070
  }
20877
21071
  ]
@@ -20884,6 +21078,8 @@ var init_mcp_ping = __esm({
20884
21078
  "src/mcp/tools/mcp-ping.ts"() {
20885
21079
  "use strict";
20886
21080
  init_mcp_transport_health();
21081
+ init_license_gate();
21082
+ init_tool_telemetry();
20887
21083
  PID_PATH3 = path33.join(homedir4(), ".exe-os", "exed.pid");
20888
21084
  }
20889
21085
  });
@@ -22171,6 +22367,7 @@ import { fileURLToPath as fileURLToPath4 } from "url";
22171
22367
  function isMainModule(importMetaUrl) {
22172
22368
  if (process.argv[1] == null) return false;
22173
22369
  if (process.argv[1].includes("mcp/server")) return false;
22370
+ if (process.argv[1].includes("exe-daemon")) return false;
22174
22371
  try {
22175
22372
  const scriptPath = realpathSync(process.argv[1]);
22176
22373
  const modulePath = realpathSync(fileURLToPath4(importMetaUrl));
@@ -24005,6 +24202,7 @@ __export(cloud_sync_exports, {
24005
24202
  cloudPull: () => cloudPull,
24006
24203
  cloudPullBehaviors: () => cloudPullBehaviors,
24007
24204
  cloudPullBlob: () => cloudPullBlob,
24205
+ cloudPullCodeContext: () => cloudPullCodeContext,
24008
24206
  cloudPullConversations: () => cloudPullConversations,
24009
24207
  cloudPullDocuments: () => cloudPullDocuments,
24010
24208
  cloudPullGlobalProcedures: () => cloudPullGlobalProcedures,
@@ -24014,6 +24212,7 @@ __export(cloud_sync_exports, {
24014
24212
  cloudPush: () => cloudPush,
24015
24213
  cloudPushBehaviors: () => cloudPushBehaviors,
24016
24214
  cloudPushBlob: () => cloudPushBlob,
24215
+ cloudPushCodeContext: () => cloudPushCodeContext,
24017
24216
  cloudPushConversations: () => cloudPushConversations,
24018
24217
  cloudPushDocuments: () => cloudPushDocuments,
24019
24218
  cloudPushGlobalProcedures: () => cloudPushGlobalProcedures,
@@ -24092,7 +24291,8 @@ function loadPgClient() {
24092
24291
  return new Ctor();
24093
24292
  }
24094
24293
  const { Pool } = await import("pg");
24095
- const pool = new Pool({ connectionString: process.env.DATABASE_URL });
24294
+ const { pgSslConfig: pgSslConfig2 } = await Promise.resolve().then(() => (init_pg_ssl(), pg_ssl_exports));
24295
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL, ...pgSslConfig2() });
24096
24296
  return {
24097
24297
  async $queryRawUnsafe(query, ...values) {
24098
24298
  const result3 = await pool.query(query, values);
@@ -24637,6 +24837,17 @@ async function cloudSync(config2) {
24637
24837
  } catch (err) {
24638
24838
  logError(`[cloud-sync] DB backup upload error: ${err instanceof Error ? err.message : String(err)}`);
24639
24839
  }
24840
+ let codeContextResult = { pushed: 0, pulled: 0 };
24841
+ try {
24842
+ codeContextResult.pushed = await cloudPushCodeContext(config2);
24843
+ } catch (err) {
24844
+ logError(`[cloud-sync] Code context push: ${err instanceof Error ? err.message : String(err)}`);
24845
+ }
24846
+ try {
24847
+ codeContextResult.pulled = await cloudPullCodeContext(config2);
24848
+ } catch (err) {
24849
+ logError(`[cloud-sync] Code context pull: ${err instanceof Error ? err.message : String(err)}`);
24850
+ }
24640
24851
  return {
24641
24852
  pushed,
24642
24853
  pulled,
@@ -24646,7 +24857,8 @@ async function cloudSync(config2) {
24646
24857
  tasks: tasksResult,
24647
24858
  conversations: conversationsResult,
24648
24859
  documents: documentsResult,
24649
- roster: rosterResult
24860
+ roster: rosterResult,
24861
+ codeContext: codeContextResult
24650
24862
  };
24651
24863
  }
24652
24864
  function recordRosterDeletion(name) {
@@ -25284,7 +25496,99 @@ async function cloudPullDocuments(config2) {
25284
25496
  }
25285
25497
  return { pulled };
25286
25498
  }
25287
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS2, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_REUPLOAD_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
25499
+ async function cloudPushCodeContext(config2) {
25500
+ assertSecureEndpoint(config2.endpoint);
25501
+ if (!existsSync36(CODE_CONTEXT_DIR)) return 0;
25502
+ const files = readdirSync11(CODE_CONTEXT_DIR).filter(
25503
+ (f) => f.endsWith(".json") && !f.endsWith(".vectors.json") && !f.startsWith(".")
25504
+ );
25505
+ if (files.length === 0) return 0;
25506
+ const metaPath = path41.join(CODE_CONTEXT_DIR, ".sync-meta.json");
25507
+ let syncMeta = {};
25508
+ if (existsSync36(metaPath)) {
25509
+ try {
25510
+ syncMeta = JSON.parse(readFileSync28(metaPath, "utf-8"));
25511
+ } catch {
25512
+ }
25513
+ }
25514
+ let pushed = 0;
25515
+ for (const file of files) {
25516
+ const filePath = path41.join(CODE_CONTEXT_DIR, file);
25517
+ try {
25518
+ const stat = statSync8(filePath);
25519
+ const lastPushed = syncMeta[file] ?? 0;
25520
+ if (stat.mtimeMs <= lastPushed) continue;
25521
+ const content = readFileSync28(filePath, "utf-8");
25522
+ const header = content.substring(0, 300);
25523
+ if (header.includes("/tmp") || header.includes("/var/folders") || header.includes(".worktrees/")) continue;
25524
+ const compressed = compress(Buffer.from(content, "utf8"));
25525
+ const encrypted = encryptSyncBlob(compressed);
25526
+ const resp = await fetchWithRetry(`${config2.endpoint}/sync/push-code-context`, {
25527
+ method: "POST",
25528
+ headers: {
25529
+ Authorization: `Bearer ${config2.apiKey}`,
25530
+ "Content-Type": "application/json",
25531
+ "X-Device-Id": loadDeviceId()
25532
+ },
25533
+ body: JSON.stringify({ key: file, blob: encrypted })
25534
+ });
25535
+ if (resp.ok) {
25536
+ syncMeta[file] = stat.mtimeMs;
25537
+ pushed++;
25538
+ }
25539
+ } catch {
25540
+ }
25541
+ }
25542
+ if (pushed > 0) {
25543
+ try {
25544
+ writeFileSync19(metaPath, JSON.stringify(syncMeta));
25545
+ } catch {
25546
+ }
25547
+ }
25548
+ return pushed;
25549
+ }
25550
+ async function cloudPullCodeContext(config2) {
25551
+ assertSecureEndpoint(config2.endpoint);
25552
+ try {
25553
+ const resp = await fetchWithRetry(`${config2.endpoint}/sync/pull-code-context`, {
25554
+ method: "GET",
25555
+ headers: {
25556
+ Authorization: `Bearer ${config2.apiKey}`,
25557
+ "X-Device-Id": loadDeviceId()
25558
+ }
25559
+ });
25560
+ if (!resp.ok) return 0;
25561
+ const data = await resp.json();
25562
+ if (!data.indexes || data.indexes.length === 0) return 0;
25563
+ mkdirSync16(CODE_CONTEXT_DIR, { recursive: true });
25564
+ let pulled = 0;
25565
+ for (const { key, blob } of data.indexes) {
25566
+ try {
25567
+ if (key.endsWith(".vectors.json")) continue;
25568
+ const localPath = path41.join(CODE_CONTEXT_DIR, key);
25569
+ const compressed = decryptSyncBlob(blob);
25570
+ const content = decompress(compressed).toString("utf8");
25571
+ if (!existsSync36(localPath)) {
25572
+ writeFileSync19(localPath, content, "utf-8");
25573
+ pulled++;
25574
+ } else {
25575
+ const localContent = readFileSync28(localPath, "utf-8");
25576
+ if (localContent.length !== content.length) {
25577
+ writeFileSync19(localPath, content, "utf-8");
25578
+ pulled++;
25579
+ }
25580
+ }
25581
+ } catch {
25582
+ }
25583
+ }
25584
+ return pulled;
25585
+ } catch (err) {
25586
+ process.stderr.write(`[cloud-sync] Code context pull failed: ${err instanceof Error ? err.message : String(err)}
25587
+ `);
25588
+ return 0;
25589
+ }
25590
+ }
25591
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS2, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_REUPLOAD_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH, CODE_CONTEXT_DIR;
25288
25592
  var init_cloud_sync = __esm({
25289
25593
  "src/lib/cloud-sync.ts"() {
25290
25594
  "use strict";
@@ -25305,6 +25609,7 @@ var init_cloud_sync = __esm({
25305
25609
  _pgFailed = false;
25306
25610
  CLOUD_REUPLOAD_REQUIRED_MESSAGE = "Cloud sync is blocked because this device rotated its memory encryption key. Run `exe-os cloud reupload` first to re-upload the cloud backup with the new key.";
25307
25611
  ROSTER_DELETIONS_PATH = path41.join(EXE_AI_DIR, "roster-deletions.json");
25612
+ CODE_CONTEXT_DIR = path41.join(EXE_AI_DIR, "code-context");
25308
25613
  }
25309
25614
  });
25310
25615
 
@@ -29700,6 +30005,22 @@ async function getExeDbReadClient() {
29700
30005
  }
29701
30006
  if (!prismaPromise4) {
29702
30007
  prismaPromise4 = (async () => {
30008
+ const readonlyUrl = process.env.MCP_READONLY_DATABASE_URL;
30009
+ if (readonlyUrl) {
30010
+ const { Pool } = await import("pg");
30011
+ const { pgSslConfig: pgSslConfig2 } = await Promise.resolve().then(() => (init_pg_ssl(), pg_ssl_exports));
30012
+ const pool = new Pool({ connectionString: readonlyUrl, ...pgSslConfig2() });
30013
+ return {
30014
+ async $queryRawUnsafe(query, ...values) {
30015
+ const result3 = await pool.query(query, values);
30016
+ return result3.rows;
30017
+ },
30018
+ async $disconnect() {
30019
+ await pool.end();
30020
+ }
30021
+ };
30022
+ }
30023
+ console.warn("[exe-db-read] WARNING: MCP_READONLY_DATABASE_URL not set \u2014 falling back to admin role");
29703
30024
  const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
29704
30025
  if (explicitPath) {
29705
30026
  const mod2 = await import(pathToFileURL6(explicitPath).href);
@@ -30702,7 +31023,10 @@ function registerSupportTools(server) {
30702
31023
  endpoint2.searchParams.set("status", status2);
30703
31024
  endpoint2.searchParams.set("limit", String(limit));
30704
31025
  const headers = { "content-type": "application/json" };
30705
- if (licenseKey) headers["x-exe-license-key"] = licenseKey;
31026
+ if (licenseKey) {
31027
+ headers["authorization"] = `Bearer ${licenseKey}`;
31028
+ headers["x-exe-license-key"] = licenseKey;
31029
+ }
30706
31030
  if (licenseToken) headers["x-exe-license-token"] = licenseToken;
30707
31031
  try {
30708
31032
  const res = await fetch(endpoint2.toString(), { method: "GET", headers, signal: AbortSignal.timeout(15e3) });
@@ -30765,7 +31089,10 @@ function registerSupportTools(server) {
30765
31089
  endpoint2.searchParams.set("status", status2);
30766
31090
  endpoint2.searchParams.set("limit", String(limit));
30767
31091
  const headers = { "content-type": "application/json" };
30768
- if (licenseKey) headers["x-exe-license-key"] = licenseKey;
31092
+ if (licenseKey) {
31093
+ headers["authorization"] = `Bearer ${licenseKey}`;
31094
+ headers["x-exe-license-key"] = licenseKey;
31095
+ }
30769
31096
  if (licenseToken) headers["x-exe-license-token"] = licenseToken;
30770
31097
  try {
30771
31098
  const res = await fetch(endpoint2.toString(), { method: "GET", headers, signal: AbortSignal.timeout(15e3) });
@@ -31243,7 +31570,7 @@ var init_exe_healthcheck = __esm({
31243
31570
  init_is_main();
31244
31571
  init_config();
31245
31572
  init_mcp_transport_health();
31246
- if (isMainModule(import.meta.url)) {
31573
+ if (isMainModule(import.meta.url) && (process.argv[1] ?? "").includes("exe-healthcheck")) {
31247
31574
  const { results, passed, failed } = runHealthCheck();
31248
31575
  console.log("\n exe-os Health Check\n");
31249
31576
  for (const r of results) {
@@ -32496,7 +32823,269 @@ var init_support_inbox = __esm({
32496
32823
  }
32497
32824
  });
32498
32825
 
32826
+ // src/mcp/tools/decision.ts
32827
+ import { z as z95 } from "zod";
32828
+ function buildHandlers6() {
32829
+ const tools = /* @__PURE__ */ new Map();
32830
+ const fake = {
32831
+ registerTool(name, _config, handler) {
32832
+ tools.set(name, handler);
32833
+ }
32834
+ };
32835
+ registerStoreDecision(fake);
32836
+ registerGetDecision(fake);
32837
+ return tools;
32838
+ }
32839
+ function registerDecision(server) {
32840
+ const handlers = buildHandlers6();
32841
+ server.registerTool(
32842
+ "decision",
32843
+ {
32844
+ title: "Decision",
32845
+ description: "Store or retrieve authoritative decisions. Actions: store (persist a decision by domain), get (look up a decision by domain/query).",
32846
+ inputSchema: {
32847
+ action: z95.enum(["store", "get"]).describe("Decision operation"),
32848
+ domain: z95.string().optional().describe("Decision domain (e.g. 'architecture', 'hiring')"),
32849
+ decision: z95.string().optional().describe("Decision text for action=store"),
32850
+ rationale: z95.string().optional().describe("Why this decision was made (action=store)"),
32851
+ query: z95.string().optional().describe("Search query for action=get"),
32852
+ limit: z95.coerce.number().optional().describe("Max results for action=get")
32853
+ }
32854
+ },
32855
+ async (input, extra) => {
32856
+ const action = input.action;
32857
+ const toolName = ACTION_TO_TOOL3[action];
32858
+ if (!toolName) return { content: [{ type: "text", text: `Unknown decision action: ${action}` }], isError: true };
32859
+ const handler = handlers.get(toolName);
32860
+ if (!handler) return { content: [{ type: "text", text: `Handler not found: ${toolName}` }], isError: true };
32861
+ const { action: _, ...args } = input;
32862
+ return handler(args, extra);
32863
+ }
32864
+ );
32865
+ }
32866
+ var ACTION_TO_TOOL3;
32867
+ var init_decision = __esm({
32868
+ "src/mcp/tools/decision.ts"() {
32869
+ "use strict";
32870
+ init_store_decision();
32871
+ init_get_decision();
32872
+ ACTION_TO_TOOL3 = {
32873
+ store: "store_decision",
32874
+ get: "get_decision"
32875
+ };
32876
+ }
32877
+ });
32878
+
32879
+ // src/mcp/tools/session.ts
32880
+ import { z as z96 } from "zod";
32881
+ function buildHandlers7() {
32882
+ const tools = /* @__PURE__ */ new Map();
32883
+ const fake = {
32884
+ registerTool(name, _config, handler) {
32885
+ tools.set(name, handler);
32886
+ }
32887
+ };
32888
+ registerGetSessionEvents(fake);
32889
+ registerGetLastAssistantResponse(fake);
32890
+ return tools;
32891
+ }
32892
+ function registerSession2(server) {
32893
+ const handlers = buildHandlers7();
32894
+ server.registerTool(
32895
+ "session",
32896
+ {
32897
+ title: "Session",
32898
+ description: "Session inspection tools. Actions: events (get session event log), last_response (get the most recent assistant response text).",
32899
+ inputSchema: {
32900
+ action: z96.enum(["events", "last_response"]).describe("Session operation"),
32901
+ session_id: z96.string().optional().describe("Session ID for action=events"),
32902
+ limit: z96.coerce.number().optional().describe("Max events for action=events")
32903
+ }
32904
+ },
32905
+ async (input, extra) => {
32906
+ const action = input.action;
32907
+ const toolName = ACTION_TO_TOOL4[action];
32908
+ if (!toolName) return { content: [{ type: "text", text: `Unknown session action: ${action}` }], isError: true };
32909
+ const handler = handlers.get(toolName);
32910
+ if (!handler) return { content: [{ type: "text", text: `Handler not found: ${toolName}` }], isError: true };
32911
+ const { action: _, ...args } = input;
32912
+ return handler(args, extra);
32913
+ }
32914
+ );
32915
+ }
32916
+ var ACTION_TO_TOOL4;
32917
+ var init_session = __esm({
32918
+ "src/mcp/tools/session.ts"() {
32919
+ "use strict";
32920
+ init_get_session_events();
32921
+ ACTION_TO_TOOL4 = {
32922
+ events: "get_session_events",
32923
+ last_response: "get_last_assistant_response"
32924
+ };
32925
+ }
32926
+ });
32927
+
32928
+ // src/mcp/tools/support-consolidated.ts
32929
+ import { z as z97 } from "zod";
32930
+ function buildHandlers8() {
32931
+ const tools = /* @__PURE__ */ new Map();
32932
+ const fake = {
32933
+ registerTool(name, _config, handler) {
32934
+ tools.set(name, handler);
32935
+ }
32936
+ };
32937
+ registerCreateBugReport(fake);
32938
+ registerCreateFeatureRequest(fake);
32939
+ registerSupportTools(fake);
32940
+ registerListBugReports(fake);
32941
+ registerGetBugReport(fake);
32942
+ registerTriageBugReport(fake);
32943
+ registerListFeatureRequests(fake);
32944
+ registerGetFeatureRequest(fake);
32945
+ registerTriageFeatureRequest(fake);
32946
+ return tools;
32947
+ }
32948
+ function registerSupportConsolidated(server) {
32949
+ const handlers = buildHandlers8();
32950
+ server.registerTool(
32951
+ "support",
32952
+ {
32953
+ title: "Support",
32954
+ 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.",
32955
+ inputSchema: {
32956
+ action: z97.enum([
32957
+ "create_bug",
32958
+ "create_feature",
32959
+ "list_my_bugs",
32960
+ "list_my_features",
32961
+ "list_bugs",
32962
+ "get_bug",
32963
+ "triage_bug",
32964
+ "list_features",
32965
+ "get_feature",
32966
+ "triage_feature",
32967
+ "health",
32968
+ "test"
32969
+ ]).describe("Support operation"),
32970
+ title: z97.string().optional().describe("Bug/feature title"),
32971
+ description: z97.string().optional().describe("Bug/feature description"),
32972
+ steps_to_reproduce: z97.string().optional().describe("Steps to reproduce (bugs)"),
32973
+ expected_behavior: z97.string().optional().describe("Expected behavior (bugs)"),
32974
+ actual_behavior: z97.string().optional().describe("Actual behavior (bugs)"),
32975
+ severity: z97.string().optional().describe("Severity: p0/p1/p2/p3"),
32976
+ use_case: z97.string().optional().describe("Use case (features)"),
32977
+ proposed_solution: z97.string().optional().describe("Proposed solution (features)"),
32978
+ priority: z97.string().optional().describe("Priority (features)"),
32979
+ id: z97.string().optional().describe("Bug/feature ID for get/triage"),
32980
+ status: z97.string().optional().describe("Filter by status"),
32981
+ classification: z97.string().optional().describe("Triage classification"),
32982
+ resolution: z97.string().optional().describe("Triage resolution"),
32983
+ notes: z97.string().optional().describe("Triage notes (mapped to triage_notes for bug/feature triage)"),
32984
+ triage_notes: z97.string().optional().describe("Triage notes (legacy field name)"),
32985
+ linked_task_id: z97.string().optional().describe("Linked task ID for triage"),
32986
+ linked_commit: z97.string().optional().describe("Linked commit hash for triage"),
32987
+ fixed_version: z97.string().optional().describe("Version that fixes this bug"),
32988
+ limit: z97.coerce.number().optional().describe("Max results")
32989
+ }
32990
+ },
32991
+ async (input, extra) => {
32992
+ const action = input.action;
32993
+ const toolName = ACTION_TO_TOOL5[action];
32994
+ if (!toolName) return { content: [{ type: "text", text: `Unknown support action: ${action}` }], isError: true };
32995
+ const handler = handlers.get(toolName);
32996
+ if (!handler) return { content: [{ type: "text", text: `Handler not found: ${toolName}` }], isError: true };
32997
+ const { action: _, ...args } = input;
32998
+ if ((action === "triage_bug" || action === "triage_feature") && args.notes && !args.triage_notes) {
32999
+ args.triage_notes = args.notes;
33000
+ delete args.notes;
33001
+ }
33002
+ return handler(args, extra);
33003
+ }
33004
+ );
33005
+ }
33006
+ var ACTION_TO_TOOL5;
33007
+ var init_support_consolidated = __esm({
33008
+ "src/mcp/tools/support-consolidated.ts"() {
33009
+ "use strict";
33010
+ init_create_bug_report();
33011
+ init_create_feature_request();
33012
+ init_support();
33013
+ init_support_inbox();
33014
+ ACTION_TO_TOOL5 = {
33015
+ create_bug: "create_bug_report",
33016
+ create_feature: "create_feature_request",
33017
+ list_my_bugs: "list_my_bug_reports",
33018
+ list_my_features: "list_my_feature_requests",
33019
+ list_bugs: "list_bug_reports",
33020
+ get_bug: "get_bug_report",
33021
+ triage_bug: "triage_bug_report",
33022
+ list_features: "list_feature_requests",
33023
+ get_feature: "get_feature_request",
33024
+ triage_feature: "triage_feature_request",
33025
+ health: "support_health",
33026
+ test: "support_test"
33027
+ };
33028
+ }
33029
+ });
33030
+
33031
+ // src/mcp/tools/diagnostics.ts
33032
+ import { z as z98 } from "zod";
33033
+ function buildHandlers9() {
33034
+ const tools = /* @__PURE__ */ new Map();
33035
+ const fake = {
33036
+ registerTool(name, _config, handler) {
33037
+ tools.set(name, handler);
33038
+ }
33039
+ };
33040
+ registerCliParityTools(fake);
33041
+ return tools;
33042
+ }
33043
+ function registerDiagnostics(server) {
33044
+ const handlers = buildHandlers9();
33045
+ const toolNames = Array.from(handlers.keys());
33046
+ void (toolNames.length > 0 ? toolNames : ["healthcheck"]);
33047
+ server.registerTool(
33048
+ "diagnostics",
33049
+ {
33050
+ title: "Diagnostics",
33051
+ description: `System diagnostics and admin operations. Actions: ${toolNames.join(", ")}. Covers health checks, update status, cloud status, key management, and employee rename.`,
33052
+ inputSchema: {
33053
+ action: z98.string().describe(`Diagnostic operation: ${toolNames.join(", ")}`),
33054
+ // Pass-through params used by various sub-tools
33055
+ name: z98.string().optional().describe("Employee name (rename_employee)"),
33056
+ new_name: z98.string().optional().describe("New employee name (rename_employee)"),
33057
+ query: z98.string().optional().describe("Search/filter query"),
33058
+ format: z98.string().optional().describe("Output format")
33059
+ }
33060
+ },
33061
+ async (input, extra) => {
33062
+ const action = input.action;
33063
+ const handler = handlers.get(action);
33064
+ if (!handler) {
33065
+ return {
33066
+ content: [{ type: "text", text: `Unknown diagnostics action: ${action}. Available: ${toolNames.join(", ")}` }],
33067
+ isError: true
33068
+ };
33069
+ }
33070
+ const { action: _, ...args } = input;
33071
+ return handler(args, extra);
33072
+ }
33073
+ );
33074
+ }
33075
+ var init_diagnostics = __esm({
33076
+ "src/mcp/tools/diagnostics.ts"() {
33077
+ "use strict";
33078
+ init_cli_parity();
33079
+ }
33080
+ });
33081
+
32499
33082
  // src/mcp/tool-gates.ts
33083
+ function isToolAllowedForPlan(registerFnName, plan) {
33084
+ const category = TOOL_CATEGORIES[registerFnName];
33085
+ if (!category) return true;
33086
+ const requiredPlan = PLAN_GATES[category] ?? "free";
33087
+ return PLAN_TIERS[plan] >= PLAN_TIERS[requiredPlan];
33088
+ }
32500
33089
  function isChiefOfStaffRole(role) {
32501
33090
  const normalized = (role ?? "").trim().toLowerCase();
32502
33091
  return normalized === "chief of staff" || normalized === "executive assistant";
@@ -32513,7 +33102,7 @@ function isToolAllowed(registerFnName) {
32513
33102
  if (!allowedRoles || allowedRoles.length === 0) return true;
32514
33103
  return allowedRoles.includes(role);
32515
33104
  }
32516
- var TOOL_GATES, TOOL_CATEGORIES, CHIEF_OF_STAFF_READ_TOOLS;
33105
+ var TOOL_GATES, TOOL_CATEGORIES, PLAN_TIERS, PLAN_GATES, CHIEF_OF_STAFF_READ_TOOLS;
32517
33106
  var init_tool_gates = __esm({
32518
33107
  "src/mcp/tool-gates.ts"() {
32519
33108
  "use strict";
@@ -32571,6 +33160,11 @@ var init_tool_gates = __esm({
32571
33160
  registerGetSessionEvents: "core",
32572
33161
  registerGetLastAssistantResponse: "core",
32573
33162
  registerCodeContext: "graph-read",
33163
+ // consolidated tools
33164
+ registerDecision: "core",
33165
+ registerSession: "core",
33166
+ registerSupportConsolidated: "core",
33167
+ registerDiagnostics: "admin",
32574
33168
  registerListBugReports: "admin",
32575
33169
  registerGetBugReport: "admin",
32576
33170
  registerTriageBugReport: "admin",
@@ -32664,6 +33258,30 @@ var init_tool_gates = __esm({
32664
33258
  registerListGlobalProcedures: "orchestration",
32665
33259
  registerDeactivateGlobalProcedure: "orchestration"
32666
33260
  };
33261
+ PLAN_TIERS = {
33262
+ free: 0,
33263
+ pro: 1,
33264
+ team: 2,
33265
+ agency: 3,
33266
+ enterprise: 4
33267
+ };
33268
+ PLAN_GATES = {
33269
+ core: "free",
33270
+ tasks: "free",
33271
+ comms: "free",
33272
+ reminders: "free",
33273
+ "graph-read": "free",
33274
+ licensing: "free",
33275
+ // always allow checking/managing own license
33276
+ "graph-write": "pro",
33277
+ wiki: "pro",
33278
+ crm: "pro",
33279
+ "raw-data": "pro",
33280
+ documents: "pro",
33281
+ gateway: "pro",
33282
+ admin: "pro",
33283
+ orchestration: "pro"
33284
+ };
32667
33285
  CHIEF_OF_STAFF_READ_TOOLS = /* @__PURE__ */ new Set([
32668
33286
  "registerRecallMyMemory",
32669
33287
  "registerAskTeamMemory",
@@ -32689,9 +33307,12 @@ var register_tools_exports = {};
32689
33307
  __export(register_tools_exports, {
32690
33308
  registerAllTools: () => registerAllTools
32691
33309
  });
32692
- function registerAllTools(server) {
33310
+ function registerAllTools(server, license, hasKey) {
32693
33311
  const gate = (name, fn) => {
32694
- if (isToolAllowed(name)) fn(server);
33312
+ if (!isToolAllowed(name)) return;
33313
+ if (hasKey === false && !ALWAYS_ALLOWED.has(name)) return;
33314
+ if (license && !isToolAllowedForPlan(name, license.plan)) return;
33315
+ fn(server);
32695
33316
  };
32696
33317
  const mcpToolSurface = process.env.EXE_MCP_TOOL_SURFACE ?? "legacy";
32697
33318
  const exposeConsolidatedTasks = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
@@ -32712,6 +33333,14 @@ function registerAllTools(server) {
32712
33333
  const exposeLegacyGraph = mcpToolSurface !== "consolidated";
32713
33334
  const exposeConsolidatedConfig = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
32714
33335
  const exposeLegacyConfig = mcpToolSurface !== "consolidated";
33336
+ const exposeConsolidatedDecisions = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
33337
+ const exposeLegacyDecisions = mcpToolSurface !== "consolidated";
33338
+ const exposeConsolidatedSession = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
33339
+ const exposeLegacySession = mcpToolSurface !== "consolidated";
33340
+ const exposeConsolidatedSupport = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
33341
+ const exposeLegacySupport = mcpToolSurface !== "consolidated";
33342
+ const exposeConsolidatedDiagnostics = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
33343
+ const exposeLegacyDiagnostics = mcpToolSurface !== "consolidated";
32715
33344
  if (exposeConsolidatedMemory) {
32716
33345
  gate("registerMemory", registerMemory);
32717
33346
  }
@@ -32825,23 +33454,43 @@ function registerAllTools(server) {
32825
33454
  if (exposeLegacyMemory) {
32826
33455
  gate("registerSearchEverything", registerSearchEverything);
32827
33456
  }
32828
- gate("registerStoreDecision", registerStoreDecision);
32829
- gate("registerGetDecision", registerGetDecision);
32830
- gate("registerCreateBugReport", registerCreateBugReport);
32831
- gate("registerCreateFeatureRequest", registerCreateFeatureRequest);
32832
- gate("registerSupportTools", registerSupportTools);
32833
- gate("registerCliParityTools", registerCliParityTools);
32834
- gate("registerGetSessionEvents", registerGetSessionEvents);
32835
- gate("registerGetLastAssistantResponse", registerGetLastAssistantResponse);
32836
- gate("registerCodeContext", registerCodeContext);
32837
- if (process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN) {
32838
- gate("registerListBugReports", registerListBugReports);
32839
- gate("registerGetBugReport", registerGetBugReport);
32840
- gate("registerTriageBugReport", registerTriageBugReport);
32841
- gate("registerListFeatureRequests", registerListFeatureRequests);
32842
- gate("registerGetFeatureRequest", registerGetFeatureRequest);
32843
- gate("registerTriageFeatureRequest", registerTriageFeatureRequest);
33457
+ if (exposeConsolidatedDecisions) {
33458
+ gate("registerDecision", registerDecision);
33459
+ }
33460
+ if (exposeLegacyDecisions) {
33461
+ gate("registerStoreDecision", registerStoreDecision);
33462
+ gate("registerGetDecision", registerGetDecision);
33463
+ }
33464
+ if (exposeConsolidatedSupport) {
33465
+ gate("registerSupportConsolidated", registerSupportConsolidated);
33466
+ }
33467
+ if (exposeLegacySupport) {
33468
+ gate("registerCreateBugReport", registerCreateBugReport);
33469
+ gate("registerCreateFeatureRequest", registerCreateFeatureRequest);
33470
+ gate("registerSupportTools", registerSupportTools);
33471
+ if (process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN) {
33472
+ gate("registerListBugReports", registerListBugReports);
33473
+ gate("registerGetBugReport", registerGetBugReport);
33474
+ gate("registerTriageBugReport", registerTriageBugReport);
33475
+ gate("registerListFeatureRequests", registerListFeatureRequests);
33476
+ gate("registerGetFeatureRequest", registerGetFeatureRequest);
33477
+ gate("registerTriageFeatureRequest", registerTriageFeatureRequest);
33478
+ }
33479
+ }
33480
+ if (exposeConsolidatedDiagnostics) {
33481
+ gate("registerDiagnostics", registerDiagnostics);
33482
+ }
33483
+ if (exposeLegacyDiagnostics) {
33484
+ gate("registerCliParityTools", registerCliParityTools);
33485
+ }
33486
+ if (exposeConsolidatedSession) {
33487
+ gate("registerSession", registerSession2);
33488
+ }
33489
+ if (exposeLegacySession) {
33490
+ gate("registerGetSessionEvents", registerGetSessionEvents);
33491
+ gate("registerGetLastAssistantResponse", registerGetLastAssistantResponse);
32844
33492
  }
33493
+ gate("registerCodeContext", registerCodeContext);
32845
33494
  if (exposeLegacyConfig) {
32846
33495
  gate("registerGetAgentSpend", registerGetAgentSpend);
32847
33496
  }
@@ -32890,6 +33539,7 @@ function registerAllTools(server) {
32890
33539
  gate("registerCompanyActions", registerCompanyActions);
32891
33540
  }
32892
33541
  }
33542
+ var ALWAYS_ALLOWED;
32893
33543
  var init_register_tools = __esm({
32894
33544
  "src/mcp/register-tools.ts"() {
32895
33545
  "use strict";
@@ -32989,7 +33639,16 @@ var init_register_tools = __esm({
32989
33639
  init_get_session_events();
32990
33640
  init_code_context();
32991
33641
  init_support_inbox();
33642
+ init_decision();
33643
+ init_session();
33644
+ init_support_consolidated();
33645
+ init_diagnostics();
32992
33646
  init_tool_gates();
33647
+ ALWAYS_ALLOWED = /* @__PURE__ */ new Set([
33648
+ "registerMcpPing",
33649
+ "registerGetLicenseStatus",
33650
+ "registerActivateLicense"
33651
+ ]);
32993
33652
  }
32994
33653
  });
32995
33654
 
@@ -34007,7 +34666,7 @@ import os24 from "os";
34007
34666
  import net2 from "net";
34008
34667
  import { createServer as createHttpServer } from "http";
34009
34668
  import { randomUUID as randomUUID11 } from "crypto";
34010
- import { writeFileSync as writeFileSync29, unlinkSync as unlinkSync15, mkdirSync as mkdirSync25, existsSync as existsSync47, readFileSync as readFileSync41, chmodSync as chmodSync2 } from "fs";
34669
+ import { writeFileSync as writeFileSync29, unlinkSync as unlinkSync15, mkdirSync as mkdirSync25, existsSync as existsSync47, readFileSync as readFileSync41, chmodSync as chmodSync3 } from "fs";
34011
34670
  import path62 from "path";
34012
34671
 
34013
34672
  // src/lib/orchestration-metrics.ts
@@ -34091,6 +34750,7 @@ var _context = null;
34091
34750
  var _model = null;
34092
34751
  var _llama = null;
34093
34752
  var _shuttingDown = false;
34753
+ var _mcpHttpReady = Promise.resolve();
34094
34754
  var _daemonToken = "";
34095
34755
  var MAX_QUEUE_SIZE = 1e3;
34096
34756
  var highQueue = [];
@@ -34140,6 +34800,13 @@ async function processQueue() {
34140
34800
  if (entry.socket.destroyed) continue;
34141
34801
  try {
34142
34802
  const vectors = [];
34803
+ if (!_context) {
34804
+ sendResponse(entry.socket, {
34805
+ id: entry.request.id,
34806
+ error: "Model not loaded yet \u2014 embeddings unavailable"
34807
+ });
34808
+ continue;
34809
+ }
34143
34810
  for (const text3 of entry.request.texts) {
34144
34811
  const embedding = await _context.getEmbeddingFor(text3);
34145
34812
  const vector = Array.from(embedding.vector);
@@ -34555,7 +35222,7 @@ function startMemoryQueueDrain() {
34555
35222
  function startServer() {
34556
35223
  mkdirSync25(path62.dirname(SOCKET_PATH2), { recursive: true });
34557
35224
  try {
34558
- chmodSync2(path62.dirname(SOCKET_PATH2), 448);
35225
+ chmodSync3(path62.dirname(SOCKET_PATH2), 448);
34559
35226
  } catch {
34560
35227
  }
34561
35228
  _daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV2] ?? null);
@@ -34677,12 +35344,12 @@ function startServer() {
34677
35344
  process.stderr.write(`[exed] Listening on ${SOCKET_PATH2}
34678
35345
  `);
34679
35346
  try {
34680
- chmodSync2(SOCKET_PATH2, 384);
35347
+ chmodSync3(SOCKET_PATH2, 384);
34681
35348
  } catch {
34682
35349
  }
34683
35350
  checkIdle();
34684
35351
  });
34685
- void startMcpHttpServer();
35352
+ _mcpHttpReady = startMcpHttpServer();
34686
35353
  }
34687
35354
  async function startMcpHttpServer() {
34688
35355
  process.stderr.write("[exed] MCP HTTP: starting setup...\n");
@@ -34735,11 +35402,13 @@ async function startMcpHttpServer() {
34735
35402
  recordMcpHttpEvent({ level: "warn", message, status: status2, ...extra });
34736
35403
  };
34737
35404
  var parseDurationMs = parseDurationMs2, closeMcpSession = closeMcpSession2, sweepStaleMcpSessions = sweepStaleMcpSessions2, getJsonRpcId = getJsonRpcId2, sendJsonRpcError = sendJsonRpcError2;
35405
+ process.stderr.write("[exed] MCP HTTP: importing SDK modules...\n");
34738
35406
  const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
34739
35407
  const { StreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
34740
35408
  const { isInitializeRequest } = await import("@modelcontextprotocol/sdk/types.js");
34741
35409
  const { registerAllTools: registerAllTools2 } = await Promise.resolve().then(() => (init_register_tools(), register_tools_exports));
34742
35410
  const { runWithAgent: runWithAgent2 } = await Promise.resolve().then(() => (init_agent_context(), agent_context_exports));
35411
+ process.stderr.write("[exed] MCP HTTP: SDK imports done, initializing DB...\n");
34743
35412
  const dbReady = await ensureStoreForPolling();
34744
35413
  if (!dbReady) {
34745
35414
  process.stderr.write(
@@ -34748,6 +35417,18 @@ async function startMcpHttpServer() {
34748
35417
  return;
34749
35418
  }
34750
35419
  process.stderr.write("[exed] MCP HTTP: DB ready\n");
35420
+ const { initLicenseGate: initLicenseGate2, getCachedLicenseGate: getCachedLicenseGate2 } = await Promise.resolve().then(() => (init_license_gate(), license_gate_exports));
35421
+ const gateResult = await initLicenseGate2();
35422
+ if (!gateResult.hasKey) {
35423
+ process.stderr.write(
35424
+ "[exed] MCP HTTP: No license key found \u2014 tools restricted to diagnostics only.\n"
35425
+ );
35426
+ } else if (gateResult.license) {
35427
+ process.stderr.write(
35428
+ `[exed] MCP HTTP: License validated \u2014 plan=${gateResult.license.plan}
35429
+ `
35430
+ );
35431
+ }
34751
35432
  const transports = /* @__PURE__ */ new Map();
34752
35433
  const MCP_HTTP_PORT = parseInt(process.env.EXE_MCP_PORT || "48739", 10);
34753
35434
  const MCP_SESSION_TTL_MS = parseDurationMs2(process.env.EXE_MCP_SESSION_TTL_MS, 4 * 60 * 60 * 1e3, { allowZero: true });
@@ -34767,16 +35448,14 @@ async function startMcpHttpServer() {
34767
35448
  return;
34768
35449
  }
34769
35450
  const url = new URL(req.url || "/", `http://127.0.0.1:${MCP_HTTP_PORT}`);
34770
- const authHeader = req.headers.authorization;
34771
- if (!authHeader || authHeader !== `Bearer ${_daemonToken}`) {
34772
- sendJsonRpcError2(res, 401, "Unauthorized: invalid or missing daemon token", "unknown", {
34773
- method: req.method,
34774
- path: url.pathname
34775
- });
35451
+ if (url.pathname !== "/mcp") {
35452
+ res.writeHead(404, { "Content-Type": "text/plain" });
35453
+ res.end("Not Found");
34776
35454
  return;
34777
35455
  }
34778
- if (url.pathname !== "/mcp") {
34779
- sendJsonRpcError2(res, 404, "Not Found: MCP endpoint is /mcp", "unknown", {
35456
+ const authHeader = req.headers.authorization;
35457
+ if (authHeader && authHeader !== `Bearer ${_daemonToken}`) {
35458
+ sendJsonRpcError2(res, 401, "Unauthorized: invalid daemon token", "unknown", {
34780
35459
  method: req.method,
34781
35460
  path: url.pathname
34782
35461
  });
@@ -34837,7 +35516,10 @@ async function startMcpHttpServer() {
34837
35516
  if (sid) closeMcpSession2(sid, "session_closed");
34838
35517
  };
34839
35518
  const sessionMcp = new McpServer({ name: "exe-os", version: "1.3.0" });
34840
- registerAllTools2(sessionMcp);
35519
+ const gateState = getCachedLicenseGate2();
35520
+ const { wrapServerWithTelemetry: wrapServerWithTelemetry2 } = await Promise.resolve().then(() => (init_tool_telemetry(), tool_telemetry_exports));
35521
+ const wrappedMcp = wrapServerWithTelemetry2(sessionMcp);
35522
+ registerAllTools2(wrappedMcp, gateState.license, gateState.hasKey);
34841
35523
  await sessionMcp.connect(transport);
34842
35524
  } else {
34843
35525
  const message = sessionId ? "Bad Request: MCP session is stale or unknown; reconnect MCP client" : "Bad Request: missing MCP session; initialize before tool calls";
@@ -34910,8 +35592,9 @@ async function startMcpHttpServer() {
34910
35592
  }
34911
35593
  }
34912
35594
  });
34913
- httpServer.listen(MCP_HTTP_PORT, "127.0.0.1", () => {
34914
- process.stderr.write(`[exed] MCP HTTP listening on 127.0.0.1:${MCP_HTTP_PORT}
35595
+ const MCP_HTTP_HOST = process.env.EXE_MCP_HOST || process.env.EXED_HOST || "127.0.0.1";
35596
+ httpServer.listen(MCP_HTTP_PORT, MCP_HTTP_HOST, () => {
35597
+ process.stderr.write(`[exed] MCP HTTP listening on ${MCP_HTTP_HOST}:${MCP_HTTP_PORT}
34915
35598
  `);
34916
35599
  recordMcpHttpEvent({
34917
35600
  level: "info",
@@ -35595,6 +36278,18 @@ function handleSignalShutdown() {
35595
36278
  }
35596
36279
  process.on("SIGINT", handleSignalShutdown);
35597
36280
  process.on("SIGTERM", handleSignalShutdown);
36281
+ process.on("unhandledRejection", (reason) => {
36282
+ process.stderr.write(
36283
+ `[exed] Unhandled rejection (caught): ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}
36284
+ `
36285
+ );
36286
+ });
36287
+ process.on("uncaughtException", (err) => {
36288
+ process.stderr.write(
36289
+ `[exed] Uncaught exception (caught): ${err.stack ?? err.message}
36290
+ `
36291
+ );
36292
+ });
35598
36293
  function checkExistingDaemon() {
35599
36294
  try {
35600
36295
  if (!existsSync47(PID_PATH4)) return false;
@@ -35694,14 +36389,20 @@ if (checkExistingDaemon()) {
35694
36389
  }
35695
36390
  writeFileSync29(PID_PATH4, String(process.pid));
35696
36391
  try {
35697
- chmodSync2(PID_PATH4, 384);
36392
+ chmodSync3(PID_PATH4, 384);
35698
36393
  } catch {
35699
36394
  }
35700
36395
  process.env.EXE_IS_DAEMON = "1";
35701
36396
  try {
35702
36397
  initMetrics();
35703
- await loadModel();
35704
36398
  startServer();
36399
+ await _mcpHttpReady;
36400
+ loadModel().catch((err) => {
36401
+ process.stderr.write(
36402
+ `[exed] Background model load failed: ${err instanceof Error ? err.message : String(err)}
36403
+ `
36404
+ );
36405
+ });
35705
36406
  startReviewPolling();
35706
36407
  startSessionTTL();
35707
36408
  startIdleKill();
@@ -35721,6 +36422,9 @@ try {
35721
36422
  startRssWatchdog();
35722
36423
  startBackgroundJobGuardrails();
35723
36424
  startTaskEnforcementScanner();
36425
+ const { startToolTelemetryFlush: startToolTelemetryFlush2 } = await Promise.resolve().then(() => (init_tool_telemetry(), tool_telemetry_exports));
36426
+ startToolTelemetryFlush2();
36427
+ process.stderr.write("[exed] Tool telemetry started (flush every 5m)\n");
35724
36428
  const { startProjectionWorker: startProjectionWorker2 } = await Promise.resolve().then(() => (init_projection_worker(), projection_worker_exports));
35725
36429
  startProjectionWorker2();
35726
36430
  try {