@askexenow/exe-os 0.9.61 → 0.9.63

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 (103) hide show
  1. package/deploy/stack-manifests/v0.9.json +62 -13
  2. package/dist/bin/backfill-conversations.js +210 -2
  3. package/dist/bin/backfill-responses.js +210 -2
  4. package/dist/bin/backfill-vectors.js +47 -2
  5. package/dist/bin/cleanup-stale-review-tasks.js +210 -2
  6. package/dist/bin/cli.js +488 -25
  7. package/dist/bin/exe-agent-config.js +2 -2
  8. package/dist/bin/exe-agent.js +2 -2
  9. package/dist/bin/exe-assign.js +210 -2
  10. package/dist/bin/exe-boot.js +47 -2
  11. package/dist/bin/exe-call.js +2 -2
  12. package/dist/bin/exe-cloud.js +2 -2
  13. package/dist/bin/exe-dispatch.js +210 -2
  14. package/dist/bin/exe-doctor.js +2691 -1212
  15. package/dist/bin/exe-export-behaviors.js +210 -2
  16. package/dist/bin/exe-forget.js +264 -2
  17. package/dist/bin/exe-gateway.js +210 -2
  18. package/dist/bin/exe-heartbeat.js +212 -4
  19. package/dist/bin/exe-kill.js +210 -2
  20. package/dist/bin/exe-launch-agent.js +210 -2
  21. package/dist/bin/exe-link.js +47 -2
  22. package/dist/bin/exe-new-employee.js +113 -3
  23. package/dist/bin/exe-pending-messages.js +210 -2
  24. package/dist/bin/exe-pending-notifications.js +210 -2
  25. package/dist/bin/exe-pending-reviews.js +210 -2
  26. package/dist/bin/exe-rename.js +210 -2
  27. package/dist/bin/exe-review.js +210 -2
  28. package/dist/bin/exe-search.js +234 -3
  29. package/dist/bin/exe-session-cleanup.js +210 -2
  30. package/dist/bin/exe-settings.js +2 -2
  31. package/dist/bin/exe-start-codex.js +322 -4
  32. package/dist/bin/exe-start-opencode.js +336 -4
  33. package/dist/bin/exe-status.js +210 -2
  34. package/dist/bin/exe-team.js +210 -2
  35. package/dist/bin/git-sweep.js +210 -2
  36. package/dist/bin/graph-backfill.js +210 -2
  37. package/dist/bin/graph-export.js +210 -2
  38. package/dist/bin/install.js +113 -3
  39. package/dist/bin/intercom-check.js +210 -2
  40. package/dist/bin/scan-tasks.js +210 -2
  41. package/dist/bin/setup.js +47 -2
  42. package/dist/bin/shard-migrate.js +210 -2
  43. package/dist/bin/stack-update.js +158 -13
  44. package/dist/bin/update.js +2 -2
  45. package/dist/gateway/index.js +210 -2
  46. package/dist/hooks/bug-report-worker.js +321 -44
  47. package/dist/hooks/codex-stop-task-finalizer.js +210 -2
  48. package/dist/hooks/commit-complete.js +210 -2
  49. package/dist/hooks/error-recall.js +234 -3
  50. package/dist/hooks/exe-heartbeat-hook.js +2 -2
  51. package/dist/hooks/ingest-worker.js +2 -2
  52. package/dist/hooks/ingest.js +210 -2
  53. package/dist/hooks/instructions-loaded.js +210 -2
  54. package/dist/hooks/notification.js +210 -2
  55. package/dist/hooks/post-compact.js +210 -2
  56. package/dist/hooks/post-tool-combined.js +234 -3
  57. package/dist/hooks/pre-compact.js +446 -111
  58. package/dist/hooks/pre-tool-use.js +210 -2
  59. package/dist/hooks/prompt-submit.js +234 -3
  60. package/dist/hooks/session-end.js +347 -124
  61. package/dist/hooks/session-start.js +236 -5
  62. package/dist/hooks/stop.js +210 -2
  63. package/dist/hooks/subagent-stop.js +210 -2
  64. package/dist/hooks/summary-worker.js +171 -74
  65. package/dist/index.js +210 -2
  66. package/dist/lib/agent-config.js +2 -2
  67. package/dist/lib/cloud-sync.js +47 -2
  68. package/dist/lib/config.js +2 -2
  69. package/dist/lib/consolidation.js +2 -2
  70. package/dist/lib/database.js +47 -2
  71. package/dist/lib/db-daemon-client.js +2 -2
  72. package/dist/lib/db.js +47 -2
  73. package/dist/lib/device-registry.js +47 -2
  74. package/dist/lib/embedder.js +2 -2
  75. package/dist/lib/employee-templates.js +2 -2
  76. package/dist/lib/employees.js +2 -2
  77. package/dist/lib/exe-daemon-client.js +2 -2
  78. package/dist/lib/exe-daemon.js +592 -10
  79. package/dist/lib/hybrid-search.js +234 -3
  80. package/dist/lib/identity.js +2 -2
  81. package/dist/lib/license.js +2 -2
  82. package/dist/lib/messaging.js +2 -2
  83. package/dist/lib/reminders.js +2 -2
  84. package/dist/lib/schedules.js +47 -2
  85. package/dist/lib/skill-learning.js +2 -2
  86. package/dist/lib/store.js +210 -2
  87. package/dist/lib/task-router.js +2 -2
  88. package/dist/lib/tasks.js +2 -2
  89. package/dist/lib/tmux-routing.js +2 -2
  90. package/dist/lib/token-spend.js +2 -2
  91. package/dist/mcp/server.js +608 -10
  92. package/dist/mcp/tools/complete-reminder.js +2 -2
  93. package/dist/mcp/tools/create-reminder.js +2 -2
  94. package/dist/mcp/tools/create-task.js +2 -2
  95. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  96. package/dist/mcp/tools/list-reminders.js +2 -2
  97. package/dist/mcp/tools/list-tasks.js +2 -2
  98. package/dist/mcp/tools/send-message.js +2 -2
  99. package/dist/mcp/tools/update-task.js +2 -2
  100. package/dist/runtime/index.js +210 -2
  101. package/dist/tui/App.js +210 -2
  102. package/package.json +3 -2
  103. package/stack.release.json +23 -7
@@ -257,8 +257,8 @@ var init_config = __esm({
257
257
  rerankerAutoTrigger: {
258
258
  enabled: true,
259
259
  broadQueryMinCardinality: 5e4,
260
- fetchTopK: 150,
261
- returnTopK: 5
260
+ fetchTopK: 200,
261
+ returnTopK: 20
262
262
  }
263
263
  },
264
264
  graphRagEnabled: true,
@@ -313,6 +313,15 @@ var init_memory = __esm({
313
313
  });
314
314
 
315
315
  // src/lib/daemon-protocol.ts
316
+ var daemon_protocol_exports = {};
317
+ __export(daemon_protocol_exports, {
318
+ deserializeArgs: () => deserializeArgs,
319
+ deserializeResultSet: () => deserializeResultSet,
320
+ deserializeValue: () => deserializeValue,
321
+ serializeArgs: () => serializeArgs,
322
+ serializeResultSet: () => serializeResultSet,
323
+ serializeValue: () => serializeValue
324
+ });
316
325
  function serializeValue(v) {
317
326
  if (v === null || v === void 0) return null;
318
327
  if (typeof v === "bigint") return Number(v);
@@ -337,6 +346,9 @@ function deserializeValue(v) {
337
346
  }
338
347
  return v;
339
348
  }
349
+ function serializeArgs(args) {
350
+ return args.map(serializeValue);
351
+ }
340
352
  function deserializeArgs(args) {
341
353
  return args.map(deserializeValue);
342
354
  }
@@ -3109,6 +3121,51 @@ async function ensureSchema() {
3109
3121
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
3110
3122
  END;
3111
3123
  `);
3124
+ await client.executeMultiple(`
3125
+ CREATE TABLE IF NOT EXISTS memory_cards (
3126
+ id TEXT PRIMARY KEY,
3127
+ memory_id TEXT NOT NULL,
3128
+ agent_id TEXT NOT NULL,
3129
+ session_id TEXT NOT NULL,
3130
+ project_name TEXT,
3131
+ timestamp TEXT NOT NULL,
3132
+ card_type TEXT NOT NULL,
3133
+ subject TEXT,
3134
+ predicate TEXT,
3135
+ object TEXT,
3136
+ content TEXT NOT NULL,
3137
+ source_ref TEXT,
3138
+ confidence REAL DEFAULT 0.6,
3139
+ active INTEGER DEFAULT 1,
3140
+ created_at TEXT NOT NULL
3141
+ );
3142
+
3143
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
3144
+ ON memory_cards(agent_id, active, timestamp);
3145
+
3146
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
3147
+ ON memory_cards(memory_id);
3148
+
3149
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
3150
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
3151
+
3152
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
3153
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3154
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3155
+ END;
3156
+
3157
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
3158
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3159
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3160
+ END;
3161
+
3162
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
3163
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3164
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3165
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3166
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3167
+ END;
3168
+ `);
3112
3169
  try {
3113
3170
  await client.execute({
3114
3171
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4745,6 +4802,164 @@ ${p.content}`).join("\n\n");
4745
4802
  }
4746
4803
  });
4747
4804
 
4805
+ // src/lib/memory-cards.ts
4806
+ var memory_cards_exports = {};
4807
+ __export(memory_cards_exports, {
4808
+ extractMemoryCards: () => extractMemoryCards,
4809
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4810
+ searchMemoryCards: () => searchMemoryCards
4811
+ });
4812
+ import { createHash as createHash2 } from "crypto";
4813
+ function stableId(memoryId, type, content) {
4814
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4815
+ }
4816
+ function cleanText(text3) {
4817
+ return text3.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4818
+ }
4819
+ function splitSentences(text3) {
4820
+ return cleanText(text3).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4821
+ }
4822
+ function inferCardType(sentence, toolName) {
4823
+ const lower = sentence.toLowerCase();
4824
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4825
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4826
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4827
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4828
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4829
+ return "fact";
4830
+ }
4831
+ function extractSubject(sentence, agentId) {
4832
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4833
+ return explicit?.[1] ?? agentId;
4834
+ }
4835
+ function predicateFor(type) {
4836
+ switch (type) {
4837
+ case "preference":
4838
+ return "prefers";
4839
+ case "belief_update":
4840
+ return "updated";
4841
+ case "decision":
4842
+ return "decided";
4843
+ case "event":
4844
+ return "happened";
4845
+ case "code":
4846
+ return "implemented";
4847
+ default:
4848
+ return "states";
4849
+ }
4850
+ }
4851
+ function extractMemoryCards(row) {
4852
+ const sentences = splitSentences(row.raw_text);
4853
+ const cards = [];
4854
+ for (const sentence of sentences) {
4855
+ const type = inferCardType(sentence, row.tool_name);
4856
+ const subject = extractSubject(sentence, row.agent_id);
4857
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4858
+ cards.push({
4859
+ id: stableId(row.id, type, content),
4860
+ memory_id: row.id,
4861
+ agent_id: row.agent_id,
4862
+ session_id: row.session_id,
4863
+ project_name: row.project_name ?? null,
4864
+ timestamp: row.timestamp,
4865
+ card_type: type,
4866
+ subject,
4867
+ predicate: predicateFor(type),
4868
+ object: content,
4869
+ content,
4870
+ source_ref: row.id,
4871
+ confidence: type === "fact" ? 0.55 : 0.65
4872
+ });
4873
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4874
+ }
4875
+ return cards;
4876
+ }
4877
+ async function insertMemoryCardsForBatch(rows) {
4878
+ const cards = rows.flatMap(extractMemoryCards);
4879
+ if (cards.length === 0) return 0;
4880
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4881
+ const client = getClient();
4882
+ const stmts = cards.map((card) => ({
4883
+ sql: `INSERT OR IGNORE INTO memory_cards
4884
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4885
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4886
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4887
+ args: [
4888
+ card.id,
4889
+ card.memory_id,
4890
+ card.agent_id,
4891
+ card.session_id,
4892
+ card.project_name,
4893
+ card.timestamp,
4894
+ card.card_type,
4895
+ card.subject,
4896
+ card.predicate,
4897
+ card.object,
4898
+ card.content,
4899
+ card.source_ref,
4900
+ card.confidence,
4901
+ now
4902
+ ]
4903
+ }));
4904
+ await client.batch(stmts, "write");
4905
+ return cards.length;
4906
+ }
4907
+ function buildMatchExpr(queryText) {
4908
+ const terms = queryText.toLowerCase().split(/\s+/).filter((t) => t.length >= 3).map((t) => t.replace(/[^a-z0-9_]/g, "")).filter((t) => t.length >= 3).slice(0, 12);
4909
+ if (terms.length === 0) return null;
4910
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4911
+ }
4912
+ async function searchMemoryCards(queryText, agentId, options) {
4913
+ const limit = options?.limit ?? 10;
4914
+ const matchExpr = buildMatchExpr(queryText);
4915
+ if (!matchExpr) return [];
4916
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4917
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4918
+ FROM memory_cards c
4919
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4920
+ WHERE memory_cards_fts MATCH ?
4921
+ AND c.agent_id = ?
4922
+ AND COALESCE(c.active, 1) = 1`;
4923
+ const args = [matchExpr, agentId];
4924
+ if (options?.projectName) {
4925
+ sql += ` AND c.project_name = ?`;
4926
+ args.push(options.projectName);
4927
+ }
4928
+ if (options?.since) {
4929
+ sql += ` AND c.timestamp >= ?`;
4930
+ args.push(options.since);
4931
+ }
4932
+ sql += ` ORDER BY rank LIMIT ?`;
4933
+ args.push(limit);
4934
+ const result = await getClient().execute({ sql, args });
4935
+ return result.rows.map((row) => ({
4936
+ id: `card:${String(row.id)}`,
4937
+ agent_id: String(row.agent_id),
4938
+ agent_role: "memory_card",
4939
+ session_id: String(row.session_id),
4940
+ timestamp: String(row.timestamp),
4941
+ tool_name: `memory_card:${String(row.card_type)}`,
4942
+ project_name: row.project_name == null ? "" : String(row.project_name),
4943
+ has_error: false,
4944
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4945
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4946
+ vector: [],
4947
+ importance: 6,
4948
+ status: "active",
4949
+ confidence: Number(row.confidence ?? 0.6),
4950
+ last_accessed: String(row.timestamp)
4951
+ }));
4952
+ }
4953
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4954
+ var init_memory_cards = __esm({
4955
+ "src/lib/memory-cards.ts"() {
4956
+ "use strict";
4957
+ init_database();
4958
+ MAX_CARDS_PER_MEMORY = 6;
4959
+ MAX_SENTENCE_CHARS = 360;
4960
+ }
4961
+ });
4962
+
4748
4963
  // src/lib/store.ts
4749
4964
  var store_exports = {};
4750
4965
  __export(store_exports, {
@@ -5083,6 +5298,11 @@ async function flushBatch() {
5083
5298
  const globalClient = getClient();
5084
5299
  const globalStmts = batch.map(buildStmt);
5085
5300
  await globalClient.batch(globalStmts, "write");
5301
+ try {
5302
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
5303
+ await insertMemoryCardsForBatch2(batch);
5304
+ } catch {
5305
+ }
5086
5306
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
5087
5307
  _pendingRecords.splice(0, batch.length);
5088
5308
  try {
@@ -6738,9 +6958,18 @@ async function hybridSearch(queryText, agentId, options) {
6738
6958
  } catch {
6739
6959
  }
6740
6960
  }
6741
- const [ftsResults, vectorResults, grepResults] = await Promise.all([
6961
+ const includeStructuredCards = effectiveIsBroad || effectiveOptions.includeStructuredCards === true;
6962
+ const [ftsResults, vectorResults, cardResults, grepResults] = await Promise.all([
6742
6963
  lightweightSearch(effectiveQuery, agentId, fetchOptions),
6743
6964
  queryVector ? searchMemories(queryVector, agentId, fetchOptions) : Promise.resolve([]),
6965
+ includeStructuredCards ? (async () => {
6966
+ try {
6967
+ const { searchMemoryCards: searchMemoryCards2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
6968
+ return await searchMemoryCards2(effectiveQuery, agentId, fetchOptions);
6969
+ } catch {
6970
+ return [];
6971
+ }
6972
+ })() : Promise.resolve([]),
6744
6973
  grepPromise
6745
6974
  ]);
6746
6975
  const lists = [];
@@ -6753,6 +6982,10 @@ async function hybridSearch(queryText, agentId, options) {
6753
6982
  lists.push(vectorResults);
6754
6983
  weights.push(1);
6755
6984
  }
6985
+ if (cardResults.length > 0) {
6986
+ lists.push(cardResults);
6987
+ weights.push(1.2);
6988
+ }
6756
6989
  if (grepResults.length > 0) {
6757
6990
  lists.push(grepResults);
6758
6991
  weights.push(0.5);
@@ -6821,6 +7054,7 @@ async function hybridSearch(queryText, agentId, options) {
6821
7054
  signals: {
6822
7055
  fts: ftsResults.length,
6823
7056
  vector: vectorResults.length,
7057
+ cards: cardResults.length,
6824
7058
  grep: grepResults.length,
6825
7059
  entityBoost: entityBoostRan,
6826
7060
  reranker: rerankedAndBlended !== null,
@@ -6970,6 +7204,15 @@ async function lightweightSearch(queryText, agentId, options) {
6970
7204
  if (options?.includeSource && results.length > 0) {
6971
7205
  await attachDocumentMetadata(results);
6972
7206
  }
7207
+ if (options?.includeStructuredCards !== true) return results;
7208
+ try {
7209
+ const { searchMemoryCards: searchMemoryCards2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
7210
+ const cardResults = await searchMemoryCards2(queryText, agentId, options);
7211
+ if (cardResults.length > 0) {
7212
+ return rrfMergeMulti([results, cardResults], limit, RRF_K, [1, 1.2]);
7213
+ }
7214
+ } catch {
7215
+ }
6973
7216
  return results;
6974
7217
  }
6975
7218
  async function ftsQuery(client, matchExpr, agentId, options, limit) {
@@ -13353,7 +13596,7 @@ __export(identity_exports, {
13353
13596
  import { existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "fs";
13354
13597
  import { readdirSync as readdirSync7 } from "fs";
13355
13598
  import path29 from "path";
13356
- import { createHash as createHash2 } from "crypto";
13599
+ import { createHash as createHash3 } from "crypto";
13357
13600
  function ensureDir2() {
13358
13601
  if (!existsSync23(IDENTITY_DIR2)) {
13359
13602
  mkdirSync10(IDENTITY_DIR2, { recursive: true });
@@ -13400,7 +13643,7 @@ function parseFrontmatter(raw) {
13400
13643
  };
13401
13644
  }
13402
13645
  function contentHash(content) {
13403
- return createHash2("sha256").update(content).digest("hex").slice(0, 16);
13646
+ return createHash3("sha256").update(content).digest("hex").slice(0, 16);
13404
13647
  }
13405
13648
  function getIdentity(agentId) {
13406
13649
  const filePath = identityPath(agentId);
@@ -20760,6 +21003,218 @@ var init_conflict_detector = __esm({
20760
21003
  }
20761
21004
  });
20762
21005
 
21006
+ // src/adapters/runtime-hook-manifest.ts
21007
+ function manifestEntryForCommand(command) {
21008
+ return EXE_HOOK_MANIFEST.find((entry) => command.includes(entry.commandMarker));
21009
+ }
21010
+ var EXE_HOOKS, EXE_HOOK_MANIFEST, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS;
21011
+ var init_runtime_hook_manifest = __esm({
21012
+ "src/adapters/runtime-hook-manifest.ts"() {
21013
+ "use strict";
21014
+ EXE_HOOKS = {
21015
+ postToolCombined: "dist/hooks/post-tool-combined.js",
21016
+ sessionStart: "dist/hooks/session-start.js",
21017
+ promptSubmit: "dist/hooks/prompt-submit.js",
21018
+ heartbeat: "dist/hooks/exe-heartbeat-hook.js",
21019
+ stop: "dist/hooks/stop.js",
21020
+ preToolUse: "dist/hooks/pre-tool-use.js",
21021
+ subagentStop: "dist/hooks/subagent-stop.js",
21022
+ preCompact: "dist/hooks/pre-compact.js",
21023
+ postCompact: "dist/hooks/post-compact.js",
21024
+ sessionEnd: "dist/hooks/session-end.js",
21025
+ notification: "dist/hooks/notification.js",
21026
+ instructionsLoaded: "dist/hooks/instructions-loaded.js"
21027
+ };
21028
+ EXE_HOOK_MANIFEST = [
21029
+ {
21030
+ key: "postToolCombined",
21031
+ event: "PostToolUse",
21032
+ commandMarker: EXE_HOOKS.postToolCombined,
21033
+ owner: "exe-os",
21034
+ purpose: "Single PostToolUse entrypoint for ingestion, error recall, summaries, and bug detection.",
21035
+ runtimes: ["claude", "codex", "opencode"],
21036
+ checkpointRole: "none"
21037
+ },
21038
+ {
21039
+ key: "sessionStart",
21040
+ event: "SessionStart",
21041
+ commandMarker: EXE_HOOKS.sessionStart,
21042
+ owner: "exe-os",
21043
+ purpose: "Loads agent identity, procedures, and boot context.",
21044
+ runtimes: ["claude", "codex", "opencode"],
21045
+ checkpointRole: "none"
21046
+ },
21047
+ {
21048
+ key: "promptSubmit",
21049
+ event: "UserPromptSubmit",
21050
+ commandMarker: EXE_HOOKS.promptSubmit,
21051
+ owner: "exe-os",
21052
+ purpose: "Injects current tasks, pending reviews, and local context before each prompt.",
21053
+ runtimes: ["claude", "codex", "opencode"],
21054
+ checkpointRole: "none"
21055
+ },
21056
+ {
21057
+ key: "heartbeat",
21058
+ event: "UserPromptSubmit",
21059
+ commandMarker: EXE_HOOKS.heartbeat,
21060
+ owner: "exe-os",
21061
+ purpose: "Lightweight heartbeat/status sidecar for Claude Code sessions.",
21062
+ runtimes: ["claude"],
21063
+ checkpointRole: "none"
21064
+ },
21065
+ {
21066
+ key: "stop",
21067
+ event: "Stop",
21068
+ commandMarker: EXE_HOOKS.stop,
21069
+ owner: "exe-os",
21070
+ purpose: "Finalizes task state, capacity signals, and emergency checkpointing.",
21071
+ runtimes: ["claude", "codex", "opencode"],
21072
+ checkpointRole: "capacity_checkpoint"
21073
+ },
21074
+ {
21075
+ key: "preToolUse",
21076
+ event: "PreToolUse",
21077
+ commandMarker: EXE_HOOKS.preToolUse,
21078
+ owner: "exe-os",
21079
+ purpose: "Preflight guardrails before shell/tool execution.",
21080
+ runtimes: ["claude", "codex", "opencode"],
21081
+ checkpointRole: "none"
21082
+ },
21083
+ {
21084
+ key: "subagentStop",
21085
+ event: "SubagentStop",
21086
+ commandMarker: EXE_HOOKS.subagentStop,
21087
+ owner: "exe-os",
21088
+ purpose: "Captures subagent completion context.",
21089
+ runtimes: ["claude"],
21090
+ checkpointRole: "none"
21091
+ },
21092
+ {
21093
+ key: "preCompact",
21094
+ event: "PreCompact",
21095
+ commandMarker: EXE_HOOKS.preCompact,
21096
+ owner: "exe-os",
21097
+ purpose: "Writes active-task snapshot and compaction recovery context.",
21098
+ runtimes: ["claude"],
21099
+ checkpointRole: "recovery_context"
21100
+ },
21101
+ {
21102
+ key: "postCompact",
21103
+ event: "PostCompact",
21104
+ commandMarker: EXE_HOOKS.postCompact,
21105
+ owner: "exe-os",
21106
+ purpose: "Rehydrates recovery context after compaction.",
21107
+ runtimes: ["claude"],
21108
+ checkpointRole: "recovery_context"
21109
+ },
21110
+ {
21111
+ key: "sessionEnd",
21112
+ event: "SessionEnd",
21113
+ commandMarker: EXE_HOOKS.sessionEnd,
21114
+ owner: "exe-os",
21115
+ purpose: "Stores session-end checkpoint and triages orphaned in-progress tasks.",
21116
+ runtimes: ["claude"],
21117
+ checkpointRole: "session_summary"
21118
+ },
21119
+ {
21120
+ key: "notification",
21121
+ event: "Notification",
21122
+ commandMarker: EXE_HOOKS.notification,
21123
+ owner: "exe-os",
21124
+ purpose: "Captures runtime notifications and nudges.",
21125
+ runtimes: ["claude"],
21126
+ checkpointRole: "none"
21127
+ },
21128
+ {
21129
+ key: "instructionsLoaded",
21130
+ event: "InstructionsLoaded",
21131
+ commandMarker: EXE_HOOKS.instructionsLoaded,
21132
+ owner: "exe-os",
21133
+ purpose: "Applies runtime instruction post-processing.",
21134
+ runtimes: ["claude"],
21135
+ checkpointRole: "none"
21136
+ }
21137
+ ];
21138
+ LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS = [
21139
+ "dist/hooks/ingest.js",
21140
+ "dist/hooks/error-recall.js",
21141
+ "dist/hooks/ingest-worker.js"
21142
+ ];
21143
+ }
21144
+ });
21145
+
21146
+ // src/bin/fast-db-init.ts
21147
+ var fast_db_init_exports = {};
21148
+ __export(fast_db_init_exports, {
21149
+ fastDbInit: () => fastDbInit
21150
+ });
21151
+ async function fastDbInit() {
21152
+ const { isInitialized: isInitialized2, getClient: getClient2, setExternalClient: setExternalClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
21153
+ if (isInitialized2()) {
21154
+ return getClient2();
21155
+ }
21156
+ try {
21157
+ const { connectEmbedDaemon: connectEmbedDaemon2, sendDaemonRequest: sendDaemonRequest2, isClientConnected: isClientConnected2 } = await Promise.resolve().then(() => (init_exe_daemon_client(), exe_daemon_client_exports));
21158
+ const { deserializeResultSet: deserializeResultSet2 } = await Promise.resolve().then(() => (init_daemon_protocol(), daemon_protocol_exports));
21159
+ await connectEmbedDaemon2();
21160
+ if (isClientConnected2()) {
21161
+ const daemonClient = {
21162
+ async execute(stmt) {
21163
+ const sql = typeof stmt === "string" ? stmt : stmt.sql;
21164
+ const args = typeof stmt === "string" ? [] : Array.isArray(stmt.args) ? stmt.args : [];
21165
+ const resp = await sendDaemonRequest2({ type: "db-execute", sql, args });
21166
+ if (resp.error) throw new Error(String(resp.error));
21167
+ if (resp.db) return deserializeResultSet2(resp.db);
21168
+ throw new Error("Unexpected daemon response");
21169
+ },
21170
+ async batch(stmts, mode) {
21171
+ const statements = stmts.map((s) => {
21172
+ const sql = typeof s === "string" ? s : s.sql;
21173
+ const args = typeof s === "string" ? [] : Array.isArray(s.args) ? s.args : [];
21174
+ return { sql, args };
21175
+ });
21176
+ const resp = await sendDaemonRequest2({ type: "db-batch", statements, mode: mode ?? "deferred" });
21177
+ if (resp.error) throw new Error(String(resp.error));
21178
+ const batchResults = resp["db-batch"];
21179
+ if (batchResults) return batchResults.map(deserializeResultSet2);
21180
+ throw new Error("Unexpected daemon batch response");
21181
+ },
21182
+ async transaction(_mode) {
21183
+ throw new Error("Transactions not supported via daemon socket");
21184
+ },
21185
+ async executeMultiple(_sql) {
21186
+ throw new Error("executeMultiple not supported via daemon socket");
21187
+ },
21188
+ async migrate(_stmts) {
21189
+ throw new Error("migrate not supported via daemon socket");
21190
+ },
21191
+ sync() {
21192
+ return Promise.resolve(void 0);
21193
+ },
21194
+ close() {
21195
+ },
21196
+ get closed() {
21197
+ return false;
21198
+ },
21199
+ get protocol() {
21200
+ return "file";
21201
+ }
21202
+ };
21203
+ setExternalClient2(daemonClient);
21204
+ return daemonClient;
21205
+ }
21206
+ } catch {
21207
+ }
21208
+ const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
21209
+ await initStore2({ lightweight: true });
21210
+ return getClient2();
21211
+ }
21212
+ var init_fast_db_init = __esm({
21213
+ "src/bin/fast-db-init.ts"() {
21214
+ "use strict";
21215
+ }
21216
+ });
21217
+
20763
21218
  // src/lib/db-backup.ts
20764
21219
  var db_backup_exports = {};
20765
21220
  __export(db_backup_exports, {
@@ -21066,6 +21521,121 @@ function auditHookHealth() {
21066
21521
  const topPatterns = [...patternCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => ({ pattern, count }));
21067
21522
  return { logExists: true, totalLines, errorsLastHour, topPatterns };
21068
21523
  }
21524
+ function safeReadJson(filePath) {
21525
+ if (!existsSync31(filePath)) return null;
21526
+ try {
21527
+ return JSON.parse(readFileSync23(filePath, "utf-8"));
21528
+ } catch {
21529
+ return null;
21530
+ }
21531
+ }
21532
+ function collectHookCommandsFromClaudeSettings(settings) {
21533
+ const hooks = settings?.hooks;
21534
+ if (!hooks || typeof hooks !== "object") return [];
21535
+ const commands = [];
21536
+ for (const [event, groups] of Object.entries(hooks)) {
21537
+ if (!Array.isArray(groups)) continue;
21538
+ for (const group of groups) {
21539
+ const hooksForGroup = group?.hooks;
21540
+ if (!Array.isArray(hooksForGroup)) continue;
21541
+ for (const hook of hooksForGroup) {
21542
+ const command = hook?.command;
21543
+ if (typeof command === "string") commands.push({ event, command });
21544
+ }
21545
+ }
21546
+ }
21547
+ return commands;
21548
+ }
21549
+ function collectHookCommandsFromCodexHooks(config2) {
21550
+ const commands = [];
21551
+ if (!config2 || typeof config2 !== "object") return commands;
21552
+ const root = config2;
21553
+ for (const [event, value] of Object.entries(root)) {
21554
+ const entries = Array.isArray(value) ? value : value && typeof value === "object" ? Object.values(value) : [];
21555
+ for (const entry of entries) {
21556
+ if (typeof entry === "string") commands.push({ event, command: entry });
21557
+ const command = entry?.command;
21558
+ if (typeof command === "string") commands.push({ event, command });
21559
+ }
21560
+ }
21561
+ return commands;
21562
+ }
21563
+ function buildHookOwnershipIssues(runtime, commands) {
21564
+ const issues = [];
21565
+ for (const marker of LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS) {
21566
+ const count = commands.filter((cmd) => cmd.command.includes(marker)).length;
21567
+ if (count > 0) {
21568
+ issues.push({
21569
+ runtime,
21570
+ event: "PostToolUse",
21571
+ marker,
21572
+ count,
21573
+ message: `Legacy split PostToolUse hook still installed: ${marker}`
21574
+ });
21575
+ }
21576
+ }
21577
+ for (const entry of EXE_HOOK_MANIFEST.filter((hook) => hook.runtimes.includes(runtime))) {
21578
+ const matching = commands.filter((cmd) => cmd.command.includes(entry.commandMarker));
21579
+ if (matching.length > 1) {
21580
+ issues.push({
21581
+ runtime,
21582
+ event: entry.event,
21583
+ marker: entry.commandMarker,
21584
+ count: matching.length,
21585
+ message: `Duplicate exe-os hook owner for ${entry.event}: ${entry.commandMarker}`
21586
+ });
21587
+ }
21588
+ for (const cmd of matching) {
21589
+ if (cmd.event !== entry.event) {
21590
+ issues.push({
21591
+ runtime,
21592
+ event: cmd.event,
21593
+ marker: entry.commandMarker,
21594
+ count: 1,
21595
+ message: `exe-os hook ${entry.commandMarker} is registered under ${cmd.event}, expected ${entry.event}`
21596
+ });
21597
+ }
21598
+ }
21599
+ }
21600
+ for (const cmd of commands) {
21601
+ if (!cmd.command.includes("dist/hooks/")) continue;
21602
+ if (!cmd.command.includes("exe-os")) continue;
21603
+ const entry = manifestEntryForCommand(cmd.command);
21604
+ const isLegacy = LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS.some((marker) => cmd.command.includes(marker));
21605
+ if (!entry && !isLegacy) {
21606
+ issues.push({
21607
+ runtime,
21608
+ event: cmd.event,
21609
+ marker: "dist/hooks/",
21610
+ count: 1,
21611
+ message: `Unknown exe-os hook command not present in ownership manifest: ${cmd.command.slice(0, 160)}`
21612
+ });
21613
+ }
21614
+ }
21615
+ return issues;
21616
+ }
21617
+ function auditHookOwnership() {
21618
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
21619
+ const checkedFiles = [];
21620
+ const issues = [];
21621
+ const claudeSettingsPath = path36.join(home, ".claude", "settings.json");
21622
+ const claudeSettings = safeReadJson(claudeSettingsPath);
21623
+ if (claudeSettings) {
21624
+ checkedFiles.push(claudeSettingsPath);
21625
+ issues.push(...buildHookOwnershipIssues("claude", collectHookCommandsFromClaudeSettings(claudeSettings)));
21626
+ }
21627
+ const codexHooksPath = path36.join(home, ".codex", "hooks.json");
21628
+ const codexHooks = safeReadJson(codexHooksPath);
21629
+ if (codexHooks) {
21630
+ checkedFiles.push(codexHooksPath);
21631
+ issues.push(...buildHookOwnershipIssues("codex", collectHookCommandsFromCodexHooks(codexHooks)));
21632
+ }
21633
+ return {
21634
+ checkedFiles,
21635
+ issues,
21636
+ staleLegacyHooks: issues.filter((issue) => issue.message.includes("Legacy split"))
21637
+ };
21638
+ }
21069
21639
  async function auditShards() {
21070
21640
  try {
21071
21641
  const { auditShardHealth: auditShardHealth2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -21110,7 +21680,8 @@ async function runAudit(client, flags) {
21110
21680
  }
21111
21681
  const duplicateCount = duplicates.reduce((sum, d) => sum + d.delete_ids.length, 0);
21112
21682
  const hookHealth = auditHookHealth();
21113
- return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth, shards };
21683
+ const hookOwnership = auditHookOwnership();
21684
+ return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth, hookOwnership, shards };
21114
21685
  }
21115
21686
  function indicator(value, warn) {
21116
21687
  if (value === 0) return "\u{1F7E2}";
@@ -21189,6 +21760,15 @@ function formatReport(report, flags) {
21189
21760
  lines.push(` ${p.count}x: ${p.pattern}`);
21190
21761
  }
21191
21762
  }
21763
+ const ho = report.hookOwnership;
21764
+ if (ho.issues.length === 0) {
21765
+ lines.push(`\u{1F7E2} Hook ownership: ${ho.checkedFiles.length > 0 ? "manifest clean" : "no local hook config found"}`);
21766
+ } else {
21767
+ lines.push(`\u{1F534} Hook ownership: ${fmtNum(ho.issues.length)} issue(s)`);
21768
+ for (const issue of ho.issues.slice(0, 5)) {
21769
+ lines.push(` [${issue.runtime}/${issue.event}] ${issue.message}`);
21770
+ }
21771
+ }
21192
21772
  const sh = report.shards;
21193
21773
  if (sh.total > 0) {
21194
21774
  if (sh.unreadable === 0) {
@@ -21258,6 +21838,9 @@ function formatReport(report, flags) {
21258
21838
  if (report.conflicts.superseded > 0) {
21259
21839
  recs.push(`${fmtNum(report.conflicts.superseded)} superseded memories can be deactivated`);
21260
21840
  }
21841
+ if (report.hookOwnership.issues.length > 0) {
21842
+ recs.push(`Run exe-os install to refresh hook config; remove stale exe-os hook commands if they remain`);
21843
+ }
21261
21844
  if (recs.length > 0) {
21262
21845
  lines.push("Recommendations:");
21263
21846
  for (const r of recs) {
@@ -21391,8 +21974,8 @@ function splitAtSentences(text3, maxChunkSize) {
21391
21974
  }
21392
21975
  async function main(argv = process.argv.slice(2)) {
21393
21976
  const flags = parseFlags(argv);
21394
- await initStore();
21395
- const client = getClient();
21977
+ const { fastDbInit: fastDbInit2 } = await Promise.resolve().then(() => (init_fast_db_init(), fast_db_init_exports));
21978
+ const client = await fastDbInit2();
21396
21979
  const report = await runAudit(client, flags);
21397
21980
  console.log(formatReport(report, flags));
21398
21981
  if (flags.fix || flags.dryRun) {
@@ -21452,10 +22035,9 @@ ${mode} Complete.`);
21452
22035
  var init_exe_doctor = __esm({
21453
22036
  "src/bin/exe-doctor.ts"() {
21454
22037
  "use strict";
21455
- init_store();
21456
- init_database();
21457
22038
  init_is_main();
21458
22039
  init_conflict_detector();
22040
+ init_runtime_hook_manifest();
21459
22041
  if (isMainModule(import.meta.url) && (process.argv[1] ?? "").includes("exe-doctor")) {
21460
22042
  main().catch((err) => {
21461
22043
  console.error(err instanceof Error ? err.message : String(err));