@askexenow/exe-os 0.9.86 → 0.9.87

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 (72) hide show
  1. package/dist/bin/agentic-ontology-backfill.js +18 -0
  2. package/dist/bin/agentic-reflection-backfill.js +18 -0
  3. package/dist/bin/agentic-semantic-label.js +18 -0
  4. package/dist/bin/backfill-conversations.js +18 -0
  5. package/dist/bin/backfill-responses.js +18 -0
  6. package/dist/bin/backfill-vectors.js +18 -0
  7. package/dist/bin/bulk-sync-postgres.js +18 -0
  8. package/dist/bin/cleanup-stale-review-tasks.js +18 -0
  9. package/dist/bin/cli.js +187 -4
  10. package/dist/bin/exe-agent.js +18 -0
  11. package/dist/bin/exe-assign.js +18 -0
  12. package/dist/bin/exe-boot.js +18 -0
  13. package/dist/bin/exe-call.js +18 -0
  14. package/dist/bin/exe-cloud.js +18 -0
  15. package/dist/bin/exe-dispatch.js +18 -0
  16. package/dist/bin/exe-doctor.js +18 -0
  17. package/dist/bin/exe-export-behaviors.js +18 -0
  18. package/dist/bin/exe-forget.js +18 -0
  19. package/dist/bin/exe-gateway.js +18 -0
  20. package/dist/bin/exe-heartbeat.js +18 -0
  21. package/dist/bin/exe-kill.js +18 -0
  22. package/dist/bin/exe-launch-agent.js +18 -0
  23. package/dist/bin/exe-new-employee.js +33 -2
  24. package/dist/bin/exe-pending-messages.js +18 -0
  25. package/dist/bin/exe-pending-notifications.js +18 -0
  26. package/dist/bin/exe-pending-reviews.js +18 -0
  27. package/dist/bin/exe-rename.js +18 -0
  28. package/dist/bin/exe-review.js +18 -0
  29. package/dist/bin/exe-search.js +18 -0
  30. package/dist/bin/exe-session-cleanup.js +18 -0
  31. package/dist/bin/exe-start-codex.js +18 -0
  32. package/dist/bin/exe-start-opencode.js +18 -0
  33. package/dist/bin/exe-status.js +18 -0
  34. package/dist/bin/exe-team.js +18 -0
  35. package/dist/bin/git-sweep.js +18 -0
  36. package/dist/bin/graph-backfill.js +18 -0
  37. package/dist/bin/graph-export.js +18 -0
  38. package/dist/bin/install.js +9 -0
  39. package/dist/bin/intercom-check.js +18 -0
  40. package/dist/bin/scan-tasks.js +18 -0
  41. package/dist/bin/setup.js +24 -2
  42. package/dist/bin/shard-migrate.js +18 -0
  43. package/dist/gateway/index.js +18 -0
  44. package/dist/hooks/bug-report-worker.js +18 -0
  45. package/dist/hooks/codex-stop-task-finalizer.js +18 -0
  46. package/dist/hooks/commit-complete.js +18 -0
  47. package/dist/hooks/error-recall.js +18 -0
  48. package/dist/hooks/ingest.js +18 -0
  49. package/dist/hooks/instructions-loaded.js +18 -0
  50. package/dist/hooks/notification.js +18 -0
  51. package/dist/hooks/post-compact.js +18 -0
  52. package/dist/hooks/post-tool-combined.js +18 -0
  53. package/dist/hooks/pre-compact.js +18 -0
  54. package/dist/hooks/pre-tool-use.js +18 -0
  55. package/dist/hooks/prompt-submit.js +18 -0
  56. package/dist/hooks/session-end.js +18 -0
  57. package/dist/hooks/session-start.js +18 -0
  58. package/dist/hooks/stop.js +18 -0
  59. package/dist/hooks/subagent-stop.js +18 -0
  60. package/dist/hooks/summary-worker.js +18 -0
  61. package/dist/index.js +18 -0
  62. package/dist/lib/employee-templates.js +18 -0
  63. package/dist/lib/exe-daemon.js +712 -169
  64. package/dist/lib/hybrid-search.js +18 -0
  65. package/dist/lib/identity-templates.js +6 -2
  66. package/dist/lib/schedules.js +18 -0
  67. package/dist/lib/store.js +18 -0
  68. package/dist/mcp/server.js +670 -143
  69. package/dist/mcp/tools/create-task.js +6 -2
  70. package/dist/runtime/index.js +18 -0
  71. package/dist/tui/App.js +18 -0
  72. package/package.json +1 -1
@@ -5350,7 +5350,7 @@ async function tryKeytar() {
5350
5350
  }
5351
5351
  function deriveMachineKey() {
5352
5352
  try {
5353
- const crypto23 = __require("crypto");
5353
+ const crypto24 = __require("crypto");
5354
5354
  const material = [
5355
5355
  os6.hostname(),
5356
5356
  os6.userInfo().username,
@@ -5359,7 +5359,7 @@ function deriveMachineKey() {
5359
5359
  // Machine ID on Linux (stable across reboots)
5360
5360
  process.platform === "linux" ? readMachineId() : ""
5361
5361
  ].join("|");
5362
- return crypto23.createHash("sha256").update(material).digest();
5362
+ return crypto24.createHash("sha256").update(material).digest();
5363
5363
  } catch {
5364
5364
  return null;
5365
5365
  }
@@ -5373,9 +5373,9 @@ function readMachineId() {
5373
5373
  }
5374
5374
  }
5375
5375
  function encryptWithMachineKey(plaintext, machineKey) {
5376
- const crypto23 = __require("crypto");
5377
- const iv = crypto23.randomBytes(12);
5378
- const cipher = crypto23.createCipheriv("aes-256-gcm", machineKey, iv);
5376
+ const crypto24 = __require("crypto");
5377
+ const iv = crypto24.randomBytes(12);
5378
+ const cipher = crypto24.createCipheriv("aes-256-gcm", machineKey, iv);
5379
5379
  let encrypted = cipher.update(plaintext, "utf-8", "base64");
5380
5380
  encrypted += cipher.final("base64");
5381
5381
  const authTag = cipher.getAuthTag().toString("base64");
@@ -5384,13 +5384,13 @@ function encryptWithMachineKey(plaintext, machineKey) {
5384
5384
  function decryptWithMachineKey(encrypted, machineKey) {
5385
5385
  if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
5386
5386
  try {
5387
- const crypto23 = __require("crypto");
5387
+ const crypto24 = __require("crypto");
5388
5388
  const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
5389
5389
  if (parts.length !== 3) return null;
5390
5390
  const [ivB64, tagB64, cipherB64] = parts;
5391
5391
  const iv = Buffer.from(ivB64, "base64");
5392
5392
  const authTag = Buffer.from(tagB64, "base64");
5393
- const decipher = crypto23.createDecipheriv("aes-256-gcm", machineKey, iv);
5393
+ const decipher = crypto24.createDecipheriv("aes-256-gcm", machineKey, iv);
5394
5394
  decipher.setAuthTag(authTag);
5395
5395
  let decrypted = decipher.update(cipherB64, "base64", "utf-8");
5396
5396
  decrypted += decipher.final("utf-8");
@@ -5819,6 +5819,24 @@ var init_platform_procedures = __esm({
5819
5819
  priority: "p0",
5820
5820
  content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
5821
5821
  },
5822
+ {
5823
+ title: "Bug report status check \u2014 surface available fixes on boot",
5824
+ domain: "support",
5825
+ priority: "p1",
5826
+ content: "Once per session (COO boot only, never repeat), call list_my_bug_reports to check if any previously filed bug reports have been fixed by AskExe. If any report has status 'fixed' with a fixed_version, surface it to the founder immediately: '\u{1F527} N bug fix(es) available \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no reports exist or none are fixed, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
5827
+ },
5828
+ {
5829
+ title: "Feature request triage \u2014 upstream feature vs local customization",
5830
+ domain: "support",
5831
+ priority: "p0",
5832
+ content: "When an agent or founder identifies a desired capability that exe-os does not yet provide, the COO (or equivalent coordinator) must decide: is this a local customization (identity, behavior, procedure, config, branding, workflow preference that can be configured in customer-owned layers) or an upstream feature request (a platform capability that requires changes to exe-os code, shipped via npm update)? Local customizations: implement immediately using store_behavior, update_identity, company_procedure, or config changes. Upstream features: use create_feature_request to submit to AskExe. Include use case, business impact, and current workaround. Do NOT ask the founder for permission to file a feature request \u2014 file it proactively when the need is clear."
5833
+ },
5834
+ {
5835
+ title: "Feature request status check \u2014 surface shipped features on boot",
5836
+ domain: "support",
5837
+ priority: "p1",
5838
+ content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
5839
+ },
5822
5840
  // --- Operations ---
5823
5841
  {
5824
5842
  title: "Managers must supervise deployed workers",
@@ -7193,8 +7211,8 @@ async function embedDirect(text3) {
7193
7211
  const llamaCpp = await import("node-llama-cpp");
7194
7212
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
7195
7213
  const { existsSync: existsSync48 } = await import("fs");
7196
- const path62 = await import("path");
7197
- const modelPath = path62.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
7214
+ const path63 = await import("path");
7215
+ const modelPath = path63.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
7198
7216
  if (!existsSync48(modelPath)) {
7199
7217
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
7200
7218
  }
@@ -8286,10 +8304,10 @@ async function hybridSearch(queryText, agentId, options) {
8286
8304
  };
8287
8305
  try {
8288
8306
  const fs = await import("fs");
8289
- const path62 = await import("path");
8307
+ const path63 = await import("path");
8290
8308
  const os25 = await import("os");
8291
- const logPath = path62.join(os25.homedir(), ".exe-os", "search-quality.jsonl");
8292
- fs.mkdirSync(path62.dirname(logPath), { recursive: true });
8309
+ const logPath = path63.join(os25.homedir(), ".exe-os", "search-quality.jsonl");
8310
+ fs.mkdirSync(path63.dirname(logPath), { recursive: true });
8293
8311
  fs.appendFileSync(logPath, JSON.stringify(logEntry) + "\n");
8294
8312
  } catch {
8295
8313
  }
@@ -9922,8 +9940,8 @@ __export(wiki_client_exports, {
9922
9940
  listDocuments: () => listDocuments,
9923
9941
  listWorkspaces: () => listWorkspaces
9924
9942
  });
9925
- async function wikiFetch(config2, path62, method = "GET", body) {
9926
- const url = `${config2.baseUrl}/api/v1${path62}`;
9943
+ async function wikiFetch(config2, path63, method = "GET", body) {
9944
+ const url = `${config2.baseUrl}/api/v1${path63}`;
9927
9945
  const headers = {
9928
9946
  Authorization: `Bearer ${config2.apiKey}`,
9929
9947
  "Content-Type": "application/json"
@@ -9956,7 +9974,7 @@ async function wikiFetch(config2, path62, method = "GET", body) {
9956
9974
  }
9957
9975
  }
9958
9976
  if (!response.ok) {
9959
- throw new Error(`Wiki API ${method} ${path62}: ${response.status} ${response.statusText}`);
9977
+ throw new Error(`Wiki API ${method} ${path63}: ${response.status} ${response.statusText}`);
9960
9978
  }
9961
9979
  return response.json();
9962
9980
  } finally {
@@ -15037,12 +15055,14 @@ On EVERY new conversation, before doing anything else:
15037
15055
  1. **Memory scan**: Run recall_my_memory with broad queries \u2014 "project", "client", "pipeline", "campaign", "deal", "decision", "blocker". Summarize what you find.
15038
15056
  2. **Task scan**: Run list_tasks to see what's open, in progress, blocked, or needs review across all employees.
15039
15057
  3. **Team check**: Run ask_team_memory for recent activity from CTO/CMO/engineers.
15040
- 4. **Present the brief**: Give the founder a concise status report:
15058
+ 4. **Bug fix check** (one-time, never repeat): Call list_my_bug_reports to see if AskExe has fixed any previously filed bugs. If any have status "fixed" with a fixed_version, tell the founder: "\u{1F527} N bug fix(es) available \u2014 run \`exe-os update\` to get version X.Y.Z." Skip silently if none or if the call fails.
15059
+ 5. **Present the brief**: Give the founder a concise status report:
15041
15060
  - What's active and progressing
15042
15061
  - What's blocked and needs attention
15043
15062
  - What decisions are pending
15063
+ - Available bug fixes (from step 4, if any)
15044
15064
  - What you recommend doing next
15045
- 5. Then ask: "What's the priority?"
15065
+ 6. Then ask: "What's the priority?"
15046
15066
 
15047
15067
  If this is your FIRST ever conversation (few or no prior memories):
15048
15068
  - Search more broadly: "product", "SEO", "meeting", "strategy", "revenue"
@@ -15062,6 +15082,8 @@ Never say "I have no memories" without first searching broadly. Your memory may
15062
15082
  - **get_identity** \u2014 read any agent's identity for coordination
15063
15083
  - **set_agent_config** \u2014 view or change which tool (Claude Code, Codex, OpenCode) and model each agent uses. Call with no args to show all agents' current settings. Call with agent_id + runtime + model to change.
15064
15084
  - **send_message** \u2014 direct intercom to employees
15085
+ - **create_bug_report** \u2014 file a bug when you encounter an Exe OS platform issue
15086
+ - **list_my_bug_reports** \u2014 check status of filed bugs (boot check: surface available fixes to founder)
15065
15087
  ${PLAN_MODE_COMPAT}
15066
15088
  ## Completion Workflow
15067
15089
 
@@ -19058,12 +19080,12 @@ function registerExportGraph(server) {
19058
19080
  }
19059
19081
  const html = await exportGraphHTML(client);
19060
19082
  const fs = await import("fs");
19061
- const path62 = await import("path");
19083
+ const path63 = await import("path");
19062
19084
  const os25 = await import("os");
19063
- const outDir = path62.join(os25.homedir(), ".exe-os", "exports");
19085
+ const outDir = path63.join(os25.homedir(), ".exe-os", "exports");
19064
19086
  fs.mkdirSync(outDir, { recursive: true });
19065
19087
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
19066
- const filePath = path62.join(outDir, `graph-${timestamp}.html`);
19088
+ const filePath = path63.join(outDir, `graph-${timestamp}.html`);
19067
19089
  fs.writeFileSync(filePath, html, "utf-8");
19068
19090
  return {
19069
19091
  content: [
@@ -26148,9 +26170,9 @@ var init_hostinger_api = __esm({
26148
26170
  }
26149
26171
  this.lastRequestTime = Date.now();
26150
26172
  }
26151
- async request(method, path62, body) {
26173
+ async request(method, path63, body) {
26152
26174
  await this.rateLimit();
26153
- const url = `${this.baseUrl}${path62}`;
26175
+ const url = `${this.baseUrl}${path63}`;
26154
26176
  const headers = {
26155
26177
  Authorization: `Bearer ${this.apiKey}`,
26156
26178
  "Content-Type": "application/json",
@@ -26219,8 +26241,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
26219
26241
  }
26220
26242
  return envelope.result;
26221
26243
  }
26222
- function buildUrl(zoneId, path62 = "/dns_records", query) {
26223
- const normalizedPath = path62.startsWith("/") ? path62 : `/${path62}`;
26244
+ function buildUrl(zoneId, path63 = "/dns_records", query) {
26245
+ const normalizedPath = path63.startsWith("/") ? path63 : `/${path63}`;
26224
26246
  const url = new URL(
26225
26247
  `${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
26226
26248
  );
@@ -30179,9 +30201,207 @@ var init_create_bug_report = __esm({
30179
30201
  }
30180
30202
  });
30181
30203
 
30204
+ // src/mcp/tools/create-feature-request.ts
30205
+ import { z as z89 } from "zod";
30206
+ import crypto20 from "crypto";
30207
+ import { mkdir as mkdir7, writeFile as writeFile8 } from "fs/promises";
30208
+ import path54 from "path";
30209
+ function slugify3(input) {
30210
+ return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "feature-request";
30211
+ }
30212
+ function section2(title, body) {
30213
+ return `## ${title}
30214
+
30215
+ ${body?.trim() || "Not provided"}`;
30216
+ }
30217
+ function buildMarkdown2(input) {
30218
+ return [
30219
+ `# Feature Request \u2014 ${input.title}`,
30220
+ "",
30221
+ `id: ${input.id}`,
30222
+ `category: ${input.category}`,
30223
+ `priority: ${input.priority}`,
30224
+ `filed_by: ${input.agentId} (${input.agentRole})`,
30225
+ `package_version: ${input.packageVersion}`,
30226
+ `project: ${input.projectName ?? "unknown"}`,
30227
+ `created_at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
30228
+ "",
30229
+ section2("Description", input.description),
30230
+ section2("Use Case", input.useCase),
30231
+ section2("Current Workaround", input.currentWorkaround),
30232
+ section2("Proposed Solution", input.proposedSolution),
30233
+ section2("Business Impact", input.businessImpact)
30234
+ ].join("\n");
30235
+ }
30236
+ async function maybeSendUpstream2(payload) {
30237
+ const config2 = await loadConfig();
30238
+ const routerUrl = process.env.API_ROUTER_URL?.replace(/\/+$/, "");
30239
+ const endpoint2 = config2.support?.featureRequestEndpoint || process.env.EXE_FEATURE_REQUEST_ENDPOINT || (routerUrl ? `${routerUrl}/v1/support/feature-requests` : "https://askexe.com/v1/support/feature-requests");
30240
+ const token = config2.support?.featureRequestToken || process.env.EXE_FEATURE_REQUEST_TOKEN;
30241
+ const licenseKey = loadLicense() || process.env.EXE_LICENSE_KEY || config2.cloud?.apiKey;
30242
+ const licenseToken = readCachedLicenseToken();
30243
+ if (!endpoint2) {
30244
+ return "not_configured";
30245
+ }
30246
+ try {
30247
+ const parsed = new URL(endpoint2);
30248
+ if (parsed.protocol !== "https:" && !["localhost", "127.0.0.1", "::1"].includes(parsed.hostname)) {
30249
+ return "failed: insecure endpoint rejected";
30250
+ }
30251
+ const response = await fetch(parsed, {
30252
+ method: "POST",
30253
+ headers: {
30254
+ "content-type": "application/json",
30255
+ ...token ? { authorization: `Bearer ${token}` } : {},
30256
+ ...licenseKey ? { "x-exe-license-key": licenseKey } : {},
30257
+ ...licenseToken ? { "x-exe-license-token": licenseToken } : {}
30258
+ },
30259
+ body: JSON.stringify(payload),
30260
+ signal: AbortSignal.timeout(1e4)
30261
+ });
30262
+ if (!response.ok) return `failed: HTTP ${response.status}`;
30263
+ return "sent";
30264
+ } catch (err) {
30265
+ return `failed: ${err instanceof Error ? err.message : String(err)}`;
30266
+ }
30267
+ }
30268
+ function registerCreateFeatureRequest(server) {
30269
+ server.registerTool(
30270
+ "create_feature_request",
30271
+ {
30272
+ title: "Create Feature Request",
30273
+ description: "File a feature request for exe-os: upstream_feature (requires platform changes), local_customization (configurable in customer layers), integration (third-party), or unclear. Writes a local report, stores memory, and optionally sends to AskExe.",
30274
+ inputSchema: {
30275
+ title: z89.string().min(3).describe("Short descriptive title"),
30276
+ category: CATEGORY.describe(
30277
+ "upstream_feature = platform capability change; local_customization = configurable in customer layers; integration = third-party connector; unclear = needs product triage"
30278
+ ),
30279
+ priority: PRIORITY.default("p2").describe("p0 critical \u2192 p3 low"),
30280
+ description: z89.string().min(10).describe("What capability is needed and why"),
30281
+ use_case: z89.string().optional().describe("Concrete scenario where this feature would be used"),
30282
+ current_workaround: z89.string().optional().describe("How the need is currently addressed, if at all"),
30283
+ proposed_solution: z89.string().optional().describe("Suggested implementation approach"),
30284
+ business_impact: z89.string().optional().describe("Impact on business/workflow if this ships"),
30285
+ package_version: z89.string().optional().describe("Installed @askexenow/exe-os version"),
30286
+ project_name: z89.string().optional().describe("Project/customer context"),
30287
+ send_upstream: z89.boolean().default(true).describe("Attempt to POST to configured AskExe support endpoint")
30288
+ }
30289
+ },
30290
+ async ({
30291
+ title,
30292
+ category,
30293
+ priority,
30294
+ description,
30295
+ use_case,
30296
+ current_workaround,
30297
+ proposed_solution,
30298
+ business_impact,
30299
+ package_version,
30300
+ project_name,
30301
+ send_upstream
30302
+ }) => {
30303
+ const { agentId, agentRole } = getActiveAgent();
30304
+ const id = crypto20.randomUUID();
30305
+ const version = package_version ?? "unknown";
30306
+ const markdown = buildMarkdown2({
30307
+ id,
30308
+ title,
30309
+ category,
30310
+ priority,
30311
+ agentId,
30312
+ agentRole,
30313
+ packageVersion: version,
30314
+ description,
30315
+ useCase: use_case,
30316
+ currentWorkaround: current_workaround,
30317
+ proposedSolution: proposed_solution,
30318
+ businessImpact: business_impact,
30319
+ projectName: project_name
30320
+ });
30321
+ const outDir = path54.join(EXE_AI_DIR, "feature-requests");
30322
+ await mkdir7(outDir, { recursive: true });
30323
+ const reportPath = path54.join(outDir, `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}-${slugify3(title)}-${id.slice(0, 8)}.md`);
30324
+ await writeFile8(reportPath, markdown, "utf-8");
30325
+ let vector = null;
30326
+ try {
30327
+ vector = await embed(markdown);
30328
+ } catch {
30329
+ vector = null;
30330
+ }
30331
+ await writeMemory({
30332
+ id,
30333
+ agent_id: agentId,
30334
+ agent_role: agentRole,
30335
+ session_id: process.env.SESSION_ID ?? "manual",
30336
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
30337
+ tool_name: "create_feature_request",
30338
+ project_name: project_name ?? "support",
30339
+ has_error: false,
30340
+ raw_text: markdown,
30341
+ vector,
30342
+ source_path: reportPath,
30343
+ source_type: "feature_request",
30344
+ memory_type: "feature_request",
30345
+ tier: 1,
30346
+ importance: priority === "p0" ? 10 : priority === "p1" ? 9 : priority === "p2" ? 7 : 5,
30347
+ intent: "request",
30348
+ domain: "support",
30349
+ file_paths: null
30350
+ });
30351
+ await flushBatch();
30352
+ const upstreamStatus = send_upstream ? await maybeSendUpstream2({
30353
+ id,
30354
+ title,
30355
+ category,
30356
+ priority,
30357
+ description,
30358
+ use_case,
30359
+ current_workaround,
30360
+ proposed_solution,
30361
+ business_impact,
30362
+ package_version: version,
30363
+ project_name,
30364
+ agent_id: agentId,
30365
+ agent_role: agentRole
30366
+ }) : "skipped";
30367
+ return {
30368
+ content: [
30369
+ {
30370
+ type: "text",
30371
+ text: `Feature request created.
30372
+ ID: ${id}
30373
+ Category: ${category}
30374
+ Local report: ${reportPath}
30375
+ Memory stored: ${id}
30376
+ Upstream status: ${upstreamStatus}`
30377
+ }
30378
+ ]
30379
+ };
30380
+ }
30381
+ );
30382
+ }
30383
+ var CATEGORY, PRIORITY;
30384
+ var init_create_feature_request = __esm({
30385
+ "src/mcp/tools/create-feature-request.ts"() {
30386
+ "use strict";
30387
+ init_embedder();
30388
+ init_active_agent();
30389
+ init_config();
30390
+ init_license();
30391
+ init_store();
30392
+ CATEGORY = z89.enum([
30393
+ "upstream_feature",
30394
+ "local_customization",
30395
+ "integration",
30396
+ "unclear"
30397
+ ]);
30398
+ PRIORITY = z89.enum(["p0", "p1", "p2", "p3"]);
30399
+ }
30400
+ });
30401
+
30182
30402
  // src/bin/exe-support.ts
30183
30403
  import { mkdirSync as mkdirSync21, readFileSync as readFileSync35, unlinkSync as unlinkSync13, writeFileSync as writeFileSync24 } from "fs";
30184
- import path54 from "path";
30404
+ import path55 from "path";
30185
30405
  import { randomUUID as randomUUID9 } from "crypto";
30186
30406
  async function runHealth() {
30187
30407
  const checks = [];
@@ -30300,8 +30520,8 @@ async function resolveEndpoints() {
30300
30520
  return { bugEndpoint, healthEndpoint, adminEndpoint };
30301
30521
  }
30302
30522
  function checkLocalWrite() {
30303
- const dir = path54.join(EXE_AI_DIR, "bug-reports");
30304
- const testPath = path54.join(dir, ".support-write-test");
30523
+ const dir = path55.join(EXE_AI_DIR, "bug-reports");
30524
+ const testPath = path55.join(dir, ".support-write-test");
30305
30525
  try {
30306
30526
  mkdirSync21(dir, { recursive: true, mode: 448 });
30307
30527
  writeFileSync24(testPath, "ok\n", { mode: 384 });
@@ -30317,10 +30537,10 @@ function checkLocalWrite() {
30317
30537
  }
30318
30538
  }
30319
30539
  function writeLocalTestReport(id, project, version) {
30320
- const dir = path54.join(EXE_AI_DIR, "bug-reports");
30540
+ const dir = path55.join(EXE_AI_DIR, "bug-reports");
30321
30541
  mkdirSync21(dir, { recursive: true, mode: 448 });
30322
30542
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
30323
- const filePath = path54.join(dir, `${date}-support-intake-test-${id.slice(0, 8)}.md`);
30543
+ const filePath = path55.join(dir, `${date}-support-intake-test-${id.slice(0, 8)}.md`);
30324
30544
  writeFileSync24(filePath, `# TEST \u2014 ${project} support intake
30325
30545
 
30326
30546
  Report ID: ${id}
@@ -30362,15 +30582,15 @@ async function maybeCloseAdmin(id, adminEndpoint, version) {
30362
30582
  }
30363
30583
  }
30364
30584
  function readPackageVersion2() {
30365
- let dir = path54.dirname(new URL(import.meta.url).pathname);
30585
+ let dir = path55.dirname(new URL(import.meta.url).pathname);
30366
30586
  for (let i = 0; i < 6; i++) {
30367
- const pkg = path54.join(dir, "package.json");
30587
+ const pkg = path55.join(dir, "package.json");
30368
30588
  try {
30369
30589
  const parsed = JSON.parse(readFileSync35(pkg, "utf8"));
30370
30590
  if (parsed.version) return parsed.version;
30371
30591
  } catch {
30372
30592
  }
30373
- dir = path54.dirname(dir);
30593
+ dir = path55.dirname(dir);
30374
30594
  }
30375
30595
  return "unknown";
30376
30596
  }
@@ -30406,7 +30626,7 @@ var init_exe_support = __esm({
30406
30626
  });
30407
30627
 
30408
30628
  // src/mcp/tools/support.ts
30409
- import { z as z89 } from "zod";
30629
+ import { z as z90 } from "zod";
30410
30630
  function formatRows(rows, mode) {
30411
30631
  const lines = [`exe-os support ${mode}`, ""];
30412
30632
  for (const row of rows) {
@@ -30447,7 +30667,7 @@ function registerSupportTools(server) {
30447
30667
  title: "Support Test",
30448
30668
  description: "End-to-end support intake smoke test. Files a clearly marked test report upstream and auto-closes it on AskExe machines with admin credentials.",
30449
30669
  inputSchema: {
30450
- project: z89.string().default("support-smoke").describe("Customer/project name, e.g. hygo")
30670
+ project: z90.string().default("support-smoke").describe("Customer/project name, e.g. hygo")
30451
30671
  }
30452
30672
  },
30453
30673
  async ({ project }) => {
@@ -30465,8 +30685,8 @@ function registerSupportTools(server) {
30465
30685
  title: "My Bug Reports",
30466
30686
  description: "List bug reports you've filed, scoped to your license. Shows status (open/triaged/fixed/closed), severity, and fixed_version so you know when to update.",
30467
30687
  inputSchema: {
30468
- status: z89.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("all").describe("Filter by status. Default: all"),
30469
- limit: z89.number().min(1).max(50).default(25).describe("Max results")
30688
+ status: z90.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("all").describe("Filter by status. Default: all"),
30689
+ limit: z90.number().min(1).max(50).default(25).describe("Max results")
30470
30690
  }
30471
30691
  },
30472
30692
  async ({ status: status2, limit }) => {
@@ -30522,6 +30742,70 @@ function registerSupportTools(server) {
30522
30742
  }
30523
30743
  }
30524
30744
  );
30745
+ server.registerTool(
30746
+ "list_my_feature_requests",
30747
+ {
30748
+ title: "My Feature Requests",
30749
+ description: "List feature requests you've filed, scoped to your license. Shows status (open/planned/in_progress/shipped/closed), priority, and shipped_version so you know when to update.",
30750
+ inputSchema: {
30751
+ status: z90.enum(["all", "open", "planned", "in_progress", "shipped", "closed", "wontdo"]).default("all").describe("Filter by status. Default: all"),
30752
+ limit: z90.number().min(1).max(50).default(25).describe("Max results")
30753
+ }
30754
+ },
30755
+ async ({ status: status2, limit }) => {
30756
+ const licenseKey = loadLicense();
30757
+ const licenseToken = readCachedLicenseToken();
30758
+ if (!licenseKey && !licenseToken) {
30759
+ return {
30760
+ content: [{ type: "text", text: "No license key found. Run `exe-os setup` or `exe-os cloud setup` first." }],
30761
+ isError: true
30762
+ };
30763
+ }
30764
+ const endpoint2 = new URL("https://askexe.com/v1/support/my-feature-requests");
30765
+ endpoint2.searchParams.set("status", status2);
30766
+ endpoint2.searchParams.set("limit", String(limit));
30767
+ const headers = { "content-type": "application/json" };
30768
+ if (licenseKey) headers["x-exe-license-key"] = licenseKey;
30769
+ if (licenseToken) headers["x-exe-license-token"] = licenseToken;
30770
+ try {
30771
+ const res = await fetch(endpoint2.toString(), { method: "GET", headers, signal: AbortSignal.timeout(15e3) });
30772
+ if (!res.ok) {
30773
+ const body = await res.text().catch(() => "");
30774
+ return {
30775
+ content: [{ type: "text", text: `Failed to fetch feature requests: HTTP ${res.status}${body ? ` \u2014 ${body}` : ""}` }],
30776
+ isError: true
30777
+ };
30778
+ }
30779
+ const data = await res.json();
30780
+ if (data.count === 0) {
30781
+ return {
30782
+ content: [{ type: "text", text: `No feature requests found${status2 !== "all" ? ` with status '${status2}'` : ""}.` }],
30783
+ structuredContent: { items: [], count: 0 }
30784
+ };
30785
+ }
30786
+ const lines = [`Feature requests (${data.count}):`, ""];
30787
+ for (const r of data.items) {
30788
+ const priIcon = r.priority === "p0" ? "\u{1F534}" : r.priority === "p1" ? "\u{1F534}" : r.priority === "p2" ? "\u{1F7E0}" : "\u{1F7E2}";
30789
+ const statusIcon = r.status === "shipped" ? "\u2705" : r.status === "closed" ? "\u2611\uFE0F" : r.status === "planned" ? "\u{1F4CB}" : r.status === "in_progress" ? "\u{1F528}" : r.status === "wontdo" ? "\u26D4" : "\u{1F535}";
30790
+ lines.push(`${priIcon} ${r.priority.toUpperCase()} ${statusIcon} ${r.status} \u2014 ${r.title}`);
30791
+ lines.push(` ID: ${r.id} | Filed: ${r.created_at?.slice(0, 10) ?? "?"}`);
30792
+ if (r.shipped_version) lines.push(` \u{1F680} Shipped in: ${r.shipped_version} \u2014 run \`exe-os update\` to get this feature`);
30793
+ if (r.target_version) lines.push(` \u{1F3AF} Target: ${r.target_version}`);
30794
+ if (r.response_notes) lines.push(` \u{1F4DD} AskExe: ${r.response_notes}`);
30795
+ lines.push("");
30796
+ }
30797
+ return {
30798
+ content: [{ type: "text", text: lines.join("\n") }],
30799
+ structuredContent: data
30800
+ };
30801
+ } catch (err) {
30802
+ return {
30803
+ content: [{ type: "text", text: `Error fetching feature requests: ${err instanceof Error ? err.message : String(err)}` }],
30804
+ isError: true
30805
+ };
30806
+ }
30807
+ }
30808
+ );
30525
30809
  }
30526
30810
  var init_support = __esm({
30527
30811
  "src/mcp/tools/support.ts"() {
@@ -30596,21 +30880,21 @@ var init_exe_status = __esm({
30596
30880
 
30597
30881
  // src/bin/exe-healthcheck.ts
30598
30882
  import { existsSync as existsSync43, readFileSync as readFileSync36, readdirSync as readdirSync14 } from "fs";
30599
- import path55 from "path";
30883
+ import path56 from "path";
30600
30884
  import { execSync as execSync15 } from "child_process";
30601
30885
  import { fileURLToPath as fileURLToPath6 } from "url";
30602
30886
  function findPackageRoot2() {
30603
- let dir = path55.dirname(fileURLToPath6(import.meta.url));
30604
- const { root } = path55.parse(dir);
30887
+ let dir = path56.dirname(fileURLToPath6(import.meta.url));
30888
+ const { root } = path56.parse(dir);
30605
30889
  while (dir !== root) {
30606
- if (existsSync43(path55.join(dir, "package.json"))) return dir;
30607
- dir = path55.dirname(dir);
30890
+ if (existsSync43(path56.join(dir, "package.json"))) return dir;
30891
+ dir = path56.dirname(dir);
30608
30892
  }
30609
30893
  throw new Error("Cannot find package root");
30610
30894
  }
30611
30895
  function checkBuildIntegrity(pkgRoot) {
30612
30896
  const results = [];
30613
- const tsupConfig = path55.join(pkgRoot, "tsup.config.ts");
30897
+ const tsupConfig = path56.join(pkgRoot, "tsup.config.ts");
30614
30898
  if (!existsSync43(tsupConfig)) {
30615
30899
  return [{ name: "build/tsup-config", pass: false, detail: "tsup.config.ts not found" }];
30616
30900
  }
@@ -30620,7 +30904,7 @@ function checkBuildIntegrity(pkgRoot) {
30620
30904
  let total = 0;
30621
30905
  for (const match of entryMatches) {
30622
30906
  const outputKey = match[1];
30623
- const expectedPath = path55.join(pkgRoot, "dist", `${outputKey}.js`);
30907
+ const expectedPath = path56.join(pkgRoot, "dist", `${outputKey}.js`);
30624
30908
  total++;
30625
30909
  if (!existsSync43(expectedPath)) {
30626
30910
  missing.push(`dist/${outputKey}.js`);
@@ -30644,7 +30928,7 @@ function checkBuildIntegrity(pkgRoot) {
30644
30928
  }
30645
30929
  function checkEmbedPipeline(pkgRoot) {
30646
30930
  const results = [];
30647
- const daemonPath = path55.join(pkgRoot, "dist", "lib", "exe-daemon.js");
30931
+ const daemonPath = path56.join(pkgRoot, "dist", "lib", "exe-daemon.js");
30648
30932
  if (!existsSync43(daemonPath)) {
30649
30933
  results.push({
30650
30934
  name: "exed/daemon-exists",
@@ -30656,17 +30940,17 @@ function checkEmbedPipeline(pkgRoot) {
30656
30940
  results.push({ name: "exed/daemon-exists", pass: true, detail: "dist/lib/exe-daemon.js exists" });
30657
30941
  const entryDirs = ["dist/hooks", "dist/bin", "dist/mcp"];
30658
30942
  for (const dir of entryDirs) {
30659
- const fullDir = path55.join(pkgRoot, dir);
30943
+ const fullDir = path56.join(pkgRoot, dir);
30660
30944
  if (!existsSync43(fullDir)) continue;
30661
30945
  let walkDir = fullDir;
30662
- const { root } = path55.parse(walkDir);
30946
+ const { root } = path56.parse(walkDir);
30663
30947
  let foundRoot = null;
30664
30948
  while (walkDir !== root) {
30665
- if (existsSync43(path55.join(walkDir, "package.json"))) {
30949
+ if (existsSync43(path56.join(walkDir, "package.json"))) {
30666
30950
  foundRoot = walkDir;
30667
30951
  break;
30668
30952
  }
30669
- walkDir = path55.dirname(walkDir);
30953
+ walkDir = path56.dirname(walkDir);
30670
30954
  }
30671
30955
  if (!foundRoot) {
30672
30956
  results.push({
@@ -30676,7 +30960,7 @@ function checkEmbedPipeline(pkgRoot) {
30676
30960
  });
30677
30961
  continue;
30678
30962
  }
30679
- const resolvedDaemon = path55.join(foundRoot, "dist", "lib", "exe-daemon.js");
30963
+ const resolvedDaemon = path56.join(foundRoot, "dist", "lib", "exe-daemon.js");
30680
30964
  const reachable = existsSync43(resolvedDaemon);
30681
30965
  results.push({
30682
30966
  name: `exed/reachable-from-${dir}`,
@@ -30688,7 +30972,7 @@ function checkEmbedPipeline(pkgRoot) {
30688
30972
  }
30689
30973
  function checkTaskSystem(pkgRoot) {
30690
30974
  const results = [];
30691
- const scannerPath = path55.join(pkgRoot, "dist", "bin", "scan-tasks.js");
30975
+ const scannerPath = path56.join(pkgRoot, "dist", "bin", "scan-tasks.js");
30692
30976
  if (!existsSync43(scannerPath)) {
30693
30977
  results.push({ name: "tasks/scanner", pass: false, detail: "scan-tasks.js not found" });
30694
30978
  return results;
@@ -30711,7 +30995,7 @@ function checkTaskSystem(pkgRoot) {
30711
30995
  }
30712
30996
  function checkWorkerSpawning(pkgRoot) {
30713
30997
  const results = [];
30714
- const workerPath = path55.join(pkgRoot, "dist", "hooks", "ingest-worker.js");
30998
+ const workerPath = path56.join(pkgRoot, "dist", "hooks", "ingest-worker.js");
30715
30999
  if (!existsSync43(workerPath)) {
30716
31000
  results.push({ name: "workers/ingest-worker", pass: false, detail: "ingest-worker.js not found" });
30717
31001
  return results;
@@ -30726,14 +31010,14 @@ function checkWorkerSpawning(pkgRoot) {
30726
31010
  detail: `Parse error: ${err instanceof Error ? err.message.slice(0, 200) : String(err)}`
30727
31011
  });
30728
31012
  }
30729
- const hooksDir = path55.join(pkgRoot, "dist", "hooks");
31013
+ const hooksDir = path56.join(pkgRoot, "dist", "hooks");
30730
31014
  if (existsSync43(hooksDir)) {
30731
31015
  const hookFiles = readdirSync14(hooksDir).filter((f) => f.endsWith(".js") && !f.endsWith(".js.map"));
30732
31016
  let hooksPassed = 0;
30733
31017
  const hooksFailed = [];
30734
31018
  for (const hook of hookFiles) {
30735
31019
  try {
30736
- execSync15(`node --check "${path55.join(hooksDir, hook)}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
31020
+ execSync15(`node --check "${path56.join(hooksDir, hook)}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
30737
31021
  hooksPassed++;
30738
31022
  } catch {
30739
31023
  hooksFailed.push(hook);
@@ -30757,8 +31041,8 @@ function checkWorkerSpawning(pkgRoot) {
30757
31041
  }
30758
31042
  function checkMcpTransport() {
30759
31043
  const results = [];
30760
- const pidPath = path55.join(EXE_AI_DIR, "exed.pid");
30761
- const tokenPath = path55.join(EXE_AI_DIR, "exed.token");
31044
+ const pidPath = path56.join(EXE_AI_DIR, "exed.pid");
31045
+ const tokenPath = path56.join(EXE_AI_DIR, "exed.token");
30762
31046
  let daemonAlive = false;
30763
31047
  if (existsSync43(pidPath)) {
30764
31048
  try {
@@ -30805,7 +31089,7 @@ function checkMcpTransport() {
30805
31089
  results.push({
30806
31090
  name: "mcp/monitor-summary",
30807
31091
  pass: true,
30808
- detail: `Privacy-safe local monitor summary: ${path55.join(EXE_AI_DIR, "monitor", "mcp-transport-summary.json")}`
31092
+ detail: `Privacy-safe local monitor summary: ${path56.join(EXE_AI_DIR, "monitor", "mcp-transport-summary.json")}`
30809
31093
  });
30810
31094
  return results;
30811
31095
  }
@@ -30863,7 +31147,7 @@ function checkClaudeCodeInstall() {
30863
31147
  detail: "Failed to check claude binary path"
30864
31148
  });
30865
31149
  }
30866
- const versionsDir = path55.join(
31150
+ const versionsDir = path56.join(
30867
31151
  process.env.HOME ?? process.env.USERPROFILE ?? "",
30868
31152
  ".local",
30869
31153
  "share",
@@ -30984,9 +31268,9 @@ __export(update_check_exports, {
30984
31268
  });
30985
31269
  import { execSync as execSync16 } from "child_process";
30986
31270
  import { readFileSync as readFileSync37 } from "fs";
30987
- import path56 from "path";
31271
+ import path57 from "path";
30988
31272
  function getLocalVersion(packageRoot) {
30989
- const pkgPath = path56.join(packageRoot, "package.json");
31273
+ const pkgPath = path57.join(packageRoot, "package.json");
30990
31274
  const pkg = JSON.parse(readFileSync37(pkgPath, "utf-8"));
30991
31275
  return pkg.version;
30992
31276
  }
@@ -31026,7 +31310,7 @@ var init_update_check = __esm({
31026
31310
  // src/mcp/tools/cli-parity.ts
31027
31311
  import { execFile as execFile2 } from "child_process";
31028
31312
  import { promisify as promisify2 } from "util";
31029
- import { z as z90 } from "zod";
31313
+ import { z as z91 } from "zod";
31030
31314
  async function runCommand(command, args, timeout = 6e4) {
31031
31315
  const printable = [command, ...args].join(" ");
31032
31316
  try {
@@ -31061,12 +31345,12 @@ function registerCliParityTools(server) {
31061
31345
  title: "Doctor",
31062
31346
  description: "Run exe-os doctor audit. Defaults to read-only diagnostics; optional dry-run/fix flags mirror CLI.",
31063
31347
  inputSchema: {
31064
- agent: z90.string().optional(),
31065
- project: z90.string().optional(),
31066
- verbose: z90.boolean().default(false),
31067
- conflicts: z90.boolean().default(false),
31068
- dry_run: z90.boolean().default(false),
31069
- fix: z90.boolean().default(false)
31348
+ agent: z91.string().optional(),
31349
+ project: z91.string().optional(),
31350
+ verbose: z91.boolean().default(false),
31351
+ conflicts: z91.boolean().default(false),
31352
+ dry_run: z91.boolean().default(false),
31353
+ fix: z91.boolean().default(false)
31070
31354
  }
31071
31355
  }, async ({ agent, project, verbose, conflicts, dry_run, fix }) => {
31072
31356
  const args = [];
@@ -31083,9 +31367,9 @@ function registerCliParityTools(server) {
31083
31367
  title: "Rename Employee",
31084
31368
  description: "Rename an employee using the same path as `exe-os rename <old> <new>`. Use for customer roster/identity renames.",
31085
31369
  inputSchema: {
31086
- old_name: z90.string().min(1),
31087
- new_name: z90.string().min(1),
31088
- dry_run: z90.boolean().default(false)
31370
+ old_name: z91.string().min(1),
31371
+ new_name: z91.string().min(1),
31372
+ dry_run: z91.boolean().default(false)
31089
31373
  }
31090
31374
  }, async ({ old_name, new_name, dry_run }) => {
31091
31375
  if (dry_run) {
@@ -31098,7 +31382,7 @@ function registerCliParityTools(server) {
31098
31382
  server.registerTool("status_brief", {
31099
31383
  title: "Status Brief",
31100
31384
  description: "Return current employee/tmux status. Mirrors `exe-status` and supports optional deep view for one employee.",
31101
- inputSchema: { employee: z90.string().optional() }
31385
+ inputSchema: { employee: z91.string().optional() }
31102
31386
  }, async ({ employee }) => {
31103
31387
  const text3 = await status(employee);
31104
31388
  return result2(text3, { ok: true, employee: employee ?? null });
@@ -31106,7 +31390,7 @@ function registerCliParityTools(server) {
31106
31390
  server.registerTool("pending_work_summary", {
31107
31391
  title: "Pending Work Summary",
31108
31392
  description: "Return pending reviews, messages, and notifications using the same summaries as the CLI tools.",
31109
- inputSchema: { agent: z90.string().optional() }
31393
+ inputSchema: { agent: z91.string().optional() }
31110
31394
  }, async ({ agent }) => {
31111
31395
  const parts = await Promise.all([
31112
31396
  runCommand("exe-pending-reviews", [], 3e4),
@@ -31127,7 +31411,7 @@ function registerCliParityTools(server) {
31127
31411
  server.registerTool("key_rotation_preflight", {
31128
31412
  title: "Key Rotation Preflight",
31129
31413
  description: "Dry-run key rotation/update preflight. No destructive changes.",
31130
- inputSchema: { mode: z90.enum(["rotate", "update"]).default("rotate") }
31414
+ inputSchema: { mode: z91.enum(["rotate", "update"]).default("rotate") }
31131
31415
  }, async ({ mode }) => {
31132
31416
  const out = await runCommand("exe-os", ["key", mode, "--dry-run"], 6e4);
31133
31417
  return result2(out.text, { ok: out.ok, command: out.command, mode }, !out.ok);
@@ -31146,11 +31430,11 @@ function registerCliParityTools(server) {
31146
31430
  title: "Stack Update Check",
31147
31431
  description: "Plan/check a customer stack update without applying Docker changes. Mirrors `exe-os stack-update --check`.",
31148
31432
  inputSchema: {
31149
- target: z90.string().optional(),
31150
- manifest: z90.string().optional(),
31151
- compose_file: z90.string().optional(),
31152
- env_file: z90.string().optional(),
31153
- deployment_persona: z90.enum(["customer", "askexe-control-plane"]).default("customer")
31433
+ target: z91.string().optional(),
31434
+ manifest: z91.string().optional(),
31435
+ compose_file: z91.string().optional(),
31436
+ env_file: z91.string().optional(),
31437
+ deployment_persona: z91.enum(["customer", "askexe-control-plane"]).default("customer")
31154
31438
  }
31155
31439
  }, async ({ target, manifest, compose_file, env_file, deployment_persona }) => {
31156
31440
  const args = ["stack-update", "--check", "--deployment-persona", deployment_persona];
@@ -31271,7 +31555,7 @@ var init_session_events = __esm({
31271
31555
  });
31272
31556
 
31273
31557
  // src/mcp/tools/get-session-events.ts
31274
- import { z as z91 } from "zod";
31558
+ import { z as z92 } from "zod";
31275
31559
  function canReadAgent(activeRole, activeAgent, requestedAgent) {
31276
31560
  return requestedAgent === activeAgent || activeRole === "COO" || activeRole === "CTO";
31277
31561
  }
@@ -31295,11 +31579,11 @@ function registerGetSessionEvents(server) {
31295
31579
  title: "Get Session Events",
31296
31580
  description: "Return exact recent chronological user prompts, assistant responses, and tool calls from the append-only session journal. Use this when asked what happened last, not semantic memory search.",
31297
31581
  inputSchema: {
31298
- agent_id: z91.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
31299
- session_id: z91.string().optional().describe("Optional exact runtime session id."),
31582
+ agent_id: z92.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
31583
+ session_id: z92.string().optional().describe("Optional exact runtime session id."),
31300
31584
  event_type: EVENT_TYPE.optional().describe("Filter to one event type."),
31301
- project_name: z91.string().optional().describe("Optional project filter. Pass 'all' for all projects."),
31302
- limit: z91.number().int().min(1).max(100).default(20).describe("Number of events to return.")
31585
+ project_name: z92.string().optional().describe("Optional project filter. Pass 'all' for all projects."),
31586
+ limit: z92.number().int().min(1).max(100).default(20).describe("Number of events to return.")
31303
31587
  }
31304
31588
  },
31305
31589
  async ({ agent_id, session_id, event_type, project_name, limit }) => {
@@ -31335,8 +31619,8 @@ function registerGetLastAssistantResponse(server) {
31335
31619
  title: "Get Last Assistant Response",
31336
31620
  description: "Return the exact last assistant response for an agent from the session event journal. Use for 'what was the last thing you said?'",
31337
31621
  inputSchema: {
31338
- agent_id: z91.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
31339
- project_name: z91.string().optional().describe("Optional project filter. Pass 'all' for all projects.")
31622
+ agent_id: z92.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
31623
+ project_name: z92.string().optional().describe("Optional project filter. Pass 'all' for all projects.")
31340
31624
  }
31341
31625
  },
31342
31626
  async ({ agent_id, project_name }) => {
@@ -31371,7 +31655,7 @@ var init_get_session_events = __esm({
31371
31655
  init_active_agent();
31372
31656
  init_fast_db_init();
31373
31657
  init_session_events();
31374
- EVENT_TYPE = z91.enum([
31658
+ EVENT_TYPE = z92.enum([
31375
31659
  "user_prompt",
31376
31660
  "assistant_response",
31377
31661
  "tool_call",
@@ -31382,25 +31666,96 @@ var init_get_session_events = __esm({
31382
31666
  });
31383
31667
 
31384
31668
  // src/lib/code-context-index.ts
31385
- import crypto20 from "crypto";
31386
- import path57 from "path";
31669
+ import crypto21 from "crypto";
31670
+ import path58 from "path";
31387
31671
  import { existsSync as existsSync44, mkdirSync as mkdirSync22, readFileSync as readFileSync38, readdirSync as readdirSync15, statSync as statSync10, writeFileSync as writeFileSync25 } from "fs";
31388
31672
  import { spawnSync } from "child_process";
31673
+ function vectorStorePath(projectRoot) {
31674
+ const rootHash = hashText(projectRoot).slice(0, 16);
31675
+ return path58.join(indexDir(), `${rootHash}.vectors.json`);
31676
+ }
31677
+ function loadVectorStore(projectRoot) {
31678
+ const file = vectorStorePath(projectRoot);
31679
+ if (!existsSync44(file)) return null;
31680
+ try {
31681
+ const parsed = JSON.parse(readFileSync38(file, "utf8"));
31682
+ if (parsed.version !== VECTOR_STORE_VERSION) return null;
31683
+ return parsed;
31684
+ } catch {
31685
+ return null;
31686
+ }
31687
+ }
31688
+ function saveVectorStore(projectRoot, store) {
31689
+ writeFileSync25(vectorStorePath(projectRoot), JSON.stringify(store));
31690
+ }
31691
+ function cosineSimilarity3(a, b) {
31692
+ let dot = 0, normA = 0, normB = 0;
31693
+ for (let i = 0; i < a.length; i++) {
31694
+ dot += a[i] * b[i];
31695
+ normA += a[i] * a[i];
31696
+ normB += b[i] * b[i];
31697
+ }
31698
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
31699
+ return denom === 0 ? 0 : dot / denom;
31700
+ }
31701
+ async function embedSymbols(index) {
31702
+ const rootHash = hashText(index.projectRoot).slice(0, 16);
31703
+ const existing = loadVectorStore(index.projectRoot);
31704
+ const store = {
31705
+ version: VECTOR_STORE_VERSION,
31706
+ projectRootHash: rootHash,
31707
+ vectors: {}
31708
+ };
31709
+ const allSymbols = [];
31710
+ for (const file of Object.values(index.files)) {
31711
+ for (const symbol of file.symbols) {
31712
+ if (existing?.vectors[symbol.id]) {
31713
+ store.vectors[symbol.id] = existing.vectors[symbol.id];
31714
+ } else {
31715
+ allSymbols.push({ id: symbol.id, text: symbol.summary || `${symbol.kind} ${symbol.name} in ${symbol.filePath}` });
31716
+ }
31717
+ }
31718
+ }
31719
+ if (allSymbols.length === 0) {
31720
+ saveVectorStore(index.projectRoot, store);
31721
+ return store;
31722
+ }
31723
+ const connected = await connectEmbedDaemon().catch(() => false);
31724
+ if (!connected) {
31725
+ saveVectorStore(index.projectRoot, store);
31726
+ return store;
31727
+ }
31728
+ for (let i = 0; i < allSymbols.length; i += EMBED_BATCH_SIZE) {
31729
+ const batch = allSymbols.slice(i, i + EMBED_BATCH_SIZE);
31730
+ const texts = batch.map((s) => s.text);
31731
+ try {
31732
+ const vectors = await embedBatchViaClient(texts, "low");
31733
+ if (vectors && vectors.length === batch.length) {
31734
+ for (let j = 0; j < batch.length; j++) {
31735
+ store.vectors[batch[j].id] = vectors[j];
31736
+ }
31737
+ }
31738
+ } catch {
31739
+ }
31740
+ }
31741
+ saveVectorStore(index.projectRoot, store);
31742
+ return store;
31743
+ }
31389
31744
  function normalizeProjectRoot(projectRoot) {
31390
- return path57.resolve(projectRoot || process.cwd());
31745
+ return path58.resolve(projectRoot || process.cwd());
31391
31746
  }
31392
31747
  function hashText(text3) {
31393
- return crypto20.createHash("sha256").update(text3).digest("hex");
31748
+ return crypto21.createHash("sha256").update(text3).digest("hex");
31394
31749
  }
31395
31750
  function indexDir() {
31396
- const dir = path57.join(EXE_AI_DIR, "code-context");
31751
+ const dir = path58.join(EXE_AI_DIR, "code-context");
31397
31752
  mkdirSync22(dir, { recursive: true });
31398
31753
  return dir;
31399
31754
  }
31400
31755
  function getCodeContextIndexPath(projectRoot) {
31401
31756
  const root = normalizeProjectRoot(projectRoot);
31402
31757
  const rootHash = hashText(root).slice(0, 16);
31403
- return path57.join(indexDir(), `${rootHash}.json`);
31758
+ return path58.join(indexDir(), `${rootHash}.json`);
31404
31759
  }
31405
31760
  function currentBranch(projectRoot) {
31406
31761
  const result3 = spawnSync("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
@@ -31413,8 +31768,8 @@ function shouldIgnore(relPath) {
31413
31768
  }
31414
31769
  function listRecursive(projectRoot, dir = projectRoot, out = []) {
31415
31770
  for (const entry of readdirSync15(dir, { withFileTypes: true })) {
31416
- const abs = path57.join(dir, entry.name);
31417
- const rel = path57.relative(projectRoot, abs).replaceAll(path57.sep, "/");
31771
+ const abs = path58.join(dir, entry.name);
31772
+ const rel = path58.relative(projectRoot, abs).replaceAll(path58.sep, "/");
31418
31773
  if (shouldIgnore(rel)) continue;
31419
31774
  if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
31420
31775
  else if (entry.isFile()) out.push(rel);
@@ -31430,7 +31785,7 @@ function listCodeFiles(projectRoot, maxFiles) {
31430
31785
  const rg = spawnSync("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
31431
31786
  files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
31432
31787
  }
31433
- return files.map((file) => file.replaceAll(path57.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
31788
+ return files.map((file) => file.replaceAll(path58.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
31434
31789
  }
31435
31790
  function parseImportPaths2(importText) {
31436
31791
  const paths = [];
@@ -31443,13 +31798,13 @@ function parseImportPaths2(importText) {
31443
31798
  }
31444
31799
  function resolveImport(fromFile, importPath, allFiles) {
31445
31800
  if (!importPath.startsWith(".")) return null;
31446
- const base = path57.posix.normalize(path57.posix.join(path57.posix.dirname(fromFile.replaceAll(path57.sep, "/")), importPath));
31801
+ const base = path58.posix.normalize(path58.posix.join(path58.posix.dirname(fromFile.replaceAll(path58.sep, "/")), importPath));
31447
31802
  const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
31448
31803
  const candidates = [base];
31449
31804
  for (const ext of ["ts", "tsx", "js", "jsx", "py", "rs", "go", "java", "cs", "cpp", "c", "rb", "php", "swift", "kt", "scala", "sql", "md", "json", "yaml", "yml"]) {
31450
31805
  candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
31451
31806
  }
31452
- for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path57.posix.join(base, indexName));
31807
+ for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path58.posix.join(base, indexName));
31453
31808
  return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
31454
31809
  }
31455
31810
  function symbolId(filePath, chunk) {
@@ -31470,7 +31825,7 @@ function saveIndex(index) {
31470
31825
  writeFileSync25(getCodeContextIndexPath(index.projectRoot), JSON.stringify(index, null, 2));
31471
31826
  }
31472
31827
  function buildFileRecord(projectRoot, relPath, allFiles, previous) {
31473
- const absPath = path57.join(projectRoot, relPath);
31828
+ const absPath = path58.join(projectRoot, relPath);
31474
31829
  let stat;
31475
31830
  try {
31476
31831
  stat = statSync10(absPath);
@@ -31508,13 +31863,13 @@ function buildCodeContextIndex(options = {}) {
31508
31863
  const branch = currentBranch(projectRoot);
31509
31864
  const previous = options.force ? null : loadIndex(projectRoot);
31510
31865
  const files = listCodeFiles(projectRoot, maxFiles);
31511
- const allFiles = new Set(files.map((file) => file.replaceAll(path57.sep, "/")));
31866
+ const allFiles = new Set(files.map((file) => file.replaceAll(path58.sep, "/")));
31512
31867
  const fileRecords = {};
31513
31868
  let rebuiltFiles = 0;
31514
31869
  let reusedFiles = 0;
31515
31870
  let skippedFiles = 0;
31516
31871
  for (const rel of files) {
31517
- const normalized = rel.replaceAll(path57.sep, "/");
31872
+ const normalized = rel.replaceAll(path58.sep, "/");
31518
31873
  const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
31519
31874
  if (record) {
31520
31875
  fileRecords[normalized] = record;
@@ -31543,11 +31898,11 @@ function loadOrBuildCodeContextIndex(options = {}) {
31543
31898
  if (loaded) {
31544
31899
  const currentFiles = listCodeFiles(projectRoot, options.maxFiles ?? DEFAULT_MAX_FILES);
31545
31900
  const unchanged = currentFiles.every((rel) => {
31546
- const normalized = rel.replaceAll(path57.sep, "/");
31901
+ const normalized = rel.replaceAll(path58.sep, "/");
31547
31902
  const existing = loaded.files[normalized];
31548
31903
  if (!existing) return false;
31549
31904
  try {
31550
- const stat = statSync10(path57.join(projectRoot, normalized));
31905
+ const stat = statSync10(path58.join(projectRoot, normalized));
31551
31906
  return stat.mtimeMs === existing.mtimeMs && stat.size === existing.size;
31552
31907
  } catch {
31553
31908
  return false;
@@ -31600,9 +31955,9 @@ function globToRegex(pattern) {
31600
31955
  }
31601
31956
  function matchesPath(filePath, patterns) {
31602
31957
  if (!patterns || patterns.length === 0) return true;
31603
- const normalized = filePath.replaceAll(path57.sep, "/");
31958
+ const normalized = filePath.replaceAll(path58.sep, "/");
31604
31959
  return patterns.some((pattern) => {
31605
- const p = pattern.replaceAll(path57.sep, "/").replace(/^\.\//, "");
31960
+ const p = pattern.replaceAll(path58.sep, "/").replace(/^\.\//, "");
31606
31961
  return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
31607
31962
  });
31608
31963
  }
@@ -31661,7 +32016,7 @@ function filteredFiles(index, options = {}) {
31661
32016
  return matchesPath(file.path, options.paths);
31662
32017
  });
31663
32018
  }
31664
- function searchCodeContext(query, options = {}) {
32019
+ function lexicalSearch(query, options = {}) {
31665
32020
  const terms = tokenize(query);
31666
32021
  if (terms.length === 0) return [];
31667
32022
  const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
@@ -31687,6 +32042,81 @@ function searchCodeContext(query, options = {}) {
31687
32042
  const limit = options.limit ?? 20;
31688
32043
  return results.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
31689
32044
  }
32045
+ function searchCodeContext(query, options = {}) {
32046
+ return lexicalSearch(query, options);
32047
+ }
32048
+ async function searchCodeContextSemantic(query, options = {}) {
32049
+ const terms = tokenize(query);
32050
+ if (terms.length === 0) return [];
32051
+ const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
32052
+ const projectRoot = normalizeProjectRoot(options.projectRoot);
32053
+ const vectorStore = loadVectorStore(projectRoot);
32054
+ if (!vectorStore || Object.keys(vectorStore.vectors).length === 0) {
32055
+ return lexicalSearch(query, options);
32056
+ }
32057
+ let queryVector = null;
32058
+ try {
32059
+ const connected = await connectEmbedDaemon().catch(() => false);
32060
+ if (connected) {
32061
+ const result3 = await embedBatchViaClient([query], "high");
32062
+ if (result3 && result3.length === 1) queryVector = result3[0];
32063
+ }
32064
+ } catch {
32065
+ }
32066
+ if (!queryVector) return lexicalSearch(query, options);
32067
+ const files = filteredFiles(index, options);
32068
+ const candidates = [];
32069
+ for (const file of files) {
32070
+ for (const symbol of file.symbols) {
32071
+ const lexical = scoreSymbol(symbol, terms);
32072
+ const vec = vectorStore.vectors[symbol.id];
32073
+ const vectorScore = vec ? cosineSimilarity3(queryVector, vec) : 0;
32074
+ if (lexical.score > 0 || vectorScore > 0.3) {
32075
+ candidates.push({
32076
+ symbol,
32077
+ lexicalScore: lexical.score,
32078
+ vectorScore,
32079
+ matches: lexical.matches
32080
+ });
32081
+ }
32082
+ }
32083
+ }
32084
+ const byLexical = [...candidates].sort((a, b) => b.lexicalScore - a.lexicalScore);
32085
+ const byVector = [...candidates].sort((a, b) => b.vectorScore - a.vectorScore);
32086
+ const lexicalRank = /* @__PURE__ */ new Map();
32087
+ const vectorRank = /* @__PURE__ */ new Map();
32088
+ byLexical.forEach((c, i) => lexicalRank.set(c.symbol.id, i + 1));
32089
+ byVector.forEach((c, i) => vectorRank.set(c.symbol.id, i + 1));
32090
+ const VECTOR_WEIGHT = 0.6;
32091
+ const LEXICAL_WEIGHT = 0.4;
32092
+ const fused = candidates.map((c) => {
32093
+ const lRank = lexicalRank.get(c.symbol.id) ?? candidates.length + 1;
32094
+ const vRank = vectorRank.get(c.symbol.id) ?? candidates.length + 1;
32095
+ const rrfScore = VECTOR_WEIGHT * (1 / (RRF_K2 + vRank)) + LEXICAL_WEIGHT * (1 / (RRF_K2 + lRank));
32096
+ return {
32097
+ symbol: c.symbol,
32098
+ score: rrfScore,
32099
+ matches: c.vectorScore > 0.3 ? [...c.matches, `semantic=${c.vectorScore.toFixed(3)}`] : c.matches,
32100
+ filePath: c.symbol.filePath,
32101
+ language: c.symbol.language,
32102
+ content: c.symbol.text,
32103
+ startLine: c.symbol.startLine,
32104
+ endLine: c.symbol.endLine
32105
+ };
32106
+ });
32107
+ const offset = Math.max(0, options.offset ?? 0);
32108
+ const limit = options.limit ?? 20;
32109
+ return fused.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
32110
+ }
32111
+ async function buildCodeContextIndexWithEmbeddings(options = {}) {
32112
+ const index = buildCodeContextIndex(options);
32113
+ const existingStore = loadVectorStore(index.projectRoot);
32114
+ const existingCount = existingStore ? Object.keys(existingStore.vectors).length : 0;
32115
+ const store = await embedSymbols(index);
32116
+ const vectorCount = Object.keys(store.vectors).length;
32117
+ const newEmbeddings = vectorCount - Math.min(existingCount, vectorCount);
32118
+ return { index, vectorCount, newEmbeddings };
32119
+ }
31690
32120
  function dependentsMap(index) {
31691
32121
  const map = /* @__PURE__ */ new Map();
31692
32122
  for (const file of Object.values(index.files)) {
@@ -31720,7 +32150,7 @@ function traceCodeSymbol(symbolName, options = {}) {
31720
32150
  }
31721
32151
  function resolveTargetFile(index, input) {
31722
32152
  if (input.filePath) {
31723
- const normalized = input.filePath.replaceAll(path57.sep, "/").replace(/^\.\//, "");
32153
+ const normalized = input.filePath.replaceAll(path58.sep, "/").replace(/^\.\//, "");
31724
32154
  if (index.files[normalized]) return { filePath: normalized, target: normalized };
31725
32155
  const suffix = Object.keys(index.files).find((file) => file.endsWith(normalized));
31726
32156
  if (suffix) return { filePath: suffix, target: input.filePath };
@@ -31750,7 +32180,7 @@ function analyzeBlastRadius(input) {
31750
32180
  }
31751
32181
  }
31752
32182
  }
31753
- const targetBase = path57.basename(target.filePath).replace(/\.[^.]+$/, "").toLowerCase();
32183
+ const targetBase = path58.basename(target.filePath).replace(/\.[^.]+$/, "").toLowerCase();
31754
32184
  const symbolLower = input.symbol?.toLowerCase();
31755
32185
  const tests = Object.keys(index.files).filter((file) => {
31756
32186
  const lower = file.toLowerCase();
@@ -31778,20 +32208,24 @@ function getCodeContextStats(options = {}) {
31778
32208
  indexPath: getCodeContextIndexPath(index.projectRoot)
31779
32209
  };
31780
32210
  }
31781
- var INDEX_VERSION, DEFAULT_MAX_FILES, IGNORE_SEGMENTS;
32211
+ var VECTOR_STORE_VERSION, EMBED_BATCH_SIZE, INDEX_VERSION, DEFAULT_MAX_FILES, IGNORE_SEGMENTS, RRF_K2;
31782
32212
  var init_code_context_index = __esm({
31783
32213
  "src/lib/code-context-index.ts"() {
31784
32214
  "use strict";
31785
32215
  init_config();
31786
32216
  init_code_chunker();
32217
+ init_exe_daemon_client();
32218
+ VECTOR_STORE_VERSION = 1;
32219
+ EMBED_BATCH_SIZE = 64;
31787
32220
  INDEX_VERSION = 2;
31788
32221
  DEFAULT_MAX_FILES = 5e3;
31789
32222
  IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
32223
+ RRF_K2 = 60;
31790
32224
  }
31791
32225
  });
31792
32226
 
31793
32227
  // src/mcp/tools/code-context.ts
31794
- import { z as z92 } from "zod";
32228
+ import { z as z93 } from "zod";
31795
32229
  function errorResult10(text3) {
31796
32230
  return { content: [{ type: "text", text: text3 }], isError: true };
31797
32231
  }
@@ -31801,23 +32235,24 @@ function jsonResult(value) {
31801
32235
  function registerCodeContext(server) {
31802
32236
  server.registerTool("code_context", {
31803
32237
  title: "Code Context",
31804
- description: "Persistent codebase context engine. One consolidated tool to avoid MCP bloat. Actions: index, search, trace, blast_radius, stats.",
32238
+ description: "Persistent codebase context engine with semantic vector search. One consolidated tool to avoid MCP bloat. Actions: index (structural only), index_embed (structural + vector embeddings), search (semantic+lexical hybrid), trace, blast_radius, stats. Search uses RRF fusion of Jina v5 embeddings (cosine similarity) + lexical scoring. Falls back to lexical if daemon unavailable.",
31805
32239
  inputSchema: {
31806
- action: z92.enum(["index", "search", "trace", "blast_radius", "stats"]).describe("Code context operation"),
31807
- project_root: z92.string().optional().describe("Repository root. Defaults to current working directory."),
31808
- query: z92.string().optional().describe("Search query for action=search"),
31809
- symbol: z92.string().optional().describe("Symbol/function/class/type name for trace or blast_radius"),
31810
- file_path: z92.string().optional().describe("File path for blast_radius"),
31811
- force: z92.boolean().optional().describe("Force rebuild before answering"),
31812
- limit: z92.coerce.number().int().min(1).max(100).optional().describe("Max results"),
31813
- offset: z92.coerce.number().int().min(0).optional().describe("Search pagination offset"),
31814
- refresh_index: z92.boolean().optional().describe("Refresh/rebuild index before searching"),
31815
- languages: z92.array(z92.string()).optional().describe('Language filters, e.g. ["python", "typescript"]'),
31816
- paths: z92.array(z92.string()).optional().describe("Path/glob filters"),
31817
- depth: z92.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
31818
- max_files: z92.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index")
31819
- }
31820
- }, async ({ action, project_root, query, symbol, file_path, force, limit, offset, refresh_index, languages, paths, depth, max_files }) => {
32240
+ action: z93.enum(["index", "index_embed", "search", "trace", "blast_radius", "stats"]).describe("Code context operation. index_embed = index + generate embeddings for semantic search."),
32241
+ project_root: z93.string().optional().describe("Repository root. Defaults to current working directory."),
32242
+ query: z93.string().optional().describe("Natural language search query (e.g. 'authentication logic', 'database migration'). Semantic search finds conceptual matches, not just keywords."),
32243
+ symbol: z93.string().optional().describe("Symbol/function/class/type name for trace or blast_radius"),
32244
+ file_path: z93.string().optional().describe("File path for blast_radius"),
32245
+ force: z93.boolean().optional().describe("Force rebuild before answering"),
32246
+ limit: z93.coerce.number().int().min(1).max(100).optional().describe("Max results"),
32247
+ offset: z93.coerce.number().int().min(0).optional().describe("Search pagination offset"),
32248
+ refresh_index: z93.boolean().optional().describe("Refresh/rebuild index before searching"),
32249
+ languages: z93.array(z93.string()).optional().describe('Language filters, e.g. ["python", "typescript"]'),
32250
+ paths: z93.array(z93.string()).optional().describe("Path/glob filters"),
32251
+ depth: z93.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
32252
+ max_files: z93.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index"),
32253
+ lexical_only: z93.boolean().optional().describe("Force lexical-only search (skip vector similarity). Default: false \u2014 uses semantic hybrid search.")
32254
+ }
32255
+ }, async ({ action, project_root, query, symbol, file_path, force, limit, offset, refresh_index, languages, paths, depth, max_files, lexical_only }) => {
31821
32256
  const opts = { projectRoot: project_root, force, maxFiles: max_files };
31822
32257
  if (action === "index") {
31823
32258
  const index = buildCodeContextIndex(opts);
@@ -31827,7 +32262,24 @@ function registerCodeContext(server) {
31827
32262
  indexedAt: index.indexedAt,
31828
32263
  files: Object.keys(index.files).length,
31829
32264
  symbols: Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0),
31830
- imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0)
32265
+ imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
32266
+ note: "Structural index only. Use action=index_embed to also generate vector embeddings for semantic search."
32267
+ });
32268
+ }
32269
+ if (action === "index_embed") {
32270
+ const { index, vectorCount, newEmbeddings } = await buildCodeContextIndexWithEmbeddings(opts);
32271
+ const totalSymbols = Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0);
32272
+ return jsonResult({
32273
+ projectRoot: index.projectRoot,
32274
+ branch: index.branch,
32275
+ indexedAt: index.indexedAt,
32276
+ files: Object.keys(index.files).length,
32277
+ symbols: totalSymbols,
32278
+ imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
32279
+ vectorCount,
32280
+ newEmbeddings,
32281
+ embeddingCoverage: totalSymbols > 0 ? `${(vectorCount / totalSymbols * 100).toFixed(1)}%` : "0%",
32282
+ note: "Structural index + vector embeddings. Semantic search is now available."
31831
32283
  });
31832
32284
  }
31833
32285
  if (action === "stats") {
@@ -31835,14 +32287,28 @@ function registerCodeContext(server) {
31835
32287
  }
31836
32288
  if (action === "search") {
31837
32289
  if (!query) return errorResult10('code_context action "search" requires query');
32290
+ const searchOpts = { ...opts, limit, offset, refreshIndex: refresh_index, languages, paths };
32291
+ if (lexical_only) {
32292
+ return jsonResult({
32293
+ query,
32294
+ mode: "lexical",
32295
+ limit: limit ?? 20,
32296
+ offset: offset ?? 0,
32297
+ languages: languages ?? [],
32298
+ paths: paths ?? [],
32299
+ results: searchCodeContext(query, searchOpts)
32300
+ });
32301
+ }
32302
+ const results = await searchCodeContextSemantic(query, searchOpts);
32303
+ const hasSemantic = results.some((r) => r.matches.some((m) => m.startsWith("semantic=")));
31838
32304
  return jsonResult({
31839
32305
  query,
32306
+ mode: hasSemantic ? "semantic+lexical" : "lexical-fallback",
31840
32307
  limit: limit ?? 20,
31841
32308
  offset: offset ?? 0,
31842
- refresh_index: refresh_index ?? false,
31843
32309
  languages: languages ?? [],
31844
32310
  paths: paths ?? [],
31845
- results: searchCodeContext(query, { ...opts, limit, offset, refreshIndex: refresh_index, languages, paths })
32311
+ results
31846
32312
  });
31847
32313
  }
31848
32314
  if (action === "trace") {
@@ -31866,7 +32332,7 @@ var init_code_context = __esm({
31866
32332
  });
31867
32333
 
31868
32334
  // src/mcp/tools/support-inbox.ts
31869
- import { z as z93 } from "zod";
32335
+ import { z as z94 } from "zod";
31870
32336
  function adminToken() {
31871
32337
  return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN;
31872
32338
  }
@@ -31905,9 +32371,9 @@ function registerListBugReports(server) {
31905
32371
  title: "List Bug Reports",
31906
32372
  description: "AskExe-internal only: list incoming customer bug reports from the support inbox.",
31907
32373
  inputSchema: {
31908
- status: z93.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
31909
- severity: z93.enum(["p0", "p1", "p2", "p3"]).optional(),
31910
- limit: z93.number().int().min(1).max(100).default(25)
32374
+ status: z94.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
32375
+ severity: z94.enum(["p0", "p1", "p2", "p3"]).optional(),
32376
+ limit: z94.number().int().min(1).max(100).default(25)
31911
32377
  }
31912
32378
  },
31913
32379
  async ({ status: status2, severity, limit }) => {
@@ -31926,7 +32392,7 @@ function registerGetBugReport(server) {
31926
32392
  {
31927
32393
  title: "Get Bug Report",
31928
32394
  description: "AskExe-internal only: fetch one customer bug report with full markdown payload.",
31929
- inputSchema: { id: z93.string().min(8) }
32395
+ inputSchema: { id: z94.string().min(8) }
31930
32396
  },
31931
32397
  async ({ id }) => {
31932
32398
  const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`);
@@ -31941,12 +32407,12 @@ function registerTriageBugReport(server) {
31941
32407
  title: "Triage Bug Report",
31942
32408
  description: "AskExe-internal only: update bug report status and link task/commit/release metadata.",
31943
32409
  inputSchema: {
31944
- id: z93.string().min(8),
32410
+ id: z94.string().min(8),
31945
32411
  status: STATUS.optional(),
31946
- triage_notes: z93.string().optional(),
31947
- linked_task_id: z93.string().optional(),
31948
- linked_commit: z93.string().optional(),
31949
- fixed_version: z93.string().optional()
32412
+ triage_notes: z94.string().optional(),
32413
+ linked_task_id: z94.string().optional(),
32414
+ linked_commit: z94.string().optional(),
32415
+ fixed_version: z94.string().optional()
31950
32416
  }
31951
32417
  },
31952
32418
  async ({ id, status: status2, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
@@ -31958,12 +32424,75 @@ function registerTriageBugReport(server) {
31958
32424
  }
31959
32425
  );
31960
32426
  }
31961
- var DEFAULT_ENDPOINT, STATUS;
32427
+ function featureEndpoint() {
32428
+ return endpoint().replace(/\/bug-reports\/?$/, "/feature-requests");
32429
+ }
32430
+ function registerListFeatureRequests(server) {
32431
+ server.registerTool(
32432
+ "list_feature_requests",
32433
+ {
32434
+ title: "List Feature Requests",
32435
+ description: "AskExe-internal only: list incoming customer feature requests from the support inbox.",
32436
+ inputSchema: {
32437
+ status: z94.enum(["all", "open", "planned", "in_progress", "shipped", "closed", "wontdo"]).default("open"),
32438
+ priority: z94.enum(["p0", "p1", "p2", "p3"]).optional(),
32439
+ limit: z94.number().int().min(1).max(100).default(25)
32440
+ }
32441
+ },
32442
+ async ({ status: status2, priority, limit }) => {
32443
+ const url = new URL(featureEndpoint());
32444
+ url.searchParams.set("status", status2);
32445
+ url.searchParams.set("limit", String(limit));
32446
+ if (priority) url.searchParams.set("priority", priority);
32447
+ const data = await requestJson(url.toString());
32448
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
32449
+ }
32450
+ );
32451
+ }
32452
+ function registerGetFeatureRequest(server) {
32453
+ server.registerTool(
32454
+ "get_feature_request",
32455
+ {
32456
+ title: "Get Feature Request",
32457
+ description: "AskExe-internal only: fetch one customer feature request with full payload.",
32458
+ inputSchema: { id: z94.string().min(8) }
32459
+ },
32460
+ async ({ id }) => {
32461
+ const data = await requestJson(`${featureEndpoint()}/${encodeURIComponent(id)}`);
32462
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
32463
+ }
32464
+ );
32465
+ }
32466
+ function registerTriageFeatureRequest(server) {
32467
+ server.registerTool(
32468
+ "triage_feature_request",
32469
+ {
32470
+ title: "Triage Feature Request",
32471
+ description: "AskExe-internal only: update feature request status, response notes, and version metadata.",
32472
+ inputSchema: {
32473
+ id: z94.string().min(8),
32474
+ status: FEATURE_STATUS.optional(),
32475
+ response_notes: z94.string().optional(),
32476
+ target_version: z94.string().optional(),
32477
+ shipped_version: z94.string().optional()
32478
+ }
32479
+ },
32480
+ async ({ id, status: status2, response_notes, target_version, shipped_version }) => {
32481
+ const data = await requestJson(`${featureEndpoint()}/${encodeURIComponent(id)}`, {
32482
+ method: "PATCH",
32483
+ body: JSON.stringify({ status: status2, response_notes, target_version, shipped_version })
32484
+ });
32485
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
32486
+ }
32487
+ );
32488
+ }
32489
+ var DEFAULT_ENDPOINT, STATUS, FEATURE_STATUS;
31962
32490
  var init_support_inbox = __esm({
31963
32491
  "src/mcp/tools/support-inbox.ts"() {
31964
32492
  "use strict";
31965
32493
  DEFAULT_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
31966
- STATUS = z93.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
32494
+ STATUS = z94.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
32495
+ FEATURE_STATUS = z94.enum(["open", "planned", "in_progress", "shipped", "closed", "wontdo"]);
31967
32496
  }
31968
32497
  });
31969
32498
 
@@ -32299,6 +32828,7 @@ function registerAllTools(server) {
32299
32828
  gate("registerStoreDecision", registerStoreDecision);
32300
32829
  gate("registerGetDecision", registerGetDecision);
32301
32830
  gate("registerCreateBugReport", registerCreateBugReport);
32831
+ gate("registerCreateFeatureRequest", registerCreateFeatureRequest);
32302
32832
  gate("registerSupportTools", registerSupportTools);
32303
32833
  gate("registerCliParityTools", registerCliParityTools);
32304
32834
  gate("registerGetSessionEvents", registerGetSessionEvents);
@@ -32308,6 +32838,9 @@ function registerAllTools(server) {
32308
32838
  gate("registerListBugReports", registerListBugReports);
32309
32839
  gate("registerGetBugReport", registerGetBugReport);
32310
32840
  gate("registerTriageBugReport", registerTriageBugReport);
32841
+ gate("registerListFeatureRequests", registerListFeatureRequests);
32842
+ gate("registerGetFeatureRequest", registerGetFeatureRequest);
32843
+ gate("registerTriageFeatureRequest", registerTriageFeatureRequest);
32311
32844
  }
32312
32845
  if (exposeLegacyConfig) {
32313
32846
  gate("registerGetAgentSpend", registerGetAgentSpend);
@@ -32450,6 +32983,7 @@ var init_register_tools = __esm({
32450
32983
  init_query_company_brain();
32451
32984
  init_company_actions();
32452
32985
  init_create_bug_report();
32986
+ init_create_feature_request();
32453
32987
  init_support();
32454
32988
  init_cli_parity();
32455
32989
  init_get_session_events();
@@ -32680,7 +33214,7 @@ __export(task_enforcement_exports, {
32680
33214
  sendNudge: () => sendNudge
32681
33215
  });
32682
33216
  import { writeFileSync as writeFileSync26 } from "fs";
32683
- import path58 from "path";
33217
+ import path59 from "path";
32684
33218
  function writeAuditEntry(entry) {
32685
33219
  try {
32686
33220
  const line = JSON.stringify(entry) + "\n";
@@ -32855,7 +33389,7 @@ var init_task_enforcement = __esm({
32855
33389
  "What do you need?"
32856
33390
  ];
32857
33391
  MANAGER_ROLES = ["COO", "CTO"];
32858
- AUDIT_LOG_PATH = path58.join(
33392
+ AUDIT_LOG_PATH = path59.join(
32859
33393
  process.env.HOME ?? process.env.USERPROFILE ?? "/tmp",
32860
33394
  ".exe-os",
32861
33395
  "enforcement-audit.jsonl"
@@ -32880,7 +33414,7 @@ __export(background_jobs_exports, {
32880
33414
  import { existsSync as existsSync45, mkdirSync as mkdirSync23, readFileSync as readFileSync39, writeFileSync as writeFileSync27, unlinkSync as unlinkSync14 } from "fs";
32881
33415
  import { execFileSync as execFileSync3 } from "child_process";
32882
33416
  import os22 from "os";
32883
- import path59 from "path";
33417
+ import path60 from "path";
32884
33418
  function ensureDirs() {
32885
33419
  mkdirSync23(LOCK_DIR, { recursive: true });
32886
33420
  }
@@ -32926,7 +33460,7 @@ function listBackgroundJobs() {
32926
33460
  return jobs;
32927
33461
  }
32928
33462
  function lockPath(type) {
32929
- return path59.join(LOCK_DIR, `${type.replace(/[^a-zA-Z0-9_.-]/g, "_")}.lock`);
33463
+ return path60.join(LOCK_DIR, `${type.replace(/[^a-zA-Z0-9_.-]/g, "_")}.lock`);
32930
33464
  }
32931
33465
  function acquireJobLock(type, ttlMs = DEFAULT_LOCK_TTL_MS) {
32932
33466
  ensureDirs();
@@ -33153,9 +33687,9 @@ var init_background_jobs = __esm({
33153
33687
  "src/lib/background-jobs.ts"() {
33154
33688
  "use strict";
33155
33689
  init_config();
33156
- JOB_DIR = path59.join(EXE_AI_DIR, "jobs");
33157
- JOBS_FILE = path59.join(JOB_DIR, "jobs.json");
33158
- LOCK_DIR = path59.join(JOB_DIR, "locks");
33690
+ JOB_DIR = path60.join(EXE_AI_DIR, "jobs");
33691
+ JOBS_FILE = path60.join(JOB_DIR, "jobs.json");
33692
+ LOCK_DIR = path60.join(JOB_DIR, "locks");
33159
33693
  DEFAULT_LOCK_TTL_MS = 6 * 60 * 60 * 1e3;
33160
33694
  MAX_HISTORY = 200;
33161
33695
  }
@@ -33168,16 +33702,16 @@ __export(ws_auth_exports, {
33168
33702
  deriveWsAuthToken: () => deriveWsAuthToken,
33169
33703
  hashAuthToken: () => hashAuthToken
33170
33704
  });
33171
- import crypto21 from "crypto";
33705
+ import crypto22 from "crypto";
33172
33706
  function deriveWsAuthToken(masterKey) {
33173
- return Buffer.from(crypto21.hkdfSync("sha256", masterKey, "", WS_AUTH_HKDF_INFO, 32));
33707
+ return Buffer.from(crypto22.hkdfSync("sha256", masterKey, "", WS_AUTH_HKDF_INFO, 32));
33174
33708
  }
33175
33709
  function deriveOrgId(masterKey) {
33176
- const raw = Buffer.from(crypto21.hkdfSync("sha256", masterKey, "", ORG_ID_HKDF_INFO, 32));
33177
- return crypto21.createHash("sha256").update(raw).digest("hex").slice(0, 32);
33710
+ const raw = Buffer.from(crypto22.hkdfSync("sha256", masterKey, "", ORG_ID_HKDF_INFO, 32));
33711
+ return crypto22.createHash("sha256").update(raw).digest("hex").slice(0, 32);
33178
33712
  }
33179
33713
  function hashAuthToken(token) {
33180
- return crypto21.createHash("sha256").update(token).digest("hex");
33714
+ return crypto22.createHash("sha256").update(token).digest("hex");
33181
33715
  }
33182
33716
  var WS_AUTH_HKDF_INFO, ORG_ID_HKDF_INFO;
33183
33717
  var init_ws_auth = __esm({
@@ -33195,10 +33729,10 @@ __export(device_registry_exports, {
33195
33729
  resolveTargetDevice: () => resolveTargetDevice,
33196
33730
  setFriendlyName: () => setFriendlyName
33197
33731
  });
33198
- import crypto22 from "crypto";
33732
+ import crypto23 from "crypto";
33199
33733
  import os23 from "os";
33200
33734
  import { readFileSync as readFileSync40, writeFileSync as writeFileSync28, mkdirSync as mkdirSync24, existsSync as existsSync46 } from "fs";
33201
- import path60 from "path";
33735
+ import path61 from "path";
33202
33736
  function getDeviceInfo() {
33203
33737
  if (existsSync46(DEVICE_JSON_PATH)) {
33204
33738
  try {
@@ -33212,11 +33746,11 @@ function getDeviceInfo() {
33212
33746
  }
33213
33747
  const hostname = os23.hostname();
33214
33748
  const info = {
33215
- deviceId: crypto22.randomUUID(),
33749
+ deviceId: crypto23.randomUUID(),
33216
33750
  friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
33217
33751
  hostname
33218
33752
  };
33219
- mkdirSync24(path60.dirname(DEVICE_JSON_PATH), { recursive: true });
33753
+ mkdirSync24(path61.dirname(DEVICE_JSON_PATH), { recursive: true });
33220
33754
  writeFileSync28(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
33221
33755
  return info;
33222
33756
  }
@@ -33257,7 +33791,7 @@ var init_device_registry = __esm({
33257
33791
  "src/lib/device-registry.ts"() {
33258
33792
  "use strict";
33259
33793
  init_config();
33260
- DEVICE_JSON_PATH = path60.join(EXE_AI_DIR, "device.json");
33794
+ DEVICE_JSON_PATH = path61.join(EXE_AI_DIR, "device.json");
33261
33795
  }
33262
33796
  });
33263
33797
 
@@ -33474,7 +34008,7 @@ import net2 from "net";
33474
34008
  import { createServer as createHttpServer } from "http";
33475
34009
  import { randomUUID as randomUUID11 } from "crypto";
33476
34010
  import { writeFileSync as writeFileSync29, unlinkSync as unlinkSync15, mkdirSync as mkdirSync25, existsSync as existsSync47, readFileSync as readFileSync41, chmodSync as chmodSync2 } from "fs";
33477
- import path61 from "path";
34011
+ import path62 from "path";
33478
34012
 
33479
34013
  // src/lib/orchestration-metrics.ts
33480
34014
  init_config();
@@ -33547,8 +34081,8 @@ function initMetrics() {
33547
34081
  // src/lib/exe-daemon.ts
33548
34082
  init_memory_write_governor();
33549
34083
  init_mcp_transport_health();
33550
- var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path61.join(EXE_AI_DIR, "exed.sock");
33551
- var PID_PATH4 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path61.join(EXE_AI_DIR, "exed.pid");
34084
+ var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path62.join(EXE_AI_DIR, "exed.sock");
34085
+ var PID_PATH4 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path62.join(EXE_AI_DIR, "exed.pid");
33552
34086
  var MODEL_FILE = "jina-embeddings-v5-small-q4_k_m.gguf";
33553
34087
  var IDLE_TIMEOUT_MS2 = parseInt(process.env.EXE_DAEMON_IDLE_TIMEOUT_MS || "0", 10);
33554
34088
  var REVIEW_POLL_INTERVAL_MS = 60 * 1e3;
@@ -33577,7 +34111,7 @@ function enqueue(queue, entry) {
33577
34111
  queue.push(entry);
33578
34112
  }
33579
34113
  async function loadModel() {
33580
- const modelPath = path61.join(MODELS_DIR, MODEL_FILE);
34114
+ const modelPath = path62.join(MODELS_DIR, MODEL_FILE);
33581
34115
  if (!existsSync47(modelPath)) {
33582
34116
  process.stderr.write(`[exed] No model at ${modelPath} \u2014 running without embeddings (VPS mode).
33583
34117
  `);
@@ -34019,14 +34553,14 @@ function startMemoryQueueDrain() {
34019
34553
  `);
34020
34554
  }
34021
34555
  function startServer() {
34022
- mkdirSync25(path61.dirname(SOCKET_PATH2), { recursive: true });
34556
+ mkdirSync25(path62.dirname(SOCKET_PATH2), { recursive: true });
34023
34557
  try {
34024
- chmodSync2(path61.dirname(SOCKET_PATH2), 448);
34558
+ chmodSync2(path62.dirname(SOCKET_PATH2), 448);
34025
34559
  } catch {
34026
34560
  }
34027
34561
  _daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV2] ?? null);
34028
34562
  for (const oldFile of ["embed.sock", "embed.pid"]) {
34029
- const oldPath = path61.join(path61.dirname(SOCKET_PATH2), oldFile);
34563
+ const oldPath = path62.join(path62.dirname(SOCKET_PATH2), oldFile);
34030
34564
  try {
34031
34565
  if (oldFile.endsWith(".pid")) {
34032
34566
  const pid = parseInt(readFileSync41(oldPath, "utf8").trim(), 10);
@@ -34151,6 +34685,7 @@ function startServer() {
34151
34685
  void startMcpHttpServer();
34152
34686
  }
34153
34687
  async function startMcpHttpServer() {
34688
+ process.stderr.write("[exed] MCP HTTP: starting setup...\n");
34154
34689
  try {
34155
34690
  let parseDurationMs2 = function(value, fallback, options = {}) {
34156
34691
  if (!value) return fallback;
@@ -34205,8 +34740,14 @@ async function startMcpHttpServer() {
34205
34740
  const { isInitializeRequest } = await import("@modelcontextprotocol/sdk/types.js");
34206
34741
  const { registerAllTools: registerAllTools2 } = await Promise.resolve().then(() => (init_register_tools(), register_tools_exports));
34207
34742
  const { runWithAgent: runWithAgent2 } = await Promise.resolve().then(() => (init_agent_context(), agent_context_exports));
34208
- const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
34209
- await initStore2();
34743
+ const dbReady = await ensureStoreForPolling();
34744
+ if (!dbReady) {
34745
+ process.stderr.write(
34746
+ "[exed] MCP HTTP: DB init failed \u2014 MCP HTTP endpoint will NOT start. Run 'exe-os setup' to fix keychain/encryption.\n"
34747
+ );
34748
+ return;
34749
+ }
34750
+ process.stderr.write("[exed] MCP HTTP: DB ready\n");
34210
34751
  const transports = /* @__PURE__ */ new Map();
34211
34752
  const MCP_HTTP_PORT = parseInt(process.env.EXE_MCP_PORT || "48739", 10);
34212
34753
  const MCP_SESSION_TTL_MS = parseDurationMs2(process.env.EXE_MCP_SESSION_TTL_MS, 4 * 60 * 60 * 1e3, { allowZero: true });
@@ -34391,8 +34932,10 @@ async function startMcpHttpServer() {
34391
34932
  }
34392
34933
  });
34393
34934
  } catch (err) {
34394
- process.stderr.write(`[exed] MCP HTTP setup failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
34935
+ const msg = err instanceof Error ? err.stack ?? err.message : String(err);
34936
+ process.stderr.write(`[exed] MCP HTTP setup FAILED: ${msg}
34395
34937
  `);
34938
+ process.stderr.write("[exed] MCP HTTP endpoint will NOT be available this session.\n");
34396
34939
  }
34397
34940
  }
34398
34941
  var _storeInitialized = false;
@@ -34660,7 +35203,7 @@ function startGraphExtraction() {
34660
35203
  `);
34661
35204
  }
34662
35205
  var AGENT_STATS_INTERVAL_MS = 60 * 1e3;
34663
- var AGENT_STATS_PATH = path61.join(EXE_AI_DIR, "agent-stats.json");
35206
+ var AGENT_STATS_PATH = path62.join(EXE_AI_DIR, "agent-stats.json");
34664
35207
  async function writeAgentStats() {
34665
35208
  fired("agent_stats");
34666
35209
  if (!await ensureStoreForPolling()) return;
@@ -34837,11 +35380,11 @@ function startIntercomQueueDrain() {
34837
35380
  const hasInProgressTask = (session) => {
34838
35381
  try {
34839
35382
  const { baseAgentName: ban } = (init_employees(), __toCommonJS(employees_exports));
34840
- const path62 = __require("path");
35383
+ const path63 = __require("path");
34841
35384
  const { existsSync: existsSync48 } = __require("fs");
34842
35385
  const os25 = __require("os");
34843
35386
  const agent = ban(session.split("-")[0] ?? session);
34844
- const markerPath = path62.join(os25.homedir(), ".exe-os", "session-cache", `current-task-${agent}.json`);
35387
+ const markerPath = path63.join(os25.homedir(), ".exe-os", "session-cache", `current-task-${agent}.json`);
34845
35388
  return existsSync48(markerPath);
34846
35389
  } catch {
34847
35390
  return false;