@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,
@@ -3121,6 +3121,51 @@ async function ensureSchema() {
3121
3121
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
3122
3122
  END;
3123
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
+ `);
3124
3169
  try {
3125
3170
  await client.execute({
3126
3171
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4410,6 +4455,164 @@ ${p.content}`).join("\n\n");
4410
4455
  }
4411
4456
  });
4412
4457
 
4458
+ // src/lib/memory-cards.ts
4459
+ var memory_cards_exports = {};
4460
+ __export(memory_cards_exports, {
4461
+ extractMemoryCards: () => extractMemoryCards,
4462
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4463
+ searchMemoryCards: () => searchMemoryCards
4464
+ });
4465
+ import { createHash as createHash2 } from "crypto";
4466
+ function stableId(memoryId, type, content) {
4467
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4468
+ }
4469
+ function cleanText(text) {
4470
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4471
+ }
4472
+ function splitSentences(text) {
4473
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4474
+ }
4475
+ function inferCardType(sentence, toolName) {
4476
+ const lower = sentence.toLowerCase();
4477
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4478
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4479
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4480
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4481
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4482
+ return "fact";
4483
+ }
4484
+ function extractSubject(sentence, agentId) {
4485
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4486
+ return explicit?.[1] ?? agentId;
4487
+ }
4488
+ function predicateFor(type) {
4489
+ switch (type) {
4490
+ case "preference":
4491
+ return "prefers";
4492
+ case "belief_update":
4493
+ return "updated";
4494
+ case "decision":
4495
+ return "decided";
4496
+ case "event":
4497
+ return "happened";
4498
+ case "code":
4499
+ return "implemented";
4500
+ default:
4501
+ return "states";
4502
+ }
4503
+ }
4504
+ function extractMemoryCards(row) {
4505
+ const sentences = splitSentences(row.raw_text);
4506
+ const cards = [];
4507
+ for (const sentence of sentences) {
4508
+ const type = inferCardType(sentence, row.tool_name);
4509
+ const subject = extractSubject(sentence, row.agent_id);
4510
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4511
+ cards.push({
4512
+ id: stableId(row.id, type, content),
4513
+ memory_id: row.id,
4514
+ agent_id: row.agent_id,
4515
+ session_id: row.session_id,
4516
+ project_name: row.project_name ?? null,
4517
+ timestamp: row.timestamp,
4518
+ card_type: type,
4519
+ subject,
4520
+ predicate: predicateFor(type),
4521
+ object: content,
4522
+ content,
4523
+ source_ref: row.id,
4524
+ confidence: type === "fact" ? 0.55 : 0.65
4525
+ });
4526
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4527
+ }
4528
+ return cards;
4529
+ }
4530
+ async function insertMemoryCardsForBatch(rows) {
4531
+ const cards = rows.flatMap(extractMemoryCards);
4532
+ if (cards.length === 0) return 0;
4533
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4534
+ const client = getClient();
4535
+ const stmts = cards.map((card) => ({
4536
+ sql: `INSERT OR IGNORE INTO memory_cards
4537
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4538
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4539
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4540
+ args: [
4541
+ card.id,
4542
+ card.memory_id,
4543
+ card.agent_id,
4544
+ card.session_id,
4545
+ card.project_name,
4546
+ card.timestamp,
4547
+ card.card_type,
4548
+ card.subject,
4549
+ card.predicate,
4550
+ card.object,
4551
+ card.content,
4552
+ card.source_ref,
4553
+ card.confidence,
4554
+ now
4555
+ ]
4556
+ }));
4557
+ await client.batch(stmts, "write");
4558
+ return cards.length;
4559
+ }
4560
+ function buildMatchExpr(queryText) {
4561
+ 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);
4562
+ if (terms.length === 0) return null;
4563
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4564
+ }
4565
+ async function searchMemoryCards(queryText, agentId, options) {
4566
+ const limit = options?.limit ?? 10;
4567
+ const matchExpr = buildMatchExpr(queryText);
4568
+ if (!matchExpr) return [];
4569
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4570
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4571
+ FROM memory_cards c
4572
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4573
+ WHERE memory_cards_fts MATCH ?
4574
+ AND c.agent_id = ?
4575
+ AND COALESCE(c.active, 1) = 1`;
4576
+ const args = [matchExpr, agentId];
4577
+ if (options?.projectName) {
4578
+ sql += ` AND c.project_name = ?`;
4579
+ args.push(options.projectName);
4580
+ }
4581
+ if (options?.since) {
4582
+ sql += ` AND c.timestamp >= ?`;
4583
+ args.push(options.since);
4584
+ }
4585
+ sql += ` ORDER BY rank LIMIT ?`;
4586
+ args.push(limit);
4587
+ const result = await getClient().execute({ sql, args });
4588
+ return result.rows.map((row) => ({
4589
+ id: `card:${String(row.id)}`,
4590
+ agent_id: String(row.agent_id),
4591
+ agent_role: "memory_card",
4592
+ session_id: String(row.session_id),
4593
+ timestamp: String(row.timestamp),
4594
+ tool_name: `memory_card:${String(row.card_type)}`,
4595
+ project_name: row.project_name == null ? "" : String(row.project_name),
4596
+ has_error: false,
4597
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4598
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4599
+ vector: [],
4600
+ importance: 6,
4601
+ status: "active",
4602
+ confidence: Number(row.confidence ?? 0.6),
4603
+ last_accessed: String(row.timestamp)
4604
+ }));
4605
+ }
4606
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4607
+ var init_memory_cards = __esm({
4608
+ "src/lib/memory-cards.ts"() {
4609
+ "use strict";
4610
+ init_database();
4611
+ MAX_CARDS_PER_MEMORY = 6;
4612
+ MAX_SENTENCE_CHARS = 360;
4613
+ }
4614
+ });
4615
+
4413
4616
  // src/lib/store.ts
4414
4617
  var store_exports = {};
4415
4618
  __export(store_exports, {
@@ -4748,6 +4951,11 @@ async function flushBatch() {
4748
4951
  const globalClient = getClient();
4749
4952
  const globalStmts = batch.map(buildStmt);
4750
4953
  await globalClient.batch(globalStmts, "write");
4954
+ try {
4955
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4956
+ await insertMemoryCardsForBatch2(batch);
4957
+ } catch {
4958
+ }
4751
4959
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4752
4960
  _pendingRecords.splice(0, batch.length);
4753
4961
  try {
@@ -6256,9 +6464,18 @@ async function hybridSearch(queryText, agentId, options) {
6256
6464
  } catch {
6257
6465
  }
6258
6466
  }
6259
- const [ftsResults, vectorResults, grepResults] = await Promise.all([
6467
+ const includeStructuredCards = effectiveIsBroad || effectiveOptions.includeStructuredCards === true;
6468
+ const [ftsResults, vectorResults, cardResults, grepResults] = await Promise.all([
6260
6469
  lightweightSearch(effectiveQuery, agentId, fetchOptions),
6261
6470
  queryVector ? searchMemories(queryVector, agentId, fetchOptions) : Promise.resolve([]),
6471
+ includeStructuredCards ? (async () => {
6472
+ try {
6473
+ const { searchMemoryCards: searchMemoryCards2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
6474
+ return await searchMemoryCards2(effectiveQuery, agentId, fetchOptions);
6475
+ } catch {
6476
+ return [];
6477
+ }
6478
+ })() : Promise.resolve([]),
6262
6479
  grepPromise
6263
6480
  ]);
6264
6481
  const lists = [];
@@ -6271,6 +6488,10 @@ async function hybridSearch(queryText, agentId, options) {
6271
6488
  lists.push(vectorResults);
6272
6489
  weights.push(1);
6273
6490
  }
6491
+ if (cardResults.length > 0) {
6492
+ lists.push(cardResults);
6493
+ weights.push(1.2);
6494
+ }
6274
6495
  if (grepResults.length > 0) {
6275
6496
  lists.push(grepResults);
6276
6497
  weights.push(0.5);
@@ -6339,6 +6560,7 @@ async function hybridSearch(queryText, agentId, options) {
6339
6560
  signals: {
6340
6561
  fts: ftsResults.length,
6341
6562
  vector: vectorResults.length,
6563
+ cards: cardResults.length,
6342
6564
  grep: grepResults.length,
6343
6565
  entityBoost: entityBoostRan,
6344
6566
  reranker: rerankedAndBlended !== null,
@@ -6488,6 +6710,15 @@ async function lightweightSearch(queryText, agentId, options) {
6488
6710
  if (options?.includeSource && results.length > 0) {
6489
6711
  await attachDocumentMetadata(results);
6490
6712
  }
6713
+ if (options?.includeStructuredCards !== true) return results;
6714
+ try {
6715
+ const { searchMemoryCards: searchMemoryCards2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
6716
+ const cardResults = await searchMemoryCards2(queryText, agentId, options);
6717
+ if (cardResults.length > 0) {
6718
+ return rrfMergeMulti([results, cardResults], limit, RRF_K, [1, 1.2]);
6719
+ }
6720
+ } catch {
6721
+ }
6491
6722
  return results;
6492
6723
  }
6493
6724
  async function ftsQuery(client, matchExpr, agentId, options, limit) {
@@ -7696,7 +7927,7 @@ __export(identity_exports, {
7696
7927
  import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "fs";
7697
7928
  import { readdirSync as readdirSync5 } from "fs";
7698
7929
  import path20 from "path";
7699
- import { createHash as createHash2 } from "crypto";
7930
+ import { createHash as createHash3 } from "crypto";
7700
7931
  function ensureDir() {
7701
7932
  if (!existsSync16(IDENTITY_DIR2)) {
7702
7933
  mkdirSync8(IDENTITY_DIR2, { recursive: true });
@@ -7743,7 +7974,7 @@ function parseFrontmatter(raw) {
7743
7974
  };
7744
7975
  }
7745
7976
  function contentHash(content) {
7746
- return createHash2("sha256").update(content).digest("hex").slice(0, 16);
7977
+ return createHash3("sha256").update(content).digest("hex").slice(0, 16);
7747
7978
  }
7748
7979
  function getIdentity(agentId) {
7749
7980
  const filePath = identityPath(agentId);
@@ -221,8 +221,8 @@ var init_config = __esm({
221
221
  rerankerAutoTrigger: {
222
222
  enabled: true,
223
223
  broadQueryMinCardinality: 5e4,
224
- fetchTopK: 150,
225
- returnTopK: 5
224
+ fetchTopK: 200,
225
+ returnTopK: 20
226
226
  }
227
227
  },
228
228
  graphRagEnabled: true,
@@ -3044,6 +3044,51 @@ async function ensureSchema() {
3044
3044
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
3045
3045
  END;
3046
3046
  `);
3047
+ await client.executeMultiple(`
3048
+ CREATE TABLE IF NOT EXISTS memory_cards (
3049
+ id TEXT PRIMARY KEY,
3050
+ memory_id TEXT NOT NULL,
3051
+ agent_id TEXT NOT NULL,
3052
+ session_id TEXT NOT NULL,
3053
+ project_name TEXT,
3054
+ timestamp TEXT NOT NULL,
3055
+ card_type TEXT NOT NULL,
3056
+ subject TEXT,
3057
+ predicate TEXT,
3058
+ object TEXT,
3059
+ content TEXT NOT NULL,
3060
+ source_ref TEXT,
3061
+ confidence REAL DEFAULT 0.6,
3062
+ active INTEGER DEFAULT 1,
3063
+ created_at TEXT NOT NULL
3064
+ );
3065
+
3066
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
3067
+ ON memory_cards(agent_id, active, timestamp);
3068
+
3069
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
3070
+ ON memory_cards(memory_id);
3071
+
3072
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
3073
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
3074
+
3075
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
3076
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3077
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3078
+ END;
3079
+
3080
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
3081
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3082
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3083
+ END;
3084
+
3085
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
3086
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3087
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3088
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3089
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3090
+ END;
3091
+ `);
3047
3092
  try {
3048
3093
  await client.execute({
3049
3094
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4476,6 +4521,164 @@ ${p.content}`).join("\n\n");
4476
4521
  }
4477
4522
  });
4478
4523
 
4524
+ // src/lib/memory-cards.ts
4525
+ var memory_cards_exports = {};
4526
+ __export(memory_cards_exports, {
4527
+ extractMemoryCards: () => extractMemoryCards,
4528
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4529
+ searchMemoryCards: () => searchMemoryCards
4530
+ });
4531
+ import { createHash as createHash2 } from "crypto";
4532
+ function stableId(memoryId, type, content) {
4533
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4534
+ }
4535
+ function cleanText(text) {
4536
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4537
+ }
4538
+ function splitSentences(text) {
4539
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4540
+ }
4541
+ function inferCardType(sentence, toolName) {
4542
+ const lower = sentence.toLowerCase();
4543
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4544
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4545
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4546
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4547
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4548
+ return "fact";
4549
+ }
4550
+ function extractSubject(sentence, agentId) {
4551
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4552
+ return explicit?.[1] ?? agentId;
4553
+ }
4554
+ function predicateFor(type) {
4555
+ switch (type) {
4556
+ case "preference":
4557
+ return "prefers";
4558
+ case "belief_update":
4559
+ return "updated";
4560
+ case "decision":
4561
+ return "decided";
4562
+ case "event":
4563
+ return "happened";
4564
+ case "code":
4565
+ return "implemented";
4566
+ default:
4567
+ return "states";
4568
+ }
4569
+ }
4570
+ function extractMemoryCards(row) {
4571
+ const sentences = splitSentences(row.raw_text);
4572
+ const cards = [];
4573
+ for (const sentence of sentences) {
4574
+ const type = inferCardType(sentence, row.tool_name);
4575
+ const subject = extractSubject(sentence, row.agent_id);
4576
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4577
+ cards.push({
4578
+ id: stableId(row.id, type, content),
4579
+ memory_id: row.id,
4580
+ agent_id: row.agent_id,
4581
+ session_id: row.session_id,
4582
+ project_name: row.project_name ?? null,
4583
+ timestamp: row.timestamp,
4584
+ card_type: type,
4585
+ subject,
4586
+ predicate: predicateFor(type),
4587
+ object: content,
4588
+ content,
4589
+ source_ref: row.id,
4590
+ confidence: type === "fact" ? 0.55 : 0.65
4591
+ });
4592
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4593
+ }
4594
+ return cards;
4595
+ }
4596
+ async function insertMemoryCardsForBatch(rows) {
4597
+ const cards = rows.flatMap(extractMemoryCards);
4598
+ if (cards.length === 0) return 0;
4599
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4600
+ const client = getClient();
4601
+ const stmts = cards.map((card) => ({
4602
+ sql: `INSERT OR IGNORE INTO memory_cards
4603
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4604
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4605
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4606
+ args: [
4607
+ card.id,
4608
+ card.memory_id,
4609
+ card.agent_id,
4610
+ card.session_id,
4611
+ card.project_name,
4612
+ card.timestamp,
4613
+ card.card_type,
4614
+ card.subject,
4615
+ card.predicate,
4616
+ card.object,
4617
+ card.content,
4618
+ card.source_ref,
4619
+ card.confidence,
4620
+ now
4621
+ ]
4622
+ }));
4623
+ await client.batch(stmts, "write");
4624
+ return cards.length;
4625
+ }
4626
+ function buildMatchExpr(queryText) {
4627
+ 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);
4628
+ if (terms.length === 0) return null;
4629
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4630
+ }
4631
+ async function searchMemoryCards(queryText, agentId, options) {
4632
+ const limit = options?.limit ?? 10;
4633
+ const matchExpr = buildMatchExpr(queryText);
4634
+ if (!matchExpr) return [];
4635
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4636
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4637
+ FROM memory_cards c
4638
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4639
+ WHERE memory_cards_fts MATCH ?
4640
+ AND c.agent_id = ?
4641
+ AND COALESCE(c.active, 1) = 1`;
4642
+ const args = [matchExpr, agentId];
4643
+ if (options?.projectName) {
4644
+ sql += ` AND c.project_name = ?`;
4645
+ args.push(options.projectName);
4646
+ }
4647
+ if (options?.since) {
4648
+ sql += ` AND c.timestamp >= ?`;
4649
+ args.push(options.since);
4650
+ }
4651
+ sql += ` ORDER BY rank LIMIT ?`;
4652
+ args.push(limit);
4653
+ const result = await getClient().execute({ sql, args });
4654
+ return result.rows.map((row) => ({
4655
+ id: `card:${String(row.id)}`,
4656
+ agent_id: String(row.agent_id),
4657
+ agent_role: "memory_card",
4658
+ session_id: String(row.session_id),
4659
+ timestamp: String(row.timestamp),
4660
+ tool_name: `memory_card:${String(row.card_type)}`,
4661
+ project_name: row.project_name == null ? "" : String(row.project_name),
4662
+ has_error: false,
4663
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4664
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4665
+ vector: [],
4666
+ importance: 6,
4667
+ status: "active",
4668
+ confidence: Number(row.confidence ?? 0.6),
4669
+ last_accessed: String(row.timestamp)
4670
+ }));
4671
+ }
4672
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4673
+ var init_memory_cards = __esm({
4674
+ "src/lib/memory-cards.ts"() {
4675
+ "use strict";
4676
+ init_database();
4677
+ MAX_CARDS_PER_MEMORY = 6;
4678
+ MAX_SENTENCE_CHARS = 360;
4679
+ }
4680
+ });
4681
+
4479
4682
  // src/lib/store.ts
4480
4683
  var store_exports = {};
4481
4684
  __export(store_exports, {
@@ -4814,6 +5017,11 @@ async function flushBatch() {
4814
5017
  const globalClient = getClient();
4815
5018
  const globalStmts = batch.map(buildStmt);
4816
5019
  await globalClient.batch(globalStmts, "write");
5020
+ try {
5021
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
5022
+ await insertMemoryCardsForBatch2(batch);
5023
+ } catch {
5024
+ }
4817
5025
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4818
5026
  _pendingRecords.splice(0, batch.length);
4819
5027
  try {