@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
@@ -202,8 +202,8 @@ var init_config = __esm({
202
202
  rerankerAutoTrigger: {
203
203
  enabled: true,
204
204
  broadQueryMinCardinality: 5e4,
205
- fetchTopK: 150,
206
- returnTopK: 5
205
+ fetchTopK: 200,
206
+ returnTopK: 20
207
207
  }
208
208
  },
209
209
  graphRagEnabled: true,
@@ -3018,6 +3018,51 @@ async function ensureSchema() {
3018
3018
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
3019
3019
  END;
3020
3020
  `);
3021
+ await client.executeMultiple(`
3022
+ CREATE TABLE IF NOT EXISTS memory_cards (
3023
+ id TEXT PRIMARY KEY,
3024
+ memory_id TEXT NOT NULL,
3025
+ agent_id TEXT NOT NULL,
3026
+ session_id TEXT NOT NULL,
3027
+ project_name TEXT,
3028
+ timestamp TEXT NOT NULL,
3029
+ card_type TEXT NOT NULL,
3030
+ subject TEXT,
3031
+ predicate TEXT,
3032
+ object TEXT,
3033
+ content TEXT NOT NULL,
3034
+ source_ref TEXT,
3035
+ confidence REAL DEFAULT 0.6,
3036
+ active INTEGER DEFAULT 1,
3037
+ created_at TEXT NOT NULL
3038
+ );
3039
+
3040
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
3041
+ ON memory_cards(agent_id, active, timestamp);
3042
+
3043
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
3044
+ ON memory_cards(memory_id);
3045
+
3046
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
3047
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
3048
+
3049
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
3050
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3051
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3052
+ END;
3053
+
3054
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
3055
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3056
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3057
+ END;
3058
+
3059
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
3060
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3061
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3062
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3063
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3064
+ END;
3065
+ `);
3021
3066
  try {
3022
3067
  await client.execute({
3023
3068
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4450,6 +4495,164 @@ ${p.content}`).join("\n\n");
4450
4495
  }
4451
4496
  });
4452
4497
 
4498
+ // src/lib/memory-cards.ts
4499
+ var memory_cards_exports = {};
4500
+ __export(memory_cards_exports, {
4501
+ extractMemoryCards: () => extractMemoryCards,
4502
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4503
+ searchMemoryCards: () => searchMemoryCards
4504
+ });
4505
+ import { createHash as createHash2 } from "crypto";
4506
+ function stableId(memoryId, type, content) {
4507
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4508
+ }
4509
+ function cleanText(text) {
4510
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4511
+ }
4512
+ function splitSentences(text) {
4513
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4514
+ }
4515
+ function inferCardType(sentence, toolName) {
4516
+ const lower = sentence.toLowerCase();
4517
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4518
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4519
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4520
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4521
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4522
+ return "fact";
4523
+ }
4524
+ function extractSubject(sentence, agentId) {
4525
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4526
+ return explicit?.[1] ?? agentId;
4527
+ }
4528
+ function predicateFor(type) {
4529
+ switch (type) {
4530
+ case "preference":
4531
+ return "prefers";
4532
+ case "belief_update":
4533
+ return "updated";
4534
+ case "decision":
4535
+ return "decided";
4536
+ case "event":
4537
+ return "happened";
4538
+ case "code":
4539
+ return "implemented";
4540
+ default:
4541
+ return "states";
4542
+ }
4543
+ }
4544
+ function extractMemoryCards(row) {
4545
+ const sentences = splitSentences(row.raw_text);
4546
+ const cards = [];
4547
+ for (const sentence of sentences) {
4548
+ const type = inferCardType(sentence, row.tool_name);
4549
+ const subject = extractSubject(sentence, row.agent_id);
4550
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4551
+ cards.push({
4552
+ id: stableId(row.id, type, content),
4553
+ memory_id: row.id,
4554
+ agent_id: row.agent_id,
4555
+ session_id: row.session_id,
4556
+ project_name: row.project_name ?? null,
4557
+ timestamp: row.timestamp,
4558
+ card_type: type,
4559
+ subject,
4560
+ predicate: predicateFor(type),
4561
+ object: content,
4562
+ content,
4563
+ source_ref: row.id,
4564
+ confidence: type === "fact" ? 0.55 : 0.65
4565
+ });
4566
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4567
+ }
4568
+ return cards;
4569
+ }
4570
+ async function insertMemoryCardsForBatch(rows) {
4571
+ const cards = rows.flatMap(extractMemoryCards);
4572
+ if (cards.length === 0) return 0;
4573
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4574
+ const client = getClient();
4575
+ const stmts = cards.map((card) => ({
4576
+ sql: `INSERT OR IGNORE INTO memory_cards
4577
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4578
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4579
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4580
+ args: [
4581
+ card.id,
4582
+ card.memory_id,
4583
+ card.agent_id,
4584
+ card.session_id,
4585
+ card.project_name,
4586
+ card.timestamp,
4587
+ card.card_type,
4588
+ card.subject,
4589
+ card.predicate,
4590
+ card.object,
4591
+ card.content,
4592
+ card.source_ref,
4593
+ card.confidence,
4594
+ now
4595
+ ]
4596
+ }));
4597
+ await client.batch(stmts, "write");
4598
+ return cards.length;
4599
+ }
4600
+ function buildMatchExpr(queryText) {
4601
+ 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);
4602
+ if (terms.length === 0) return null;
4603
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4604
+ }
4605
+ async function searchMemoryCards(queryText, agentId, options) {
4606
+ const limit = options?.limit ?? 10;
4607
+ const matchExpr = buildMatchExpr(queryText);
4608
+ if (!matchExpr) return [];
4609
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4610
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4611
+ FROM memory_cards c
4612
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4613
+ WHERE memory_cards_fts MATCH ?
4614
+ AND c.agent_id = ?
4615
+ AND COALESCE(c.active, 1) = 1`;
4616
+ const args = [matchExpr, agentId];
4617
+ if (options?.projectName) {
4618
+ sql += ` AND c.project_name = ?`;
4619
+ args.push(options.projectName);
4620
+ }
4621
+ if (options?.since) {
4622
+ sql += ` AND c.timestamp >= ?`;
4623
+ args.push(options.since);
4624
+ }
4625
+ sql += ` ORDER BY rank LIMIT ?`;
4626
+ args.push(limit);
4627
+ const result = await getClient().execute({ sql, args });
4628
+ return result.rows.map((row) => ({
4629
+ id: `card:${String(row.id)}`,
4630
+ agent_id: String(row.agent_id),
4631
+ agent_role: "memory_card",
4632
+ session_id: String(row.session_id),
4633
+ timestamp: String(row.timestamp),
4634
+ tool_name: `memory_card:${String(row.card_type)}`,
4635
+ project_name: row.project_name == null ? "" : String(row.project_name),
4636
+ has_error: false,
4637
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4638
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4639
+ vector: [],
4640
+ importance: 6,
4641
+ status: "active",
4642
+ confidence: Number(row.confidence ?? 0.6),
4643
+ last_accessed: String(row.timestamp)
4644
+ }));
4645
+ }
4646
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4647
+ var init_memory_cards = __esm({
4648
+ "src/lib/memory-cards.ts"() {
4649
+ "use strict";
4650
+ init_database();
4651
+ MAX_CARDS_PER_MEMORY = 6;
4652
+ MAX_SENTENCE_CHARS = 360;
4653
+ }
4654
+ });
4655
+
4453
4656
  // src/lib/store.ts
4454
4657
  var store_exports = {};
4455
4658
  __export(store_exports, {
@@ -4788,6 +4991,11 @@ async function flushBatch() {
4788
4991
  const globalClient = getClient();
4789
4992
  const globalStmts = batch.map(buildStmt);
4790
4993
  await globalClient.batch(globalStmts, "write");
4994
+ try {
4995
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4996
+ await insertMemoryCardsForBatch2(batch);
4997
+ } catch {
4998
+ }
4791
4999
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4792
5000
  _pendingRecords.splice(0, batch.length);
4793
5001
  try {
@@ -246,8 +246,8 @@ var init_config = __esm({
246
246
  rerankerAutoTrigger: {
247
247
  enabled: true,
248
248
  broadQueryMinCardinality: 5e4,
249
- fetchTopK: 150,
250
- returnTopK: 5
249
+ fetchTopK: 200,
250
+ returnTopK: 20
251
251
  }
252
252
  },
253
253
  graphRagEnabled: true,
@@ -2887,6 +2887,51 @@ async function ensureSchema() {
2887
2887
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2888
2888
  END;
2889
2889
  `);
2890
+ await client.executeMultiple(`
2891
+ CREATE TABLE IF NOT EXISTS memory_cards (
2892
+ id TEXT PRIMARY KEY,
2893
+ memory_id TEXT NOT NULL,
2894
+ agent_id TEXT NOT NULL,
2895
+ session_id TEXT NOT NULL,
2896
+ project_name TEXT,
2897
+ timestamp TEXT NOT NULL,
2898
+ card_type TEXT NOT NULL,
2899
+ subject TEXT,
2900
+ predicate TEXT,
2901
+ object TEXT,
2902
+ content TEXT NOT NULL,
2903
+ source_ref TEXT,
2904
+ confidence REAL DEFAULT 0.6,
2905
+ active INTEGER DEFAULT 1,
2906
+ created_at TEXT NOT NULL
2907
+ );
2908
+
2909
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2910
+ ON memory_cards(agent_id, active, timestamp);
2911
+
2912
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2913
+ ON memory_cards(memory_id);
2914
+
2915
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2916
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2917
+
2918
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2919
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2920
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2921
+ END;
2922
+
2923
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2924
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2925
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2926
+ END;
2927
+
2928
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2929
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2930
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2931
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2932
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2933
+ END;
2934
+ `);
2890
2935
  try {
2891
2936
  await client.execute({
2892
2937
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4176,6 +4221,164 @@ ${p.content}`).join("\n\n");
4176
4221
  }
4177
4222
  });
4178
4223
 
4224
+ // src/lib/memory-cards.ts
4225
+ var memory_cards_exports = {};
4226
+ __export(memory_cards_exports, {
4227
+ extractMemoryCards: () => extractMemoryCards,
4228
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4229
+ searchMemoryCards: () => searchMemoryCards
4230
+ });
4231
+ import { createHash as createHash2 } from "crypto";
4232
+ function stableId(memoryId, type, content) {
4233
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4234
+ }
4235
+ function cleanText(text) {
4236
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4237
+ }
4238
+ function splitSentences(text) {
4239
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4240
+ }
4241
+ function inferCardType(sentence, toolName) {
4242
+ const lower = sentence.toLowerCase();
4243
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4244
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4245
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4246
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4247
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4248
+ return "fact";
4249
+ }
4250
+ function extractSubject(sentence, agentId) {
4251
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4252
+ return explicit?.[1] ?? agentId;
4253
+ }
4254
+ function predicateFor(type) {
4255
+ switch (type) {
4256
+ case "preference":
4257
+ return "prefers";
4258
+ case "belief_update":
4259
+ return "updated";
4260
+ case "decision":
4261
+ return "decided";
4262
+ case "event":
4263
+ return "happened";
4264
+ case "code":
4265
+ return "implemented";
4266
+ default:
4267
+ return "states";
4268
+ }
4269
+ }
4270
+ function extractMemoryCards(row) {
4271
+ const sentences = splitSentences(row.raw_text);
4272
+ const cards = [];
4273
+ for (const sentence of sentences) {
4274
+ const type = inferCardType(sentence, row.tool_name);
4275
+ const subject = extractSubject(sentence, row.agent_id);
4276
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4277
+ cards.push({
4278
+ id: stableId(row.id, type, content),
4279
+ memory_id: row.id,
4280
+ agent_id: row.agent_id,
4281
+ session_id: row.session_id,
4282
+ project_name: row.project_name ?? null,
4283
+ timestamp: row.timestamp,
4284
+ card_type: type,
4285
+ subject,
4286
+ predicate: predicateFor(type),
4287
+ object: content,
4288
+ content,
4289
+ source_ref: row.id,
4290
+ confidence: type === "fact" ? 0.55 : 0.65
4291
+ });
4292
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4293
+ }
4294
+ return cards;
4295
+ }
4296
+ async function insertMemoryCardsForBatch(rows) {
4297
+ const cards = rows.flatMap(extractMemoryCards);
4298
+ if (cards.length === 0) return 0;
4299
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4300
+ const client = getClient();
4301
+ const stmts = cards.map((card) => ({
4302
+ sql: `INSERT OR IGNORE INTO memory_cards
4303
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4304
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4305
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4306
+ args: [
4307
+ card.id,
4308
+ card.memory_id,
4309
+ card.agent_id,
4310
+ card.session_id,
4311
+ card.project_name,
4312
+ card.timestamp,
4313
+ card.card_type,
4314
+ card.subject,
4315
+ card.predicate,
4316
+ card.object,
4317
+ card.content,
4318
+ card.source_ref,
4319
+ card.confidence,
4320
+ now
4321
+ ]
4322
+ }));
4323
+ await client.batch(stmts, "write");
4324
+ return cards.length;
4325
+ }
4326
+ function buildMatchExpr(queryText) {
4327
+ 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);
4328
+ if (terms.length === 0) return null;
4329
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4330
+ }
4331
+ async function searchMemoryCards(queryText, agentId, options) {
4332
+ const limit = options?.limit ?? 10;
4333
+ const matchExpr = buildMatchExpr(queryText);
4334
+ if (!matchExpr) return [];
4335
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4336
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4337
+ FROM memory_cards c
4338
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4339
+ WHERE memory_cards_fts MATCH ?
4340
+ AND c.agent_id = ?
4341
+ AND COALESCE(c.active, 1) = 1`;
4342
+ const args = [matchExpr, agentId];
4343
+ if (options?.projectName) {
4344
+ sql += ` AND c.project_name = ?`;
4345
+ args.push(options.projectName);
4346
+ }
4347
+ if (options?.since) {
4348
+ sql += ` AND c.timestamp >= ?`;
4349
+ args.push(options.since);
4350
+ }
4351
+ sql += ` ORDER BY rank LIMIT ?`;
4352
+ args.push(limit);
4353
+ const result = await getClient().execute({ sql, args });
4354
+ return result.rows.map((row) => ({
4355
+ id: `card:${String(row.id)}`,
4356
+ agent_id: String(row.agent_id),
4357
+ agent_role: "memory_card",
4358
+ session_id: String(row.session_id),
4359
+ timestamp: String(row.timestamp),
4360
+ tool_name: `memory_card:${String(row.card_type)}`,
4361
+ project_name: row.project_name == null ? "" : String(row.project_name),
4362
+ has_error: false,
4363
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4364
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4365
+ vector: [],
4366
+ importance: 6,
4367
+ status: "active",
4368
+ confidence: Number(row.confidence ?? 0.6),
4369
+ last_accessed: String(row.timestamp)
4370
+ }));
4371
+ }
4372
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4373
+ var init_memory_cards = __esm({
4374
+ "src/lib/memory-cards.ts"() {
4375
+ "use strict";
4376
+ init_database();
4377
+ MAX_CARDS_PER_MEMORY = 6;
4378
+ MAX_SENTENCE_CHARS = 360;
4379
+ }
4380
+ });
4381
+
4179
4382
  // src/lib/store.ts
4180
4383
  var store_exports = {};
4181
4384
  __export(store_exports, {
@@ -4514,6 +4717,11 @@ async function flushBatch() {
4514
4717
  const globalClient = getClient();
4515
4718
  const globalStmts = batch.map(buildStmt);
4516
4719
  await globalClient.batch(globalStmts, "write");
4720
+ try {
4721
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4722
+ await insertMemoryCardsForBatch2(batch);
4723
+ } catch {
4724
+ }
4517
4725
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4518
4726
  _pendingRecords.splice(0, batch.length);
4519
4727
  try {
@@ -6022,9 +6230,18 @@ async function hybridSearch(queryText, agentId, options) {
6022
6230
  } catch {
6023
6231
  }
6024
6232
  }
6025
- const [ftsResults, vectorResults, grepResults] = await Promise.all([
6233
+ const includeStructuredCards = effectiveIsBroad || effectiveOptions.includeStructuredCards === true;
6234
+ const [ftsResults, vectorResults, cardResults, grepResults] = await Promise.all([
6026
6235
  lightweightSearch(effectiveQuery, agentId, fetchOptions),
6027
6236
  queryVector ? searchMemories(queryVector, agentId, fetchOptions) : Promise.resolve([]),
6237
+ includeStructuredCards ? (async () => {
6238
+ try {
6239
+ const { searchMemoryCards: searchMemoryCards2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
6240
+ return await searchMemoryCards2(effectiveQuery, agentId, fetchOptions);
6241
+ } catch {
6242
+ return [];
6243
+ }
6244
+ })() : Promise.resolve([]),
6028
6245
  grepPromise
6029
6246
  ]);
6030
6247
  const lists = [];
@@ -6037,6 +6254,10 @@ async function hybridSearch(queryText, agentId, options) {
6037
6254
  lists.push(vectorResults);
6038
6255
  weights.push(1);
6039
6256
  }
6257
+ if (cardResults.length > 0) {
6258
+ lists.push(cardResults);
6259
+ weights.push(1.2);
6260
+ }
6040
6261
  if (grepResults.length > 0) {
6041
6262
  lists.push(grepResults);
6042
6263
  weights.push(0.5);
@@ -6105,6 +6326,7 @@ async function hybridSearch(queryText, agentId, options) {
6105
6326
  signals: {
6106
6327
  fts: ftsResults.length,
6107
6328
  vector: vectorResults.length,
6329
+ cards: cardResults.length,
6108
6330
  grep: grepResults.length,
6109
6331
  entityBoost: entityBoostRan,
6110
6332
  reranker: rerankedAndBlended !== null,
@@ -6254,6 +6476,15 @@ async function lightweightSearch(queryText, agentId, options) {
6254
6476
  if (options?.includeSource && results.length > 0) {
6255
6477
  await attachDocumentMetadata(results);
6256
6478
  }
6479
+ if (options?.includeStructuredCards !== true) return results;
6480
+ try {
6481
+ const { searchMemoryCards: searchMemoryCards2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
6482
+ const cardResults = await searchMemoryCards2(queryText, agentId, options);
6483
+ if (cardResults.length > 0) {
6484
+ return rrfMergeMulti([results, cardResults], limit, RRF_K, [1, 1.2]);
6485
+ }
6486
+ } catch {
6487
+ }
6257
6488
  return results;
6258
6489
  }
6259
6490
  async function ftsQuery(client, matchExpr, agentId, options, limit) {