@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
@@ -624,8 +624,8 @@ var init_config = __esm({
624
624
  rerankerAutoTrigger: {
625
625
  enabled: true,
626
626
  broadQueryMinCardinality: 5e4,
627
- fetchTopK: 150,
628
- returnTopK: 5
627
+ fetchTopK: 200,
628
+ returnTopK: 20
629
629
  }
630
630
  },
631
631
  graphRagEnabled: true,
@@ -3367,6 +3367,51 @@ async function ensureSchema() {
3367
3367
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
3368
3368
  END;
3369
3369
  `);
3370
+ await client.executeMultiple(`
3371
+ CREATE TABLE IF NOT EXISTS memory_cards (
3372
+ id TEXT PRIMARY KEY,
3373
+ memory_id TEXT NOT NULL,
3374
+ agent_id TEXT NOT NULL,
3375
+ session_id TEXT NOT NULL,
3376
+ project_name TEXT,
3377
+ timestamp TEXT NOT NULL,
3378
+ card_type TEXT NOT NULL,
3379
+ subject TEXT,
3380
+ predicate TEXT,
3381
+ object TEXT,
3382
+ content TEXT NOT NULL,
3383
+ source_ref TEXT,
3384
+ confidence REAL DEFAULT 0.6,
3385
+ active INTEGER DEFAULT 1,
3386
+ created_at TEXT NOT NULL
3387
+ );
3388
+
3389
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
3390
+ ON memory_cards(agent_id, active, timestamp);
3391
+
3392
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
3393
+ ON memory_cards(memory_id);
3394
+
3395
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
3396
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
3397
+
3398
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
3399
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3400
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3401
+ END;
3402
+
3403
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
3404
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3405
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3406
+ END;
3407
+
3408
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
3409
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3410
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3411
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3412
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3413
+ END;
3414
+ `);
3370
3415
  try {
3371
3416
  await client.execute({
3372
3417
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4669,6 +4714,164 @@ ${p.content}`).join("\n\n");
4669
4714
  }
4670
4715
  });
4671
4716
 
4717
+ // src/lib/memory-cards.ts
4718
+ var memory_cards_exports = {};
4719
+ __export(memory_cards_exports, {
4720
+ extractMemoryCards: () => extractMemoryCards,
4721
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4722
+ searchMemoryCards: () => searchMemoryCards
4723
+ });
4724
+ import { createHash as createHash2 } from "crypto";
4725
+ function stableId(memoryId, type, content) {
4726
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4727
+ }
4728
+ function cleanText(text) {
4729
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4730
+ }
4731
+ function splitSentences(text) {
4732
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4733
+ }
4734
+ function inferCardType(sentence, toolName) {
4735
+ const lower = sentence.toLowerCase();
4736
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4737
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4738
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4739
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4740
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4741
+ return "fact";
4742
+ }
4743
+ function extractSubject(sentence, agentId) {
4744
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4745
+ return explicit?.[1] ?? agentId;
4746
+ }
4747
+ function predicateFor(type) {
4748
+ switch (type) {
4749
+ case "preference":
4750
+ return "prefers";
4751
+ case "belief_update":
4752
+ return "updated";
4753
+ case "decision":
4754
+ return "decided";
4755
+ case "event":
4756
+ return "happened";
4757
+ case "code":
4758
+ return "implemented";
4759
+ default:
4760
+ return "states";
4761
+ }
4762
+ }
4763
+ function extractMemoryCards(row) {
4764
+ const sentences = splitSentences(row.raw_text);
4765
+ const cards = [];
4766
+ for (const sentence of sentences) {
4767
+ const type = inferCardType(sentence, row.tool_name);
4768
+ const subject = extractSubject(sentence, row.agent_id);
4769
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4770
+ cards.push({
4771
+ id: stableId(row.id, type, content),
4772
+ memory_id: row.id,
4773
+ agent_id: row.agent_id,
4774
+ session_id: row.session_id,
4775
+ project_name: row.project_name ?? null,
4776
+ timestamp: row.timestamp,
4777
+ card_type: type,
4778
+ subject,
4779
+ predicate: predicateFor(type),
4780
+ object: content,
4781
+ content,
4782
+ source_ref: row.id,
4783
+ confidence: type === "fact" ? 0.55 : 0.65
4784
+ });
4785
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4786
+ }
4787
+ return cards;
4788
+ }
4789
+ async function insertMemoryCardsForBatch(rows) {
4790
+ const cards = rows.flatMap(extractMemoryCards);
4791
+ if (cards.length === 0) return 0;
4792
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4793
+ const client = getClient();
4794
+ const stmts = cards.map((card) => ({
4795
+ sql: `INSERT OR IGNORE INTO memory_cards
4796
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4797
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4798
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4799
+ args: [
4800
+ card.id,
4801
+ card.memory_id,
4802
+ card.agent_id,
4803
+ card.session_id,
4804
+ card.project_name,
4805
+ card.timestamp,
4806
+ card.card_type,
4807
+ card.subject,
4808
+ card.predicate,
4809
+ card.object,
4810
+ card.content,
4811
+ card.source_ref,
4812
+ card.confidence,
4813
+ now
4814
+ ]
4815
+ }));
4816
+ await client.batch(stmts, "write");
4817
+ return cards.length;
4818
+ }
4819
+ function buildMatchExpr(queryText) {
4820
+ 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);
4821
+ if (terms.length === 0) return null;
4822
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4823
+ }
4824
+ async function searchMemoryCards(queryText, agentId, options) {
4825
+ const limit = options?.limit ?? 10;
4826
+ const matchExpr = buildMatchExpr(queryText);
4827
+ if (!matchExpr) return [];
4828
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4829
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4830
+ FROM memory_cards c
4831
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4832
+ WHERE memory_cards_fts MATCH ?
4833
+ AND c.agent_id = ?
4834
+ AND COALESCE(c.active, 1) = 1`;
4835
+ const args = [matchExpr, agentId];
4836
+ if (options?.projectName) {
4837
+ sql += ` AND c.project_name = ?`;
4838
+ args.push(options.projectName);
4839
+ }
4840
+ if (options?.since) {
4841
+ sql += ` AND c.timestamp >= ?`;
4842
+ args.push(options.since);
4843
+ }
4844
+ sql += ` ORDER BY rank LIMIT ?`;
4845
+ args.push(limit);
4846
+ const result = await getClient().execute({ sql, args });
4847
+ return result.rows.map((row) => ({
4848
+ id: `card:${String(row.id)}`,
4849
+ agent_id: String(row.agent_id),
4850
+ agent_role: "memory_card",
4851
+ session_id: String(row.session_id),
4852
+ timestamp: String(row.timestamp),
4853
+ tool_name: `memory_card:${String(row.card_type)}`,
4854
+ project_name: row.project_name == null ? "" : String(row.project_name),
4855
+ has_error: false,
4856
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4857
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4858
+ vector: [],
4859
+ importance: 6,
4860
+ status: "active",
4861
+ confidence: Number(row.confidence ?? 0.6),
4862
+ last_accessed: String(row.timestamp)
4863
+ }));
4864
+ }
4865
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4866
+ var init_memory_cards = __esm({
4867
+ "src/lib/memory-cards.ts"() {
4868
+ "use strict";
4869
+ init_database();
4870
+ MAX_CARDS_PER_MEMORY = 6;
4871
+ MAX_SENTENCE_CHARS = 360;
4872
+ }
4873
+ });
4874
+
4672
4875
  // src/lib/store.ts
4673
4876
  var store_exports = {};
4674
4877
  __export(store_exports, {
@@ -5007,6 +5210,11 @@ async function flushBatch() {
5007
5210
  const globalClient = getClient();
5008
5211
  const globalStmts = batch.map(buildStmt);
5009
5212
  await globalClient.batch(globalStmts, "write");
5213
+ try {
5214
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
5215
+ await insertMemoryCardsForBatch2(batch);
5216
+ } catch {
5217
+ }
5010
5218
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
5011
5219
  _pendingRecords.splice(0, batch.length);
5012
5220
  try {
@@ -277,8 +277,8 @@ var init_config = __esm({
277
277
  rerankerAutoTrigger: {
278
278
  enabled: true,
279
279
  broadQueryMinCardinality: 5e4,
280
- fetchTopK: 150,
281
- returnTopK: 5
280
+ fetchTopK: 200,
281
+ returnTopK: 20
282
282
  }
283
283
  },
284
284
  graphRagEnabled: true,
@@ -2751,6 +2751,51 @@ async function ensureSchema() {
2751
2751
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2752
2752
  END;
2753
2753
  `);
2754
+ await client.executeMultiple(`
2755
+ CREATE TABLE IF NOT EXISTS memory_cards (
2756
+ id TEXT PRIMARY KEY,
2757
+ memory_id TEXT NOT NULL,
2758
+ agent_id TEXT NOT NULL,
2759
+ session_id TEXT NOT NULL,
2760
+ project_name TEXT,
2761
+ timestamp TEXT NOT NULL,
2762
+ card_type TEXT NOT NULL,
2763
+ subject TEXT,
2764
+ predicate TEXT,
2765
+ object TEXT,
2766
+ content TEXT NOT NULL,
2767
+ source_ref TEXT,
2768
+ confidence REAL DEFAULT 0.6,
2769
+ active INTEGER DEFAULT 1,
2770
+ created_at TEXT NOT NULL
2771
+ );
2772
+
2773
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2774
+ ON memory_cards(agent_id, active, timestamp);
2775
+
2776
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2777
+ ON memory_cards(memory_id);
2778
+
2779
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2780
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2781
+
2782
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2783
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2784
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2785
+ END;
2786
+
2787
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2788
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2789
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2790
+ END;
2791
+
2792
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2793
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2794
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2795
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2796
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2797
+ END;
2798
+ `);
2754
2799
  try {
2755
2800
  await client.execute({
2756
2801
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4040,6 +4085,164 @@ ${p.content}`).join("\n\n");
4040
4085
  }
4041
4086
  });
4042
4087
 
4088
+ // src/lib/memory-cards.ts
4089
+ var memory_cards_exports = {};
4090
+ __export(memory_cards_exports, {
4091
+ extractMemoryCards: () => extractMemoryCards,
4092
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4093
+ searchMemoryCards: () => searchMemoryCards
4094
+ });
4095
+ import { createHash as createHash2 } from "crypto";
4096
+ function stableId(memoryId, type, content) {
4097
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4098
+ }
4099
+ function cleanText(text) {
4100
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4101
+ }
4102
+ function splitSentences(text) {
4103
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4104
+ }
4105
+ function inferCardType(sentence, toolName) {
4106
+ const lower = sentence.toLowerCase();
4107
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4108
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4109
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4110
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4111
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4112
+ return "fact";
4113
+ }
4114
+ function extractSubject(sentence, agentId) {
4115
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4116
+ return explicit?.[1] ?? agentId;
4117
+ }
4118
+ function predicateFor(type) {
4119
+ switch (type) {
4120
+ case "preference":
4121
+ return "prefers";
4122
+ case "belief_update":
4123
+ return "updated";
4124
+ case "decision":
4125
+ return "decided";
4126
+ case "event":
4127
+ return "happened";
4128
+ case "code":
4129
+ return "implemented";
4130
+ default:
4131
+ return "states";
4132
+ }
4133
+ }
4134
+ function extractMemoryCards(row) {
4135
+ const sentences = splitSentences(row.raw_text);
4136
+ const cards = [];
4137
+ for (const sentence of sentences) {
4138
+ const type = inferCardType(sentence, row.tool_name);
4139
+ const subject = extractSubject(sentence, row.agent_id);
4140
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4141
+ cards.push({
4142
+ id: stableId(row.id, type, content),
4143
+ memory_id: row.id,
4144
+ agent_id: row.agent_id,
4145
+ session_id: row.session_id,
4146
+ project_name: row.project_name ?? null,
4147
+ timestamp: row.timestamp,
4148
+ card_type: type,
4149
+ subject,
4150
+ predicate: predicateFor(type),
4151
+ object: content,
4152
+ content,
4153
+ source_ref: row.id,
4154
+ confidence: type === "fact" ? 0.55 : 0.65
4155
+ });
4156
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4157
+ }
4158
+ return cards;
4159
+ }
4160
+ async function insertMemoryCardsForBatch(rows) {
4161
+ const cards = rows.flatMap(extractMemoryCards);
4162
+ if (cards.length === 0) return 0;
4163
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4164
+ const client = getClient();
4165
+ const stmts = cards.map((card) => ({
4166
+ sql: `INSERT OR IGNORE INTO memory_cards
4167
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4168
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4169
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4170
+ args: [
4171
+ card.id,
4172
+ card.memory_id,
4173
+ card.agent_id,
4174
+ card.session_id,
4175
+ card.project_name,
4176
+ card.timestamp,
4177
+ card.card_type,
4178
+ card.subject,
4179
+ card.predicate,
4180
+ card.object,
4181
+ card.content,
4182
+ card.source_ref,
4183
+ card.confidence,
4184
+ now
4185
+ ]
4186
+ }));
4187
+ await client.batch(stmts, "write");
4188
+ return cards.length;
4189
+ }
4190
+ function buildMatchExpr(queryText) {
4191
+ 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);
4192
+ if (terms.length === 0) return null;
4193
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4194
+ }
4195
+ async function searchMemoryCards(queryText, agentId, options) {
4196
+ const limit = options?.limit ?? 10;
4197
+ const matchExpr = buildMatchExpr(queryText);
4198
+ if (!matchExpr) return [];
4199
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4200
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4201
+ FROM memory_cards c
4202
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4203
+ WHERE memory_cards_fts MATCH ?
4204
+ AND c.agent_id = ?
4205
+ AND COALESCE(c.active, 1) = 1`;
4206
+ const args = [matchExpr, agentId];
4207
+ if (options?.projectName) {
4208
+ sql += ` AND c.project_name = ?`;
4209
+ args.push(options.projectName);
4210
+ }
4211
+ if (options?.since) {
4212
+ sql += ` AND c.timestamp >= ?`;
4213
+ args.push(options.since);
4214
+ }
4215
+ sql += ` ORDER BY rank LIMIT ?`;
4216
+ args.push(limit);
4217
+ const result = await getClient().execute({ sql, args });
4218
+ return result.rows.map((row) => ({
4219
+ id: `card:${String(row.id)}`,
4220
+ agent_id: String(row.agent_id),
4221
+ agent_role: "memory_card",
4222
+ session_id: String(row.session_id),
4223
+ timestamp: String(row.timestamp),
4224
+ tool_name: `memory_card:${String(row.card_type)}`,
4225
+ project_name: row.project_name == null ? "" : String(row.project_name),
4226
+ has_error: false,
4227
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4228
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4229
+ vector: [],
4230
+ importance: 6,
4231
+ status: "active",
4232
+ confidence: Number(row.confidence ?? 0.6),
4233
+ last_accessed: String(row.timestamp)
4234
+ }));
4235
+ }
4236
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4237
+ var init_memory_cards = __esm({
4238
+ "src/lib/memory-cards.ts"() {
4239
+ "use strict";
4240
+ init_database();
4241
+ MAX_CARDS_PER_MEMORY = 6;
4242
+ MAX_SENTENCE_CHARS = 360;
4243
+ }
4244
+ });
4245
+
4043
4246
  // src/lib/store.ts
4044
4247
  var store_exports = {};
4045
4248
  __export(store_exports, {
@@ -4378,6 +4581,11 @@ async function flushBatch() {
4378
4581
  const globalClient = getClient();
4379
4582
  const globalStmts = batch.map(buildStmt);
4380
4583
  await globalClient.batch(globalStmts, "write");
4584
+ try {
4585
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4586
+ await insertMemoryCardsForBatch2(batch);
4587
+ } catch {
4588
+ }
4381
4589
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4382
4590
  _pendingRecords.splice(0, batch.length);
4383
4591
  try {
@@ -5136,7 +5344,7 @@ var init_tasks_review = __esm({
5136
5344
 
5137
5345
  // src/bin/exe-heartbeat.ts
5138
5346
  init_database();
5139
- import { createHash as createHash2 } from "crypto";
5347
+ import { createHash as createHash3 } from "crypto";
5140
5348
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
5141
5349
  import os12 from "os";
5142
5350
  import path17 from "path";
@@ -5264,7 +5472,7 @@ function writeMarker(marker) {
5264
5472
  }
5265
5473
  }
5266
5474
  function hashOutput(text) {
5267
- return createHash2("sha256").update(text).digest("hex");
5475
+ return createHash3("sha256").update(text).digest("hex");
5268
5476
  }
5269
5477
  async function queryPendingReviews() {
5270
5478
  let sessionScope;