@askexenow/exe-os 0.9.69 → 0.9.71

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 (79) hide show
  1. package/deploy/stack-manifests/v0.9.json +96 -16
  2. package/dist/bin/agentic-ontology-backfill.js +33 -0
  3. package/dist/bin/agentic-reflection-backfill.js +33 -0
  4. package/dist/bin/agentic-semantic-label.js +33 -0
  5. package/dist/bin/backfill-conversations.js +33 -0
  6. package/dist/bin/backfill-responses.js +33 -0
  7. package/dist/bin/backfill-vectors.js +33 -0
  8. package/dist/bin/bulk-sync-postgres.js +33 -0
  9. package/dist/bin/cleanup-stale-review-tasks.js +33 -0
  10. package/dist/bin/cli.js +1284 -178
  11. package/dist/bin/exe-agent.js +6 -0
  12. package/dist/bin/exe-assign.js +33 -0
  13. package/dist/bin/exe-boot.js +33 -0
  14. package/dist/bin/exe-call.js +6 -0
  15. package/dist/bin/exe-cloud.js +33 -0
  16. package/dist/bin/exe-dispatch.js +33 -0
  17. package/dist/bin/exe-doctor.js +33 -0
  18. package/dist/bin/exe-export-behaviors.js +33 -0
  19. package/dist/bin/exe-forget.js +33 -0
  20. package/dist/bin/exe-gateway.js +178 -110
  21. package/dist/bin/exe-heartbeat.js +33 -0
  22. package/dist/bin/exe-kill.js +33 -0
  23. package/dist/bin/exe-launch-agent.js +33 -0
  24. package/dist/bin/exe-new-employee.js +6 -0
  25. package/dist/bin/exe-pending-messages.js +33 -0
  26. package/dist/bin/exe-pending-notifications.js +33 -0
  27. package/dist/bin/exe-pending-reviews.js +33 -0
  28. package/dist/bin/exe-rename.js +40 -4
  29. package/dist/bin/exe-review.js +33 -0
  30. package/dist/bin/exe-search.js +33 -0
  31. package/dist/bin/exe-session-cleanup.js +33 -0
  32. package/dist/bin/exe-start-codex.js +33 -0
  33. package/dist/bin/exe-start-opencode.js +33 -0
  34. package/dist/bin/exe-status.js +33 -0
  35. package/dist/bin/exe-team.js +33 -0
  36. package/dist/bin/git-sweep.js +33 -0
  37. package/dist/bin/graph-backfill.js +177 -110
  38. package/dist/bin/graph-export.js +33 -0
  39. package/dist/bin/intercom-check.js +33 -0
  40. package/dist/bin/registry-proxy.js +207 -0
  41. package/dist/bin/scan-tasks.js +33 -0
  42. package/dist/bin/setup.js +33 -0
  43. package/dist/bin/shard-migrate.js +33 -0
  44. package/dist/bin/stack-update.js +128 -0
  45. package/dist/gateway/index.js +178 -110
  46. package/dist/hooks/bug-report-worker.js +33 -0
  47. package/dist/hooks/codex-stop-task-finalizer.js +33 -0
  48. package/dist/hooks/commit-complete.js +33 -0
  49. package/dist/hooks/error-recall.js +33 -0
  50. package/dist/hooks/ingest.js +33 -0
  51. package/dist/hooks/instructions-loaded.js +33 -0
  52. package/dist/hooks/notification.js +33 -0
  53. package/dist/hooks/post-compact.js +33 -0
  54. package/dist/hooks/post-tool-combined.js +698 -17
  55. package/dist/hooks/pre-compact.js +33 -0
  56. package/dist/hooks/pre-tool-use.js +33 -0
  57. package/dist/hooks/prompt-submit.js +314 -0
  58. package/dist/hooks/session-end.js +33 -0
  59. package/dist/hooks/session-start.js +33 -0
  60. package/dist/hooks/stop.js +279 -12
  61. package/dist/hooks/subagent-stop.js +33 -0
  62. package/dist/hooks/summary-worker.js +33 -0
  63. package/dist/index.js +178 -110
  64. package/dist/lib/cloud-sync.js +27 -0
  65. package/dist/lib/database.js +27 -0
  66. package/dist/lib/db.js +27 -0
  67. package/dist/lib/device-registry.js +27 -0
  68. package/dist/lib/employee-templates.js +6 -0
  69. package/dist/lib/exe-daemon.js +639 -259
  70. package/dist/lib/hybrid-search.js +33 -0
  71. package/dist/lib/registry-proxy.js +162 -0
  72. package/dist/lib/schedules.js +33 -0
  73. package/dist/lib/store.js +33 -0
  74. package/dist/mcp/server.js +561 -244
  75. package/dist/runtime/index.js +33 -0
  76. package/dist/tui/App.js +33 -0
  77. package/package.json +3 -2
  78. package/stack.release.json +6 -4
  79. package/stack.release.schema.json +89 -18
@@ -2847,6 +2847,33 @@ async function ensureSchema() {
2847
2847
  CREATE INDEX IF NOT EXISTS idx_chat_history_session
2848
2848
  ON chat_history(session_id, id);
2849
2849
  `);
2850
+ await client.executeMultiple(`
2851
+ CREATE TABLE IF NOT EXISTS session_events (
2852
+ id TEXT PRIMARY KEY,
2853
+ agent_id TEXT NOT NULL,
2854
+ agent_role TEXT NOT NULL,
2855
+ session_id TEXT NOT NULL,
2856
+ session_scope TEXT,
2857
+ project_name TEXT NOT NULL,
2858
+ event_index INTEGER NOT NULL,
2859
+ event_type TEXT NOT NULL,
2860
+ tool_name TEXT,
2861
+ tool_use_id TEXT,
2862
+ content TEXT NOT NULL,
2863
+ payload_json TEXT,
2864
+ has_error INTEGER NOT NULL DEFAULT 0,
2865
+ created_at TEXT NOT NULL
2866
+ );
2867
+
2868
+ CREATE INDEX IF NOT EXISTS idx_session_events_agent_time
2869
+ ON session_events(agent_id, created_at DESC);
2870
+
2871
+ CREATE INDEX IF NOT EXISTS idx_session_events_session_index
2872
+ ON session_events(session_id, event_index);
2873
+
2874
+ CREATE INDEX IF NOT EXISTS idx_session_events_scope_agent_time
2875
+ ON session_events(session_scope, agent_id, created_at DESC);
2876
+ `);
2850
2877
  await client.executeMultiple(`
2851
2878
  CREATE TABLE IF NOT EXISTS workspaces (
2852
2879
  id TEXT PRIMARY KEY,
@@ -5790,6 +5817,12 @@ var init_platform_procedures = __esm({
5790
5817
  priority: "p0",
5791
5818
  content: "exe-build-adv is MANDATORY for ALL work touching 3+ files. Run /exe-build-adv --auto BEFORE implementation. Pipeline: Spec \u2192 AC \u2192 Tests \u2192 Evaluate \u2192 Fix. No multi-file feature ships without pipeline artifacts. No exceptions \u2014 managers reject work without them."
5792
5819
  },
5820
+ {
5821
+ title: "Code context first for repository orientation",
5822
+ domain: "workflow",
5823
+ priority: "p1",
5824
+ content: "Before broad repo exploration, symbol tracing, blast-radius review, or codebase Q&A, agents should use the consolidated code_context MCP tool instead of manual grep/read loops. Use action=index or stats to refresh/check the index; action=search with query, limit, offset, languages, paths, refresh_index for fresh multi-language code/doc search; action=trace for symbol imports/dependents; action=blast_radius for impact analysis before edits. CLI parity exists via exe-os code-context init|index|status|stats|search|doctor. Keep code_context separate from durable employee memory: promote only validated decisions, procedures, or lessons into store_memory/commit_memory."
5825
+ },
5793
5826
  {
5794
5827
  title: "Commit discipline \u2014 never leave verified work floating",
5795
5828
  domain: "workflow",
@@ -19020,13 +19053,25 @@ var init_export_graph = __esm({
19020
19053
 
19021
19054
  // src/lib/code-chunker.ts
19022
19055
  import ts from "typescript";
19056
+ function languageForFile(filePath) {
19057
+ const base = filePath.split(/[\\/]/).pop()?.toLowerCase() ?? "";
19058
+ if (["dockerfile", "makefile", "rakefile", "gemfile"].includes(base)) return base;
19059
+ const ext = base.includes(".") ? base.split(".").pop() : void 0;
19060
+ return ext ? LANGUAGE_BY_EXTENSION[ext] : void 0;
19061
+ }
19023
19062
  function chunkSourceFile(source, fileName = "file.ts") {
19063
+ const language = languageForFile(fileName);
19064
+ if (language === "typescript" || language === "javascript") {
19065
+ return chunkTypeScriptLike(source, fileName);
19066
+ }
19067
+ return chunkGenericSource(source, fileName, language);
19068
+ }
19069
+ function chunkTypeScriptLike(source, fileName) {
19024
19070
  const sourceFile = ts.createSourceFile(
19025
19071
  fileName,
19026
19072
  source,
19027
19073
  ts.ScriptTarget.Latest,
19028
19074
  true,
19029
- // setParentNodes
19030
19075
  fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
19031
19076
  );
19032
19077
  const chunks = [];
@@ -19048,144 +19093,117 @@ function chunkSourceFile(source, fileName = "file.ts") {
19048
19093
  if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
19049
19094
  return node.name?.getText(sourceFile) ?? "(anonymous)";
19050
19095
  }
19051
- if (ts.isClassDeclaration(node)) {
19052
- return node.name?.getText(sourceFile) ?? "(anonymous class)";
19053
- }
19054
- if (ts.isInterfaceDeclaration(node)) {
19055
- return node.name.getText(sourceFile);
19056
- }
19057
- if (ts.isTypeAliasDeclaration(node)) {
19058
- return node.name.getText(sourceFile);
19059
- }
19060
- if (ts.isEnumDeclaration(node)) {
19061
- return node.name.getText(sourceFile);
19062
- }
19063
- if (ts.isVariableStatement(node)) {
19064
- const decls = node.declarationList.declarations;
19065
- return decls.map((d) => d.name.getText(sourceFile)).join(", ");
19066
- }
19067
- if (ts.isExportAssignment(node)) {
19068
- return "default export";
19069
- }
19096
+ if (ts.isClassDeclaration(node)) return node.name?.getText(sourceFile) ?? "(anonymous class)";
19097
+ if (ts.isInterfaceDeclaration(node)) return node.name.getText(sourceFile);
19098
+ if (ts.isTypeAliasDeclaration(node)) return node.name.getText(sourceFile);
19099
+ if (ts.isEnumDeclaration(node)) return node.name.getText(sourceFile);
19100
+ if (ts.isVariableStatement(node)) return node.declarationList.declarations.map((d) => d.name.getText(sourceFile)).join(", ");
19101
+ if (ts.isExportAssignment(node)) return "default export";
19070
19102
  return "(unknown)";
19071
19103
  }
19072
19104
  function visitTopLevel(node) {
19073
19105
  if (ts.isImportDeclaration(node)) {
19074
- importLines.push({
19075
- start: getLineNumber(node.getStart(sourceFile)),
19076
- end: getLineNumber(node.getEnd())
19077
- });
19106
+ importLines.push({ start: getLineNumber(node.getStart(sourceFile)), end: getLineNumber(node.getEnd()) });
19078
19107
  return;
19079
19108
  }
19080
19109
  if (ts.isFunctionDeclaration(node)) {
19081
- chunks.push({
19082
- kind: "function",
19083
- name: getName(node),
19084
- text: getNodeText(node),
19085
- startLine: getLineNumber(node.getStart(sourceFile)),
19086
- endLine: getLineNumber(node.getEnd()),
19087
- comment: getLeadingComment(node)
19088
- });
19110
+ chunks.push({ kind: "function", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
19089
19111
  return;
19090
19112
  }
19091
19113
  if (ts.isClassDeclaration(node)) {
19092
- chunks.push({
19093
- kind: "class",
19094
- name: getName(node),
19095
- text: getNodeText(node),
19096
- startLine: getLineNumber(node.getStart(sourceFile)),
19097
- endLine: getLineNumber(node.getEnd()),
19098
- comment: getLeadingComment(node)
19099
- });
19114
+ chunks.push({ kind: "class", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
19100
19115
  return;
19101
19116
  }
19102
- if (ts.isInterfaceDeclaration(node)) {
19103
- chunks.push({
19104
- kind: "type",
19105
- name: getName(node),
19106
- text: getNodeText(node),
19107
- startLine: getLineNumber(node.getStart(sourceFile)),
19108
- endLine: getLineNumber(node.getEnd()),
19109
- comment: getLeadingComment(node)
19110
- });
19111
- return;
19112
- }
19113
- if (ts.isTypeAliasDeclaration(node)) {
19114
- chunks.push({
19115
- kind: "type",
19116
- name: getName(node),
19117
- text: getNodeText(node),
19118
- startLine: getLineNumber(node.getStart(sourceFile)),
19119
- endLine: getLineNumber(node.getEnd()),
19120
- comment: getLeadingComment(node)
19121
- });
19122
- return;
19123
- }
19124
- if (ts.isEnumDeclaration(node)) {
19125
- chunks.push({
19126
- kind: "type",
19127
- name: getName(node),
19128
- text: getNodeText(node),
19129
- startLine: getLineNumber(node.getStart(sourceFile)),
19130
- endLine: getLineNumber(node.getEnd()),
19131
- comment: getLeadingComment(node)
19132
- });
19117
+ if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node)) {
19118
+ chunks.push({ kind: "type", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
19133
19119
  return;
19134
19120
  }
19135
19121
  if (ts.isVariableStatement(node)) {
19136
- const decls = node.declarationList.declarations;
19137
- const isFnLike = decls.some(
19138
- (d) => d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer))
19139
- );
19140
- chunks.push({
19141
- kind: isFnLike ? "function" : "variable",
19142
- name: getName(node),
19143
- text: getNodeText(node),
19144
- startLine: getLineNumber(node.getStart(sourceFile)),
19145
- endLine: getLineNumber(node.getEnd()),
19146
- comment: getLeadingComment(node)
19147
- });
19122
+ const isFnLike = node.declarationList.declarations.some((d) => d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer)));
19123
+ chunks.push({ kind: isFnLike ? "function" : "variable", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
19148
19124
  return;
19149
19125
  }
19150
19126
  if (ts.isExportAssignment(node)) {
19151
- chunks.push({
19152
- kind: "export",
19153
- name: "default export",
19154
- text: getNodeText(node),
19155
- startLine: getLineNumber(node.getStart(sourceFile)),
19156
- endLine: getLineNumber(node.getEnd()),
19157
- comment: getLeadingComment(node)
19158
- });
19127
+ chunks.push({ kind: "export", name: "default export", text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
19159
19128
  return;
19160
19129
  }
19161
19130
  if (ts.isExpressionStatement(node)) {
19162
19131
  const text3 = getNodeText(node);
19163
- if (text3.length > 10) {
19164
- chunks.push({
19165
- kind: "other",
19166
- name: text3.slice(0, 40).replace(/\n/g, " "),
19167
- text: text3,
19168
- startLine: getLineNumber(node.getStart(sourceFile)),
19169
- endLine: getLineNumber(node.getEnd())
19170
- });
19171
- }
19172
- return;
19132
+ if (text3.length > 10) chunks.push({ kind: "other", name: text3.slice(0, 40).replace(/\n/g, " "), text: text3, startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()) });
19173
19133
  }
19174
19134
  }
19175
19135
  sourceFile.statements.forEach(visitTopLevel);
19176
19136
  if (importLines.length > 0) {
19177
19137
  const startLine = importLines[0].start;
19178
19138
  const endLine = importLines[importLines.length - 1].end;
19179
- const importText = lines.slice(startLine - 1, endLine).join("\n");
19180
- chunks.unshift({
19181
- kind: "import",
19182
- name: `${importLines.length} imports`,
19183
- text: importText,
19184
- startLine,
19139
+ chunks.unshift({ kind: "import", name: `${importLines.length} imports`, text: lines.slice(startLine - 1, endLine).join("\n"), startLine, endLine });
19140
+ }
19141
+ chunks.sort((a, b) => a.startLine - b.startLine);
19142
+ return chunks.length > 0 ? chunks : chunkByWindows(source, 80);
19143
+ }
19144
+ function chunkGenericSource(source, _fileName, language) {
19145
+ const lines = source.split("\n");
19146
+ if (source.trim().length === 0) return [];
19147
+ if (language && TEXT_LIKE_LANGUAGES.has(language)) return chunkTextLike(lines, language);
19148
+ const chunks = [];
19149
+ const patterns = [
19150
+ { kind: "function", regex: /^\s*(?:async\s+)?def\s+([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
19151
+ { kind: "class", regex: /^\s*class\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
19152
+ { kind: "function", regex: /^\s*(?:pub\s+)?fn\s+([A-Za-z_][\w]*)\s*[<(]/, name: (m) => m[1] },
19153
+ { kind: "class", regex: /^\s*(?:pub\s+)?(?:struct|enum|trait)\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
19154
+ { kind: "function", regex: /^\s*func\s+(?:\([^)]*\)\s*)?([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
19155
+ { kind: "function", regex: /^\s*(?:public|private|protected|static|final|suspend|fun|override|open|internal|export|async|func|function|subroutine)\s+.*?([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
19156
+ { kind: "function", regex: /^\s*function\s+([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
19157
+ { kind: "type", regex: /^\s*(?:interface|type|enum|record|data\s+class|case\s+class|contract|library)\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
19158
+ { kind: "section", regex: /^\s{0,3}#{1,6}\s+(.+)$/, name: (m) => m[1].trim() }
19159
+ ];
19160
+ const starts = [];
19161
+ for (let i = 0; i < lines.length; i++) {
19162
+ const line = lines[i];
19163
+ for (const pattern of patterns) {
19164
+ const match = line.match(pattern.regex);
19165
+ if (match) {
19166
+ starts.push({ line: i + 1, kind: pattern.kind, name: pattern.name(match) });
19167
+ break;
19168
+ }
19169
+ }
19170
+ }
19171
+ if (starts.length === 0) return chunkByWindows(source, 80);
19172
+ for (let i = 0; i < starts.length; i++) {
19173
+ const start = starts[i];
19174
+ const next = starts[i + 1]?.line ?? lines.length + 1;
19175
+ const endLine = Math.max(start.line, next - 1);
19176
+ chunks.push({
19177
+ kind: start.kind,
19178
+ name: start.name,
19179
+ text: lines.slice(start.line - 1, endLine).join("\n"),
19180
+ startLine: start.line,
19185
19181
  endLine
19186
19182
  });
19187
19183
  }
19188
- chunks.sort((a, b) => a.startLine - b.startLine);
19184
+ return chunks;
19185
+ }
19186
+ function chunkTextLike(lines, language) {
19187
+ if (language === "markdown" || language === "mdx") {
19188
+ const starts = lines.map((line, i) => ({ line, i })).filter(({ line }) => /^\s{0,3}#{1,6}\s+/.test(line));
19189
+ if (starts.length > 0) {
19190
+ return starts.map((start, idx) => {
19191
+ const end = starts[idx + 1]?.i ?? lines.length;
19192
+ const title = start.line.replace(/^\s{0,3}#{1,6}\s+/, "").trim();
19193
+ return { kind: "section", name: title, text: lines.slice(start.i, end).join("\n"), startLine: start.i + 1, endLine: end };
19194
+ });
19195
+ }
19196
+ }
19197
+ return chunkByWindows(lines.join("\n"), 80);
19198
+ }
19199
+ function chunkByWindows(source, windowLines) {
19200
+ const lines = source.split("\n");
19201
+ const chunks = [];
19202
+ for (let i = 0; i < lines.length; i += windowLines) {
19203
+ const end = Math.min(lines.length, i + windowLines);
19204
+ const text3 = lines.slice(i, end).join("\n");
19205
+ if (text3.trim()) chunks.push({ kind: "other", name: `lines ${i + 1}-${end}`, text: text3, startLine: i + 1, endLine: end });
19206
+ }
19189
19207
  return chunks;
19190
19208
  }
19191
19209
  function summarizeChunk(chunk, filePath) {
@@ -19204,17 +19222,69 @@ function summarizeChunk(chunk, filePath) {
19204
19222
  return `Variable ${chunk.name} in ${location}`;
19205
19223
  case "export":
19206
19224
  return `Default export in ${location}`;
19225
+ case "section":
19226
+ return `Section ${chunk.name} in ${location}`;
19207
19227
  default:
19208
19228
  return `${chunk.kind} in ${location}`;
19209
19229
  }
19210
19230
  }
19211
19231
  function isChunkable(filePath) {
19212
- const ext = filePath.split(".").pop()?.toLowerCase();
19213
- return ext === "ts" || ext === "tsx" || ext === "js" || ext === "jsx";
19232
+ return Boolean(languageForFile(filePath));
19214
19233
  }
19234
+ var LANGUAGE_BY_EXTENSION, TEXT_LIKE_LANGUAGES;
19215
19235
  var init_code_chunker = __esm({
19216
19236
  "src/lib/code-chunker.ts"() {
19217
19237
  "use strict";
19238
+ LANGUAGE_BY_EXTENSION = {
19239
+ c: "c",
19240
+ cc: "cpp",
19241
+ cpp: "cpp",
19242
+ cxx: "cpp",
19243
+ h: "c",
19244
+ hh: "cpp",
19245
+ hpp: "cpp",
19246
+ cs: "csharp",
19247
+ css: "css",
19248
+ scss: "scss",
19249
+ f: "fortran",
19250
+ f90: "fortran",
19251
+ f95: "fortran",
19252
+ go: "go",
19253
+ html: "html",
19254
+ htm: "html",
19255
+ java: "java",
19256
+ js: "javascript",
19257
+ jsx: "javascript",
19258
+ json: "json",
19259
+ kt: "kotlin",
19260
+ kts: "kotlin",
19261
+ lua: "lua",
19262
+ md: "markdown",
19263
+ mdx: "mdx",
19264
+ pas: "pascal",
19265
+ php: "php",
19266
+ py: "python",
19267
+ r: "r",
19268
+ rb: "ruby",
19269
+ rs: "rust",
19270
+ scala: "scala",
19271
+ sc: "scala",
19272
+ sol: "solidity",
19273
+ sql: "sql",
19274
+ svelte: "svelte",
19275
+ swift: "swift",
19276
+ toml: "toml",
19277
+ ts: "typescript",
19278
+ tsx: "typescript",
19279
+ vue: "vue",
19280
+ xml: "xml",
19281
+ yaml: "yaml",
19282
+ yml: "yaml",
19283
+ sh: "shell",
19284
+ bash: "shell",
19285
+ zsh: "shell"
19286
+ };
19287
+ TEXT_LIKE_LANGUAGES = /* @__PURE__ */ new Set(["json", "markdown", "mdx", "toml", "yaml", "xml", "html", "css", "scss", "sql"]);
19218
19288
  }
19219
19289
  });
19220
19290
 
@@ -29986,10 +30056,210 @@ var init_create_bug_report = __esm({
29986
30056
  }
29987
30057
  });
29988
30058
 
30059
+ // src/lib/session-events.ts
30060
+ import { randomUUID as randomUUID9 } from "crypto";
30061
+ async function ensureSessionEventsTable(client) {
30062
+ await client.execute(`
30063
+ CREATE TABLE IF NOT EXISTS session_events (
30064
+ id TEXT PRIMARY KEY,
30065
+ agent_id TEXT NOT NULL,
30066
+ agent_role TEXT NOT NULL,
30067
+ session_id TEXT NOT NULL,
30068
+ session_scope TEXT,
30069
+ project_name TEXT NOT NULL,
30070
+ event_index INTEGER NOT NULL,
30071
+ event_type TEXT NOT NULL,
30072
+ tool_name TEXT,
30073
+ tool_use_id TEXT,
30074
+ content TEXT NOT NULL,
30075
+ payload_json TEXT,
30076
+ has_error INTEGER NOT NULL DEFAULT 0,
30077
+ created_at TEXT NOT NULL
30078
+ )
30079
+ `);
30080
+ await client.execute(`
30081
+ CREATE INDEX IF NOT EXISTS idx_session_events_agent_time
30082
+ ON session_events(agent_id, created_at DESC)
30083
+ `);
30084
+ await client.execute(`
30085
+ CREATE INDEX IF NOT EXISTS idx_session_events_session_index
30086
+ ON session_events(session_id, event_index)
30087
+ `);
30088
+ await client.execute(`
30089
+ CREATE INDEX IF NOT EXISTS idx_session_events_scope_agent_time
30090
+ ON session_events(session_scope, agent_id, created_at DESC)
30091
+ `);
30092
+ }
30093
+ async function listRecentSessionEvents(client, options) {
30094
+ await ensureSessionEventsTable(client);
30095
+ const conditions = ["agent_id = ?"];
30096
+ const args = [options.agentId];
30097
+ if (options.sessionId) {
30098
+ conditions.push("session_id = ?");
30099
+ args.push(options.sessionId);
30100
+ }
30101
+ if (options.eventType) {
30102
+ conditions.push("event_type = ?");
30103
+ args.push(options.eventType);
30104
+ }
30105
+ if (options.projectName && options.projectName !== "all") {
30106
+ conditions.push("project_name = ?");
30107
+ args.push(options.projectName);
30108
+ }
30109
+ const scope = strictSessionScopeFilter(options.sessionScope);
30110
+ const where = `WHERE ${conditions.join(" AND ")}${scope.sql}`;
30111
+ args.push(...scope.args);
30112
+ args.push(Math.min(Math.max(options.limit ?? 20, 1), 100));
30113
+ const result2 = await client.execute({
30114
+ sql: `SELECT id, agent_id, agent_role, session_id, session_scope,
30115
+ project_name, event_index, event_type, tool_name, tool_use_id,
30116
+ content, payload_json, has_error, created_at
30117
+ FROM session_events
30118
+ ${where}
30119
+ ORDER BY created_at DESC, event_index DESC
30120
+ LIMIT ?`,
30121
+ args
30122
+ });
30123
+ return result2.rows.map((row) => ({
30124
+ id: String(row.id),
30125
+ agentId: String(row.agent_id),
30126
+ agentRole: String(row.agent_role),
30127
+ sessionId: String(row.session_id),
30128
+ sessionScope: row.session_scope == null ? null : String(row.session_scope),
30129
+ projectName: String(row.project_name),
30130
+ eventIndex: Number(row.event_index),
30131
+ eventType: String(row.event_type),
30132
+ toolName: row.tool_name == null ? null : String(row.tool_name),
30133
+ toolUseId: row.tool_use_id == null ? null : String(row.tool_use_id),
30134
+ content: String(row.content),
30135
+ payloadJson: row.payload_json == null ? null : String(row.payload_json),
30136
+ hasError: Number(row.has_error) === 1,
30137
+ createdAt: String(row.created_at)
30138
+ }));
30139
+ }
30140
+ var init_session_events = __esm({
30141
+ "src/lib/session-events.ts"() {
30142
+ "use strict";
30143
+ init_task_scope();
30144
+ init_project_name();
30145
+ }
30146
+ });
30147
+
30148
+ // src/mcp/tools/get-session-events.ts
30149
+ import { z as z88 } from "zod";
30150
+ function canReadAgent(activeRole, activeAgent, requestedAgent) {
30151
+ return requestedAgent === activeAgent || activeRole === "COO" || activeRole === "CTO";
30152
+ }
30153
+ function formatEvent(event) {
30154
+ const header = [
30155
+ `[${event.createdAt}]`,
30156
+ `${event.agentId}`,
30157
+ `${event.eventType}`,
30158
+ `session=${event.sessionId}`,
30159
+ `#${event.eventIndex}`,
30160
+ event.toolName ? `tool=${event.toolName}` : "",
30161
+ event.hasError ? "ERROR" : ""
30162
+ ].filter(Boolean).join(" ");
30163
+ return `${header}
30164
+ ${event.content}`;
30165
+ }
30166
+ function registerGetSessionEvents(server) {
30167
+ server.registerTool(
30168
+ "get_session_events",
30169
+ {
30170
+ title: "Get Session Events",
30171
+ 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.",
30172
+ inputSchema: {
30173
+ agent_id: z88.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
30174
+ session_id: z88.string().optional().describe("Optional exact runtime session id."),
30175
+ event_type: EVENT_TYPE.optional().describe("Filter to one event type."),
30176
+ project_name: z88.string().optional().describe("Optional project filter. Pass 'all' for all projects."),
30177
+ limit: z88.number().int().min(1).max(100).default(20).describe("Number of events to return.")
30178
+ }
30179
+ },
30180
+ async ({ agent_id, session_id, event_type, project_name, limit }) => {
30181
+ const active = getActiveAgent();
30182
+ const targetAgent = agent_id?.trim() || active.agentId;
30183
+ if (!canReadAgent(active.agentRole, active.agentId, targetAgent)) {
30184
+ return {
30185
+ content: [{ type: "text", text: "Permission denied: only COO/CTO may read another agent's session events." }],
30186
+ isError: true
30187
+ };
30188
+ }
30189
+ const client = await fastDbInit();
30190
+ const events = await listRecentSessionEvents(client, {
30191
+ agentId: targetAgent,
30192
+ sessionId: session_id,
30193
+ eventType: event_type,
30194
+ projectName: project_name,
30195
+ limit
30196
+ });
30197
+ if (events.length === 0) {
30198
+ return { content: [{ type: "text", text: "No session events found." }] };
30199
+ }
30200
+ return {
30201
+ content: [{ type: "text", text: events.map(formatEvent).join("\n\n---\n\n") }]
30202
+ };
30203
+ }
30204
+ );
30205
+ }
30206
+ function registerGetLastAssistantResponse(server) {
30207
+ server.registerTool(
30208
+ "get_last_assistant_response",
30209
+ {
30210
+ title: "Get Last Assistant Response",
30211
+ description: "Return the exact last assistant response for an agent from the session event journal. Use for 'what was the last thing you said?'",
30212
+ inputSchema: {
30213
+ agent_id: z88.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
30214
+ project_name: z88.string().optional().describe("Optional project filter. Pass 'all' for all projects.")
30215
+ }
30216
+ },
30217
+ async ({ agent_id, project_name }) => {
30218
+ const active = getActiveAgent();
30219
+ const targetAgent = agent_id?.trim() || active.agentId;
30220
+ if (!canReadAgent(active.agentRole, active.agentId, targetAgent)) {
30221
+ return {
30222
+ content: [{ type: "text", text: "Permission denied: only COO/CTO may read another agent's last response." }],
30223
+ isError: true
30224
+ };
30225
+ }
30226
+ const client = await fastDbInit();
30227
+ const events = await listRecentSessionEvents(client, {
30228
+ agentId: targetAgent,
30229
+ eventType: "assistant_response",
30230
+ projectName: project_name,
30231
+ limit: 1
30232
+ });
30233
+ if (events.length === 0) {
30234
+ return { content: [{ type: "text", text: "No assistant response found in the session journal." }] };
30235
+ }
30236
+ return {
30237
+ content: [{ type: "text", text: formatEvent(events[0]) }]
30238
+ };
30239
+ }
30240
+ );
30241
+ }
30242
+ var EVENT_TYPE;
30243
+ var init_get_session_events = __esm({
30244
+ "src/mcp/tools/get-session-events.ts"() {
30245
+ "use strict";
30246
+ init_active_agent();
30247
+ init_fast_db_init();
30248
+ init_session_events();
30249
+ EVENT_TYPE = z88.enum([
30250
+ "user_prompt",
30251
+ "assistant_response",
30252
+ "tool_call",
30253
+ "tool_result",
30254
+ "system_event"
30255
+ ]);
30256
+ }
30257
+ });
30258
+
29989
30259
  // src/lib/code-context-index.ts
29990
30260
  import crypto20 from "crypto";
29991
30261
  import path54 from "path";
29992
- import { existsSync as existsSync43, mkdirSync as mkdirSync21, readFileSync as readFileSync35, statSync as statSync10, writeFileSync as writeFileSync24 } from "fs";
30262
+ import { existsSync as existsSync43, mkdirSync as mkdirSync21, readFileSync as readFileSync35, readdirSync as readdirSync14, statSync as statSync10, writeFileSync as writeFileSync24 } from "fs";
29993
30263
  import { spawnSync } from "child_process";
29994
30264
  function normalizeProjectRoot(projectRoot) {
29995
30265
  return path54.resolve(projectRoot || process.cwd());
@@ -30008,78 +30278,53 @@ function getCodeContextIndexPath(projectRoot) {
30008
30278
  return path54.join(indexDir(), `${rootHash}.json`);
30009
30279
  }
30010
30280
  function currentBranch(projectRoot) {
30011
- const result2 = spawnSync("git", ["branch", "--show-current"], {
30012
- cwd: projectRoot,
30013
- encoding: "utf8",
30014
- timeout: 2e3
30015
- });
30281
+ const result2 = spawnSync("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
30016
30282
  const branch = result2.status === 0 ? result2.stdout.trim() : "";
30017
30283
  return branch || "detached-or-unknown";
30018
30284
  }
30019
30285
  function shouldIgnore(relPath) {
30020
- const parts = relPath.split(path54.sep);
30286
+ const parts = relPath.split(/[\\/]/);
30021
30287
  return parts.some((part) => IGNORE_SEGMENTS.has(part));
30022
30288
  }
30023
- function isCodeFile(relPath) {
30024
- return CODE_EXTENSIONS.has(path54.extname(relPath).toLowerCase());
30289
+ function listRecursive(projectRoot, dir = projectRoot, out = []) {
30290
+ for (const entry of readdirSync14(dir, { withFileTypes: true })) {
30291
+ const abs = path54.join(dir, entry.name);
30292
+ const rel = path54.relative(projectRoot, abs).replaceAll(path54.sep, "/");
30293
+ if (shouldIgnore(rel)) continue;
30294
+ if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
30295
+ else if (entry.isFile()) out.push(rel);
30296
+ }
30297
+ return out;
30025
30298
  }
30026
30299
  function listCodeFiles(projectRoot, maxFiles) {
30027
- const git = spawnSync("git", ["ls-files", "*.ts", "*.tsx", "*.js", "*.jsx"], {
30028
- cwd: projectRoot,
30029
- encoding: "utf8",
30030
- timeout: 5e3,
30031
- maxBuffer: 1024 * 1024 * 8
30032
- });
30300
+ const git = spawnSync("git", ["ls-files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
30033
30301
  let files = [];
30034
30302
  if (git.status === 0 && git.stdout.trim()) {
30035
30303
  files = git.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
30036
30304
  } else {
30037
- const rg = spawnSync("rg", ["--files", "-g", "*.ts", "-g", "*.tsx", "-g", "*.js", "-g", "*.jsx"], {
30038
- cwd: projectRoot,
30039
- encoding: "utf8",
30040
- timeout: 5e3,
30041
- maxBuffer: 1024 * 1024 * 8
30042
- });
30043
- if (rg.status === 0 && rg.stdout.trim()) {
30044
- files = rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
30045
- }
30305
+ const rg = spawnSync("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
30306
+ files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
30046
30307
  }
30047
- return files.filter((file) => isCodeFile(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
30308
+ return files.map((file) => file.replaceAll(path54.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
30048
30309
  }
30049
30310
  function parseImportPaths2(importText) {
30050
30311
  const paths = [];
30051
- const fromRegex = /from\s+["']([^"']+)["']/g;
30052
- let match;
30053
- while ((match = fromRegex.exec(importText)) !== null) paths.push(match[1]);
30054
- const bareRegex = /^import\s+["']([^"']+)["']/gm;
30055
- while ((match = bareRegex.exec(importText)) !== null) {
30056
- if (!paths.includes(match[1])) paths.push(match[1]);
30057
- }
30058
- const requireRegex = /require\(["']([^"']+)["']\)/g;
30059
- while ((match = requireRegex.exec(importText)) !== null) {
30060
- if (!paths.includes(match[1])) paths.push(match[1]);
30312
+ const patterns = [/from\s+["']([^"']+)["']/g, /^import\s+["']([^"']+)["']/gm, /require\(["']([^"']+)["']\)/g, /use\s+([A-Za-z0-9_:]+)::/g, /#include\s+[<"]([^>"]+)[>"]/g];
30313
+ for (const regex of patterns) {
30314
+ let match;
30315
+ while ((match = regex.exec(importText)) !== null) if (!paths.includes(match[1])) paths.push(match[1]);
30061
30316
  }
30062
30317
  return paths;
30063
30318
  }
30064
30319
  function resolveImport(fromFile, importPath, allFiles) {
30065
30320
  if (!importPath.startsWith(".")) return null;
30066
30321
  const base = path54.posix.normalize(path54.posix.join(path54.posix.dirname(fromFile.replaceAll(path54.sep, "/")), importPath));
30067
- const withoutKnownExt = base.replace(/\.(?:js|jsx|ts|tsx)$/, "");
30068
- const candidates = [
30069
- base,
30070
- `${withoutKnownExt}.ts`,
30071
- `${withoutKnownExt}.tsx`,
30072
- `${withoutKnownExt}.js`,
30073
- `${withoutKnownExt}.jsx`,
30074
- `${base}.ts`,
30075
- `${base}.tsx`,
30076
- `${base}.js`,
30077
- `${base}.jsx`,
30078
- path54.posix.join(base, "index.ts"),
30079
- path54.posix.join(base, "index.tsx"),
30080
- path54.posix.join(base, "index.js"),
30081
- path54.posix.join(base, "index.jsx")
30082
- ];
30322
+ const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
30323
+ const candidates = [base];
30324
+ 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"]) {
30325
+ candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
30326
+ }
30327
+ for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path54.posix.join(base, indexName));
30083
30328
  return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
30084
30329
  }
30085
30330
  function symbolId(filePath, chunk) {
@@ -30105,39 +30350,32 @@ function buildFileRecord(projectRoot, relPath, allFiles, previous) {
30105
30350
  try {
30106
30351
  stat = statSync10(absPath);
30107
30352
  } catch {
30108
- return null;
30353
+ return { record: null, reused: false };
30109
30354
  }
30110
- if (!stat.isFile()) return null;
30355
+ if (!stat.isFile()) return { record: null, reused: false };
30356
+ const language = languageForFile(relPath);
30357
+ if (!language || !isChunkable(relPath)) return { record: null, reused: false };
30111
30358
  const source = readFileSync35(absPath, "utf8");
30112
30359
  const hash = hashText(source);
30113
- if (previous && previous.hash === hash && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size) {
30114
- return previous;
30360
+ if (previous && previous.hash === hash && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size && previous.language === language) {
30361
+ return { record: previous, reused: true };
30115
30362
  }
30116
- if (!isChunkable(relPath)) return null;
30117
30363
  const chunks = chunkSourceFile(source, relPath);
30118
30364
  const imports = chunks.filter((chunk) => chunk.kind === "import").flatMap((chunk) => parseImportPaths2(chunk.text));
30119
30365
  const resolvedImports = imports.map((importPath) => resolveImport(relPath, importPath, allFiles)).filter((file) => Boolean(file));
30120
- const symbols = chunks.filter((chunk) => chunk.kind !== "import" && chunk.name !== "(unknown)").map((chunk) => ({
30366
+ const symbols = chunks.filter((chunk) => chunk.name !== "(unknown)").map((chunk) => ({
30121
30367
  id: symbolId(relPath, chunk),
30122
30368
  name: chunk.name,
30123
30369
  kind: chunk.kind,
30124
30370
  filePath: relPath,
30371
+ language,
30125
30372
  startLine: chunk.startLine,
30126
30373
  endLine: chunk.endLine,
30127
30374
  summary: summarizeChunk(chunk, relPath),
30128
- text: chunk.text.slice(0, 6e3),
30375
+ text: chunk.text.slice(0, 8e3),
30129
30376
  comment: chunk.comment
30130
30377
  }));
30131
- return {
30132
- path: relPath,
30133
- absPath,
30134
- hash,
30135
- mtimeMs: stat.mtimeMs,
30136
- size: stat.size,
30137
- imports,
30138
- resolvedImports,
30139
- symbols
30140
- };
30378
+ return { record: { path: relPath, absPath, language, hash, mtimeMs: stat.mtimeMs, size: stat.size, imports, resolvedImports, symbols }, reused: false };
30141
30379
  }
30142
30380
  function buildCodeContextIndex(options = {}) {
30143
30381
  const projectRoot = normalizeProjectRoot(options.projectRoot);
@@ -30147,17 +30385,27 @@ function buildCodeContextIndex(options = {}) {
30147
30385
  const files = listCodeFiles(projectRoot, maxFiles);
30148
30386
  const allFiles = new Set(files.map((file) => file.replaceAll(path54.sep, "/")));
30149
30387
  const fileRecords = {};
30388
+ let rebuiltFiles = 0;
30389
+ let reusedFiles = 0;
30390
+ let skippedFiles = 0;
30150
30391
  for (const rel of files) {
30151
30392
  const normalized = rel.replaceAll(path54.sep, "/");
30152
- const record = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
30153
- if (record) fileRecords[normalized] = record;
30154
- }
30393
+ const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
30394
+ if (record) {
30395
+ fileRecords[normalized] = record;
30396
+ if (reused) reusedFiles++;
30397
+ else rebuiltFiles++;
30398
+ } else skippedFiles++;
30399
+ }
30400
+ const languageBreakdown = {};
30401
+ for (const file of Object.values(fileRecords)) languageBreakdown[file.language] = (languageBreakdown[file.language] ?? 0) + 1;
30155
30402
  const index = {
30156
30403
  version: INDEX_VERSION,
30157
30404
  projectRoot,
30158
30405
  rootHash: hashText(projectRoot).slice(0, 16),
30159
30406
  branch,
30160
30407
  indexedAt: (/* @__PURE__ */ new Date()).toISOString(),
30408
+ stats: { filesSeen: files.length, filesIndexed: Object.keys(fileRecords).length, rebuiltFiles, reusedFiles, skippedFiles, languageBreakdown },
30161
30409
  files: fileRecords
30162
30410
  };
30163
30411
  saveIndex(index);
@@ -30185,13 +30433,60 @@ function loadOrBuildCodeContextIndex(options = {}) {
30185
30433
  }
30186
30434
  return buildCodeContextIndex(options);
30187
30435
  }
30436
+ function normalizeLanguage(language) {
30437
+ return language.toLowerCase().replace(/^c\+\+$/, "cpp").replace(/^c#$/, "csharp").replace(/^js$/, "javascript").replace(/^ts$/, "typescript");
30438
+ }
30188
30439
  function tokenize(query) {
30189
- return query.toLowerCase().split(/[^a-z0-9_.$/-]+/).map((s) => s.trim()).filter((s) => s.length >= 2);
30440
+ const raw = query.toLowerCase().replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^a-z0-9_.$/-]+/).map((s) => s.trim()).filter((s) => s.length >= 2);
30441
+ const expanded = /* @__PURE__ */ new Set();
30442
+ for (const term of raw) {
30443
+ expanded.add(term);
30444
+ for (const part of term.split(/[_.$/-]+/)) if (part.length >= 2) expanded.add(part);
30445
+ const dashed = term.replace(/[_.$/]+/g, "-");
30446
+ if (dashed.length >= 2) expanded.add(dashed);
30447
+ if (!term.includes("-")) expanded.add(`${term}s`);
30448
+ if (term.endsWith("s") && term.length > 3) expanded.add(term.slice(0, -1));
30449
+ }
30450
+ return [...expanded];
30451
+ }
30452
+ function ngrams(terms) {
30453
+ const grams = [...terms];
30454
+ for (let i = 0; i < terms.length - 1; i++) grams.push(`${terms[i]} ${terms[i + 1]}`);
30455
+ return grams;
30456
+ }
30457
+ function globToRegex(pattern) {
30458
+ let out = "";
30459
+ for (let i = 0; i < pattern.length; i++) {
30460
+ const ch = pattern[i];
30461
+ const next = pattern[i + 1];
30462
+ if (ch === "*" && next === "*") {
30463
+ out += ".*";
30464
+ i++;
30465
+ continue;
30466
+ }
30467
+ if (ch === "*") {
30468
+ out += "[^/]*";
30469
+ continue;
30470
+ }
30471
+ if (".+^${}()|[]\\".includes(ch)) out += `\\${ch}`;
30472
+ else out += ch;
30473
+ }
30474
+ return new RegExp(`^${out}$`);
30475
+ }
30476
+ function matchesPath(filePath, patterns) {
30477
+ if (!patterns || patterns.length === 0) return true;
30478
+ const normalized = filePath.replaceAll(path54.sep, "/");
30479
+ return patterns.some((pattern) => {
30480
+ const p = pattern.replaceAll(path54.sep, "/").replace(/^\.\//, "");
30481
+ return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
30482
+ });
30190
30483
  }
30191
30484
  function scoreSymbol(symbol, terms) {
30485
+ const grams = ngrams(terms);
30192
30486
  const haystacks = {
30193
30487
  name: symbol.name.toLowerCase(),
30194
30488
  path: symbol.filePath.toLowerCase(),
30489
+ language: symbol.language.toLowerCase(),
30195
30490
  summary: symbol.summary.toLowerCase(),
30196
30491
  text: symbol.text.toLowerCase()
30197
30492
  };
@@ -30199,41 +30494,73 @@ function scoreSymbol(symbol, terms) {
30199
30494
  const matches = [];
30200
30495
  for (const term of terms) {
30201
30496
  if (haystacks.name === term) {
30202
- score += 80;
30497
+ score += 100;
30203
30498
  matches.push(`name=${term}`);
30204
30499
  continue;
30205
30500
  }
30206
30501
  if (haystacks.name.includes(term)) {
30207
- score += 40;
30502
+ score += 45;
30208
30503
  matches.push(`name~${term}`);
30209
30504
  }
30210
30505
  if (haystacks.path.includes(term)) {
30211
- score += 15;
30506
+ score += 18;
30212
30507
  matches.push(`path~${term}`);
30213
30508
  }
30509
+ if (haystacks.language === term) {
30510
+ score += 18;
30511
+ matches.push(`language=${term}`);
30512
+ }
30214
30513
  if (haystacks.summary.includes(term)) {
30215
- score += 12;
30514
+ score += 16;
30216
30515
  matches.push(`summary~${term}`);
30217
30516
  }
30218
30517
  if (haystacks.text.includes(term)) {
30219
- score += 4;
30518
+ score += 5;
30220
30519
  matches.push(`text~${term}`);
30221
30520
  }
30222
30521
  }
30522
+ for (const gram of grams.filter((g) => g.includes(" "))) {
30523
+ if (haystacks.text.includes(gram) || haystacks.summary.includes(gram)) {
30524
+ score += 20;
30525
+ matches.push(`phrase~${gram}`);
30526
+ }
30527
+ }
30528
+ const uniqueMatches = new Set(matches.map((m) => m.replace(/^[^=~]+[=~]/, ""))).size;
30529
+ score += uniqueMatches * 3;
30223
30530
  return { score, matches };
30224
30531
  }
30532
+ function filteredFiles(index, options = {}) {
30533
+ const languages = options.languages?.map(normalizeLanguage).filter(Boolean);
30534
+ return Object.values(index.files).filter((file) => {
30535
+ if (languages && languages.length > 0 && !languages.includes(normalizeLanguage(file.language))) return false;
30536
+ return matchesPath(file.path, options.paths);
30537
+ });
30538
+ }
30225
30539
  function searchCodeContext(query, options = {}) {
30226
30540
  const terms = tokenize(query);
30227
30541
  if (terms.length === 0) return [];
30228
- const index = loadOrBuildCodeContextIndex(options);
30542
+ const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
30229
30543
  const results = [];
30230
- for (const file of Object.values(index.files)) {
30544
+ for (const file of filteredFiles(index, options)) {
30231
30545
  for (const symbol of file.symbols) {
30232
30546
  const scored = scoreSymbol(symbol, terms);
30233
- if (scored.score > 0) results.push({ symbol, score: scored.score, matches: scored.matches });
30547
+ if (scored.score > 0) {
30548
+ results.push({
30549
+ symbol,
30550
+ score: scored.score,
30551
+ matches: scored.matches,
30552
+ filePath: symbol.filePath,
30553
+ language: symbol.language,
30554
+ content: symbol.text,
30555
+ startLine: symbol.startLine,
30556
+ endLine: symbol.endLine
30557
+ });
30558
+ }
30234
30559
  }
30235
30560
  }
30236
- return results.sort((a, b) => b.score - a.score || a.symbol.filePath.localeCompare(b.symbol.filePath)).slice(0, options.limit ?? 20);
30561
+ const offset = Math.max(0, options.offset ?? 0);
30562
+ const limit = options.limit ?? 20;
30563
+ return results.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
30237
30564
  }
30238
30565
  function dependentsMap(index) {
30239
30566
  const map = /* @__PURE__ */ new Map();
@@ -30261,19 +30588,10 @@ function traceCodeSymbol(symbolName, options = {}) {
30261
30588
  const index = loadOrBuildCodeContextIndex(options);
30262
30589
  const matches = findSymbols(index, symbolName, options.limit ?? 20);
30263
30590
  const dependents = dependentsMap(index);
30264
- return {
30265
- query: symbolName,
30266
- matches,
30267
- definitions: matches.map((symbol) => {
30268
- const file = index.files[symbol.filePath];
30269
- return {
30270
- symbol,
30271
- imports: file.resolvedImports,
30272
- dependents: [...dependents.get(symbol.filePath) ?? /* @__PURE__ */ new Set()].sort(),
30273
- relatedSymbols: file.symbols.filter((s) => s.id !== symbol.id).slice(0, 20)
30274
- };
30275
- })
30276
- };
30591
+ return { query: symbolName, matches, definitions: matches.map((symbol) => {
30592
+ const file = index.files[symbol.filePath];
30593
+ return { symbol, imports: file.resolvedImports, dependents: [...dependents.get(symbol.filePath) ?? /* @__PURE__ */ new Set()].sort(), relatedSymbols: file.symbols.filter((s) => s.id !== symbol.id).slice(0, 20) };
30594
+ }) };
30277
30595
  }
30278
30596
  function resolveTargetFile(index, input) {
30279
30597
  if (input.filePath) {
@@ -30313,20 +30631,10 @@ function analyzeBlastRadius(input) {
30313
30631
  const lower = file.toLowerCase();
30314
30632
  return (lower.includes("test") || lower.includes("spec")) && (lower.includes(targetBase) || (symbolLower ? index.files[file].symbols.some((s) => s.text.toLowerCase().includes(symbolLower)) : false));
30315
30633
  });
30316
- for (const test of tests) {
30317
- if (!impacted.has(test)) impacted.set(test, { distance: 1, reason: "related test/spec" });
30318
- }
30634
+ for (const test of tests) if (!impacted.has(test)) impacted.set(test, { distance: 1, reason: "related test/spec" });
30319
30635
  const impactedFiles = [...impacted.entries()].map(([filePath, value]) => ({ filePath, distance: value.distance, reason: value.reason })).sort((a, b) => a.distance - b.distance || a.filePath.localeCompare(b.filePath));
30320
30636
  const nonTestImpacted = impactedFiles.filter((f) => !f.filePath.match(/(test|spec)\./i)).length;
30321
- const riskLevel = nonTestImpacted >= 8 ? "high" : nonTestImpacted >= 4 ? "medium" : "low";
30322
- return {
30323
- target: target.target,
30324
- targetFile: target.filePath,
30325
- impactedFiles,
30326
- tests,
30327
- symbolsInTarget: index.files[target.filePath]?.symbols ?? [],
30328
- riskLevel
30329
- };
30637
+ return { target: target.target, targetFile: target.filePath, impactedFiles, tests, symbolsInTarget: index.files[target.filePath]?.symbols ?? [], riskLevel: nonTestImpacted >= 8 ? "high" : nonTestImpacted >= 4 ? "medium" : "low" };
30330
30638
  }
30331
30639
  function getCodeContextStats(options = {}) {
30332
30640
  const index = loadOrBuildCodeContextIndex(options);
@@ -30334,27 +30642,31 @@ function getCodeContextStats(options = {}) {
30334
30642
  projectRoot: index.projectRoot,
30335
30643
  branch: index.branch,
30336
30644
  indexedAt: index.indexedAt,
30645
+ indexAgeMs: Math.max(0, Date.now() - Date.parse(index.indexedAt)),
30337
30646
  files: Object.keys(index.files).length,
30338
30647
  symbols: Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0),
30339
30648
  imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
30649
+ languageBreakdown: index.stats.languageBreakdown,
30650
+ rebuiltFiles: index.stats.rebuiltFiles,
30651
+ reusedFiles: index.stats.reusedFiles,
30652
+ skippedFiles: index.stats.skippedFiles,
30340
30653
  indexPath: getCodeContextIndexPath(index.projectRoot)
30341
30654
  };
30342
30655
  }
30343
- var INDEX_VERSION, DEFAULT_MAX_FILES, CODE_EXTENSIONS, IGNORE_SEGMENTS;
30656
+ var INDEX_VERSION, DEFAULT_MAX_FILES, IGNORE_SEGMENTS;
30344
30657
  var init_code_context_index = __esm({
30345
30658
  "src/lib/code-context-index.ts"() {
30346
30659
  "use strict";
30347
30660
  init_config();
30348
30661
  init_code_chunker();
30349
- INDEX_VERSION = 1;
30350
- DEFAULT_MAX_FILES = 2e3;
30351
- CODE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
30352
- IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build"]);
30662
+ INDEX_VERSION = 2;
30663
+ DEFAULT_MAX_FILES = 5e3;
30664
+ IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
30353
30665
  }
30354
30666
  });
30355
30667
 
30356
30668
  // src/mcp/tools/code-context.ts
30357
- import { z as z88 } from "zod";
30669
+ import { z as z89 } from "zod";
30358
30670
  function errorResult10(text3) {
30359
30671
  return { content: [{ type: "text", text: text3 }], isError: true };
30360
30672
  }
@@ -30366,17 +30678,21 @@ function registerCodeContext(server) {
30366
30678
  title: "Code Context",
30367
30679
  description: "Persistent codebase context engine. One consolidated tool to avoid MCP bloat. Actions: index, search, trace, blast_radius, stats.",
30368
30680
  inputSchema: {
30369
- action: z88.enum(["index", "search", "trace", "blast_radius", "stats"]).describe("Code context operation"),
30370
- project_root: z88.string().optional().describe("Repository root. Defaults to current working directory."),
30371
- query: z88.string().optional().describe("Search query for action=search"),
30372
- symbol: z88.string().optional().describe("Symbol/function/class/type name for trace or blast_radius"),
30373
- file_path: z88.string().optional().describe("File path for blast_radius"),
30374
- force: z88.boolean().optional().describe("Force rebuild before answering"),
30375
- limit: z88.coerce.number().int().min(1).max(100).optional().describe("Max results"),
30376
- depth: z88.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
30377
- max_files: z88.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index")
30378
- }
30379
- }, async ({ action, project_root, query, symbol, file_path, force, limit, depth, max_files }) => {
30681
+ action: z89.enum(["index", "search", "trace", "blast_radius", "stats"]).describe("Code context operation"),
30682
+ project_root: z89.string().optional().describe("Repository root. Defaults to current working directory."),
30683
+ query: z89.string().optional().describe("Search query for action=search"),
30684
+ symbol: z89.string().optional().describe("Symbol/function/class/type name for trace or blast_radius"),
30685
+ file_path: z89.string().optional().describe("File path for blast_radius"),
30686
+ force: z89.boolean().optional().describe("Force rebuild before answering"),
30687
+ limit: z89.coerce.number().int().min(1).max(100).optional().describe("Max results"),
30688
+ offset: z89.coerce.number().int().min(0).optional().describe("Search pagination offset"),
30689
+ refresh_index: z89.boolean().optional().describe("Refresh/rebuild index before searching"),
30690
+ languages: z89.array(z89.string()).optional().describe('Language filters, e.g. ["python", "typescript"]'),
30691
+ paths: z89.array(z89.string()).optional().describe("Path/glob filters"),
30692
+ depth: z89.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
30693
+ max_files: z89.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index")
30694
+ }
30695
+ }, async ({ action, project_root, query, symbol, file_path, force, limit, offset, refresh_index, languages, paths, depth, max_files }) => {
30380
30696
  const opts = { projectRoot: project_root, force, maxFiles: max_files };
30381
30697
  if (action === "index") {
30382
30698
  const index = buildCodeContextIndex(opts);
@@ -30394,7 +30710,15 @@ function registerCodeContext(server) {
30394
30710
  }
30395
30711
  if (action === "search") {
30396
30712
  if (!query) return errorResult10('code_context action "search" requires query');
30397
- return jsonResult({ query, results: searchCodeContext(query, { ...opts, limit }) });
30713
+ return jsonResult({
30714
+ query,
30715
+ limit: limit ?? 20,
30716
+ offset: offset ?? 0,
30717
+ refresh_index: refresh_index ?? false,
30718
+ languages: languages ?? [],
30719
+ paths: paths ?? [],
30720
+ results: searchCodeContext(query, { ...opts, limit, offset, refreshIndex: refresh_index, languages, paths })
30721
+ });
30398
30722
  }
30399
30723
  if (action === "trace") {
30400
30724
  if (!symbol) return errorResult10('code_context action "trace" requires symbol');
@@ -30417,7 +30741,7 @@ var init_code_context = __esm({
30417
30741
  });
30418
30742
 
30419
30743
  // src/mcp/tools/support-inbox.ts
30420
- import { z as z89 } from "zod";
30744
+ import { z as z90 } from "zod";
30421
30745
  function adminToken() {
30422
30746
  return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN;
30423
30747
  }
@@ -30456,9 +30780,9 @@ function registerListBugReports(server) {
30456
30780
  title: "List Bug Reports",
30457
30781
  description: "AskExe-internal only: list incoming customer bug reports from the support inbox.",
30458
30782
  inputSchema: {
30459
- status: z89.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
30460
- severity: z89.enum(["p0", "p1", "p2", "p3"]).optional(),
30461
- limit: z89.number().int().min(1).max(100).default(25)
30783
+ status: z90.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
30784
+ severity: z90.enum(["p0", "p1", "p2", "p3"]).optional(),
30785
+ limit: z90.number().int().min(1).max(100).default(25)
30462
30786
  }
30463
30787
  },
30464
30788
  async ({ status, severity, limit }) => {
@@ -30477,7 +30801,7 @@ function registerGetBugReport(server) {
30477
30801
  {
30478
30802
  title: "Get Bug Report",
30479
30803
  description: "AskExe-internal only: fetch one customer bug report with full markdown payload.",
30480
- inputSchema: { id: z89.string().min(8) }
30804
+ inputSchema: { id: z90.string().min(8) }
30481
30805
  },
30482
30806
  async ({ id }) => {
30483
30807
  const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`);
@@ -30492,12 +30816,12 @@ function registerTriageBugReport(server) {
30492
30816
  title: "Triage Bug Report",
30493
30817
  description: "AskExe-internal only: update bug report status and link task/commit/release metadata.",
30494
30818
  inputSchema: {
30495
- id: z89.string().min(8),
30819
+ id: z90.string().min(8),
30496
30820
  status: STATUS.optional(),
30497
- triage_notes: z89.string().optional(),
30498
- linked_task_id: z89.string().optional(),
30499
- linked_commit: z89.string().optional(),
30500
- fixed_version: z89.string().optional()
30821
+ triage_notes: z90.string().optional(),
30822
+ linked_task_id: z90.string().optional(),
30823
+ linked_commit: z90.string().optional(),
30824
+ fixed_version: z90.string().optional()
30501
30825
  }
30502
30826
  },
30503
30827
  async ({ id, status, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
@@ -30514,7 +30838,7 @@ var init_support_inbox = __esm({
30514
30838
  "src/mcp/tools/support-inbox.ts"() {
30515
30839
  "use strict";
30516
30840
  DEFAULT_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
30517
- STATUS = z89.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
30841
+ STATUS = z90.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
30518
30842
  }
30519
30843
  });
30520
30844
 
@@ -30588,6 +30912,8 @@ var init_tool_gates = __esm({
30588
30912
  registerStoreDecision: "core",
30589
30913
  registerGetDecision: "core",
30590
30914
  registerCreateBugReport: "core",
30915
+ registerGetSessionEvents: "core",
30916
+ registerGetLastAssistantResponse: "core",
30591
30917
  registerCodeContext: "graph-read",
30592
30918
  registerListBugReports: "admin",
30593
30919
  registerGetBugReport: "admin",
@@ -30843,6 +31169,8 @@ function registerAllTools(server) {
30843
31169
  gate("registerStoreDecision", registerStoreDecision);
30844
31170
  gate("registerGetDecision", registerGetDecision);
30845
31171
  gate("registerCreateBugReport", registerCreateBugReport);
31172
+ gate("registerGetSessionEvents", registerGetSessionEvents);
31173
+ gate("registerGetLastAssistantResponse", registerGetLastAssistantResponse);
30846
31174
  gate("registerCodeContext", registerCodeContext);
30847
31175
  if (process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN) {
30848
31176
  gate("registerListBugReports", registerListBugReports);
@@ -30988,6 +31316,7 @@ var init_register_tools = __esm({
30988
31316
  init_activate_license();
30989
31317
  init_query_company_brain();
30990
31318
  init_create_bug_report();
31319
+ init_get_session_events();
30991
31320
  init_code_context();
30992
31321
  init_support_inbox();
30993
31322
  init_tool_gates();
@@ -32055,7 +32384,7 @@ init_daemon_auth();
32055
32384
  import os24 from "os";
32056
32385
  import net2 from "net";
32057
32386
  import { createServer as createHttpServer } from "http";
32058
- import { randomUUID as randomUUID9 } from "crypto";
32387
+ import { randomUUID as randomUUID10 } from "crypto";
32059
32388
  import { writeFileSync as writeFileSync28, unlinkSync as unlinkSync14, mkdirSync as mkdirSync24, existsSync as existsSync46, readFileSync as readFileSync39, chmodSync as chmodSync2 } from "fs";
32060
32389
  import path59 from "path";
32061
32390
 
@@ -32139,6 +32468,7 @@ var DAEMON_TOKEN_ENV2 = "EXE_DAEMON_TOKEN";
32139
32468
  var _context = null;
32140
32469
  var _model = null;
32141
32470
  var _llama = null;
32471
+ var _shuttingDown = false;
32142
32472
  var _daemonToken = "";
32143
32473
  var MAX_QUEUE_SIZE = 1e3;
32144
32474
  var highQueue = [];
@@ -32240,6 +32570,8 @@ function checkIdle() {
32240
32570
  }
32241
32571
  }
32242
32572
  async function shutdown() {
32573
+ if (_shuttingDown) return;
32574
+ _shuttingDown = true;
32243
32575
  resetIdleTimer2();
32244
32576
  flushToDisk();
32245
32577
  try {
@@ -32252,6 +32584,19 @@ async function shutdown() {
32252
32584
  disposeShards2();
32253
32585
  } catch {
32254
32586
  }
32587
+ if (process.platform === "darwin" && _llama) {
32588
+ try {
32589
+ unlinkSync14(SOCKET_PATH2);
32590
+ } catch {
32591
+ }
32592
+ try {
32593
+ unlinkSync14(PID_PATH4);
32594
+ } catch {
32595
+ }
32596
+ process.stderr.write("[exed] Shutdown complete (darwin fast native exit).\n");
32597
+ process.kill(process.pid, "SIGKILL");
32598
+ process.exit(0);
32599
+ }
32255
32600
  if (_context) {
32256
32601
  try {
32257
32602
  await _context.dispose();
@@ -32266,7 +32611,13 @@ async function shutdown() {
32266
32611
  }
32267
32612
  _model = null;
32268
32613
  }
32269
- _llama = null;
32614
+ if (_llama) {
32615
+ try {
32616
+ await _llama.dispose();
32617
+ } catch {
32618
+ }
32619
+ _llama = null;
32620
+ }
32270
32621
  try {
32271
32622
  unlinkSync14(SOCKET_PATH2);
32272
32623
  } catch {
@@ -32423,7 +32774,7 @@ async function handleBatchWriteMemory(socket, requestId, entries) {
32423
32774
  }
32424
32775
  }
32425
32776
  async function writeMemoryRecord(entry) {
32426
- const id = randomUUID9();
32777
+ const id = randomUUID10();
32427
32778
  const now2 = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
32428
32779
  const governed = governMemoryRecord({
32429
32780
  id,
@@ -32745,16 +33096,22 @@ async function startMcpHttpServer() {
32745
33096
  for (const [sid, lastSeen] of sessionLastSeen.entries()) {
32746
33097
  if (now2 - lastSeen > MCP_SESSION_TTL_MS) closeMcpSession2(sid, "session_expired");
32747
33098
  }
32748
- }, sendJsonRpcError2 = function(res, status, message, extra) {
33099
+ }, getJsonRpcId2 = function(body) {
33100
+ if (body && typeof body === "object" && !Array.isArray(body) && "id" in body) {
33101
+ const id = body.id;
33102
+ if (typeof id === "string" || typeof id === "number") return id;
33103
+ }
33104
+ return "unknown";
33105
+ }, sendJsonRpcError2 = function(res, status, message, id, extra) {
32749
33106
  res.writeHead(status, { "Content-Type": "application/json" });
32750
33107
  res.end(JSON.stringify({
32751
33108
  jsonrpc: "2.0",
32752
33109
  error: { code: -32e3, message },
32753
- id: null
33110
+ id
32754
33111
  }));
32755
33112
  recordMcpHttpEvent({ level: "warn", message, status, ...extra });
32756
33113
  };
32757
- var parseDurationMs = parseDurationMs2, closeMcpSession = closeMcpSession2, sweepStaleMcpSessions = sweepStaleMcpSessions2, sendJsonRpcError = sendJsonRpcError2;
33114
+ var parseDurationMs = parseDurationMs2, closeMcpSession = closeMcpSession2, sweepStaleMcpSessions = sweepStaleMcpSessions2, getJsonRpcId = getJsonRpcId2, sendJsonRpcError = sendJsonRpcError2;
32758
33115
  const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
32759
33116
  const { StreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
32760
33117
  const { isInitializeRequest } = await import("@modelcontextprotocol/sdk/types.js");
@@ -32783,14 +33140,14 @@ async function startMcpHttpServer() {
32783
33140
  const url = new URL(req.url || "/", `http://127.0.0.1:${MCP_HTTP_PORT}`);
32784
33141
  const authHeader = req.headers.authorization;
32785
33142
  if (!authHeader || authHeader !== `Bearer ${_daemonToken}`) {
32786
- sendJsonRpcError2(res, 401, "Unauthorized: invalid or missing daemon token", {
33143
+ sendJsonRpcError2(res, 401, "Unauthorized: invalid or missing daemon token", "unknown", {
32787
33144
  method: req.method,
32788
33145
  path: url.pathname
32789
33146
  });
32790
33147
  return;
32791
33148
  }
32792
33149
  if (url.pathname !== "/mcp") {
32793
- sendJsonRpcError2(res, 404, "Not Found: MCP endpoint is /mcp", {
33150
+ sendJsonRpcError2(res, 404, "Not Found: MCP endpoint is /mcp", "unknown", {
32794
33151
  method: req.method,
32795
33152
  path: url.pathname
32796
33153
  });
@@ -32814,7 +33171,7 @@ async function startMcpHttpServer() {
32814
33171
  });
32815
33172
  parsedBody = body ? JSON.parse(body) : void 0;
32816
33173
  } catch (err) {
32817
- sendJsonRpcError2(res, 400, "Bad Request: invalid JSON body", {
33174
+ sendJsonRpcError2(res, 400, "Bad Request: invalid JSON body", "unknown", {
32818
33175
  method: req.method,
32819
33176
  agentId,
32820
33177
  agentRole,
@@ -32830,7 +33187,7 @@ async function startMcpHttpServer() {
32830
33187
  sessionLastSeen.set(sessionId, Date.now());
32831
33188
  } else if (!sessionId && req.method === "POST" && isInitializeRequest(parsedBody)) {
32832
33189
  transport = new StreamableHTTPServerTransport({
32833
- sessionIdGenerator: () => randomUUID9(),
33190
+ sessionIdGenerator: () => randomUUID10(),
32834
33191
  onsessioninitialized: (sid) => {
32835
33192
  transports.set(sid, transport);
32836
33193
  sessionLastSeen.set(sid, Date.now());
@@ -32855,7 +33212,7 @@ async function startMcpHttpServer() {
32855
33212
  await sessionMcp.connect(transport);
32856
33213
  } else {
32857
33214
  const message = sessionId ? "Bad Request: MCP session is stale or unknown; reconnect MCP client" : "Bad Request: missing MCP session; initialize before tool calls";
32858
- sendJsonRpcError2(res, 400, message, {
33215
+ sendJsonRpcError2(res, 400, message, getJsonRpcId2(parsedBody), {
32859
33216
  method: req.method,
32860
33217
  hasSessionId: Boolean(sessionId),
32861
33218
  sessionId,
@@ -32914,7 +33271,7 @@ async function startMcpHttpServer() {
32914
33271
  error: err instanceof Error ? err.message : String(err)
32915
33272
  });
32916
33273
  if (!res.headersSent) {
32917
- sendJsonRpcError2(res, 500, "Internal Server Error: MCP request failed", {
33274
+ sendJsonRpcError2(res, 500, "Internal Server Error: MCP request failed", getJsonRpcId2(parsedBody), {
32918
33275
  method: req.method,
32919
33276
  sessionId,
32920
33277
  agentId,
@@ -33582,8 +33939,31 @@ function startBackgroundJobGuardrails() {
33582
33939
  timer.unref();
33583
33940
  process.stderr.write("[exed] Background job guardrails started (every 60s)\n");
33584
33941
  }
33585
- process.on("SIGINT", () => void shutdown());
33586
- process.on("SIGTERM", () => void shutdown());
33942
+ function handleSignalShutdown() {
33943
+ if (process.platform === "darwin" && _llama) {
33944
+ if (_shuttingDown) return;
33945
+ _shuttingDown = true;
33946
+ resetIdleTimer2();
33947
+ try {
33948
+ flushToDisk();
33949
+ } catch {
33950
+ }
33951
+ try {
33952
+ unlinkSync14(SOCKET_PATH2);
33953
+ } catch {
33954
+ }
33955
+ try {
33956
+ unlinkSync14(PID_PATH4);
33957
+ } catch {
33958
+ }
33959
+ process.stderr.write("[exed] Shutdown complete (darwin signal fast native exit).\n");
33960
+ process.kill(process.pid, "SIGKILL");
33961
+ process.exit(0);
33962
+ }
33963
+ void shutdown();
33964
+ }
33965
+ process.on("SIGINT", handleSignalShutdown);
33966
+ process.on("SIGTERM", handleSignalShutdown);
33587
33967
  function checkExistingDaemon() {
33588
33968
  try {
33589
33969
  if (!existsSync46(PID_PATH4)) return false;