@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
@@ -85,8 +85,8 @@ var init_config = __esm({
85
85
  rerankerAutoTrigger: {
86
86
  enabled: true,
87
87
  broadQueryMinCardinality: 5e4,
88
- fetchTopK: 150,
89
- returnTopK: 5
88
+ fetchTopK: 200,
89
+ returnTopK: 20
90
90
  }
91
91
  },
92
92
  graphRagEnabled: true,
@@ -69,8 +69,8 @@ var init_config = __esm({
69
69
  rerankerAutoTrigger: {
70
70
  enabled: true,
71
71
  broadQueryMinCardinality: 5e4,
72
- fetchTopK: 150,
73
- returnTopK: 5
72
+ fetchTopK: 200,
73
+ returnTopK: 20
74
74
  }
75
75
  },
76
76
  graphRagEnabled: true,
@@ -192,8 +192,8 @@ var init_config = __esm({
192
192
  rerankerAutoTrigger: {
193
193
  enabled: true,
194
194
  broadQueryMinCardinality: 5e4,
195
- fetchTopK: 150,
196
- returnTopK: 5
195
+ fetchTopK: 200,
196
+ returnTopK: 20
197
197
  }
198
198
  },
199
199
  graphRagEnabled: true,
@@ -2656,6 +2656,51 @@ async function ensureSchema() {
2656
2656
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2657
2657
  END;
2658
2658
  `);
2659
+ await client.executeMultiple(`
2660
+ CREATE TABLE IF NOT EXISTS memory_cards (
2661
+ id TEXT PRIMARY KEY,
2662
+ memory_id TEXT NOT NULL,
2663
+ agent_id TEXT NOT NULL,
2664
+ session_id TEXT NOT NULL,
2665
+ project_name TEXT,
2666
+ timestamp TEXT NOT NULL,
2667
+ card_type TEXT NOT NULL,
2668
+ subject TEXT,
2669
+ predicate TEXT,
2670
+ object TEXT,
2671
+ content TEXT NOT NULL,
2672
+ source_ref TEXT,
2673
+ confidence REAL DEFAULT 0.6,
2674
+ active INTEGER DEFAULT 1,
2675
+ created_at TEXT NOT NULL
2676
+ );
2677
+
2678
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2679
+ ON memory_cards(agent_id, active, timestamp);
2680
+
2681
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2682
+ ON memory_cards(memory_id);
2683
+
2684
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2685
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2686
+
2687
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2688
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2689
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2690
+ END;
2691
+
2692
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2693
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2694
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2695
+ END;
2696
+
2697
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2698
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2699
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2700
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2701
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2702
+ END;
2703
+ `);
2659
2704
  try {
2660
2705
  await client.execute({
2661
2706
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -3473,6 +3518,164 @@ ${p.content}`).join("\n\n");
3473
3518
  }
3474
3519
  });
3475
3520
 
3521
+ // src/lib/memory-cards.ts
3522
+ var memory_cards_exports = {};
3523
+ __export(memory_cards_exports, {
3524
+ extractMemoryCards: () => extractMemoryCards,
3525
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
3526
+ searchMemoryCards: () => searchMemoryCards
3527
+ });
3528
+ import { createHash as createHash2 } from "crypto";
3529
+ function stableId(memoryId, type, content) {
3530
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
3531
+ }
3532
+ function cleanText(text) {
3533
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
3534
+ }
3535
+ function splitSentences(text) {
3536
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
3537
+ }
3538
+ function inferCardType(sentence, toolName) {
3539
+ const lower = sentence.toLowerCase();
3540
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
3541
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
3542
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
3543
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
3544
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
3545
+ return "fact";
3546
+ }
3547
+ function extractSubject(sentence, agentId) {
3548
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
3549
+ return explicit?.[1] ?? agentId;
3550
+ }
3551
+ function predicateFor(type) {
3552
+ switch (type) {
3553
+ case "preference":
3554
+ return "prefers";
3555
+ case "belief_update":
3556
+ return "updated";
3557
+ case "decision":
3558
+ return "decided";
3559
+ case "event":
3560
+ return "happened";
3561
+ case "code":
3562
+ return "implemented";
3563
+ default:
3564
+ return "states";
3565
+ }
3566
+ }
3567
+ function extractMemoryCards(row) {
3568
+ const sentences = splitSentences(row.raw_text);
3569
+ const cards = [];
3570
+ for (const sentence of sentences) {
3571
+ const type = inferCardType(sentence, row.tool_name);
3572
+ const subject = extractSubject(sentence, row.agent_id);
3573
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
3574
+ cards.push({
3575
+ id: stableId(row.id, type, content),
3576
+ memory_id: row.id,
3577
+ agent_id: row.agent_id,
3578
+ session_id: row.session_id,
3579
+ project_name: row.project_name ?? null,
3580
+ timestamp: row.timestamp,
3581
+ card_type: type,
3582
+ subject,
3583
+ predicate: predicateFor(type),
3584
+ object: content,
3585
+ content,
3586
+ source_ref: row.id,
3587
+ confidence: type === "fact" ? 0.55 : 0.65
3588
+ });
3589
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
3590
+ }
3591
+ return cards;
3592
+ }
3593
+ async function insertMemoryCardsForBatch(rows) {
3594
+ const cards = rows.flatMap(extractMemoryCards);
3595
+ if (cards.length === 0) return 0;
3596
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3597
+ const client = getClient();
3598
+ const stmts = cards.map((card) => ({
3599
+ sql: `INSERT OR IGNORE INTO memory_cards
3600
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
3601
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
3602
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
3603
+ args: [
3604
+ card.id,
3605
+ card.memory_id,
3606
+ card.agent_id,
3607
+ card.session_id,
3608
+ card.project_name,
3609
+ card.timestamp,
3610
+ card.card_type,
3611
+ card.subject,
3612
+ card.predicate,
3613
+ card.object,
3614
+ card.content,
3615
+ card.source_ref,
3616
+ card.confidence,
3617
+ now
3618
+ ]
3619
+ }));
3620
+ await client.batch(stmts, "write");
3621
+ return cards.length;
3622
+ }
3623
+ function buildMatchExpr(queryText) {
3624
+ 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);
3625
+ if (terms.length === 0) return null;
3626
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
3627
+ }
3628
+ async function searchMemoryCards(queryText, agentId, options) {
3629
+ const limit = options?.limit ?? 10;
3630
+ const matchExpr = buildMatchExpr(queryText);
3631
+ if (!matchExpr) return [];
3632
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
3633
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
3634
+ FROM memory_cards c
3635
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
3636
+ WHERE memory_cards_fts MATCH ?
3637
+ AND c.agent_id = ?
3638
+ AND COALESCE(c.active, 1) = 1`;
3639
+ const args = [matchExpr, agentId];
3640
+ if (options?.projectName) {
3641
+ sql += ` AND c.project_name = ?`;
3642
+ args.push(options.projectName);
3643
+ }
3644
+ if (options?.since) {
3645
+ sql += ` AND c.timestamp >= ?`;
3646
+ args.push(options.since);
3647
+ }
3648
+ sql += ` ORDER BY rank LIMIT ?`;
3649
+ args.push(limit);
3650
+ const result = await getClient().execute({ sql, args });
3651
+ return result.rows.map((row) => ({
3652
+ id: `card:${String(row.id)}`,
3653
+ agent_id: String(row.agent_id),
3654
+ agent_role: "memory_card",
3655
+ session_id: String(row.session_id),
3656
+ timestamp: String(row.timestamp),
3657
+ tool_name: `memory_card:${String(row.card_type)}`,
3658
+ project_name: row.project_name == null ? "" : String(row.project_name),
3659
+ has_error: false,
3660
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
3661
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
3662
+ vector: [],
3663
+ importance: 6,
3664
+ status: "active",
3665
+ confidence: Number(row.confidence ?? 0.6),
3666
+ last_accessed: String(row.timestamp)
3667
+ }));
3668
+ }
3669
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
3670
+ var init_memory_cards = __esm({
3671
+ "src/lib/memory-cards.ts"() {
3672
+ "use strict";
3673
+ init_database();
3674
+ MAX_CARDS_PER_MEMORY = 6;
3675
+ MAX_SENTENCE_CHARS = 360;
3676
+ }
3677
+ });
3678
+
3476
3679
  // src/bin/exe-assign.ts
3477
3680
  init_employees();
3478
3681
 
@@ -4409,6 +4612,11 @@ async function flushBatch() {
4409
4612
  const globalClient = getClient();
4410
4613
  const globalStmts = batch.map(buildStmt);
4411
4614
  await globalClient.batch(globalStmts, "write");
4615
+ try {
4616
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4617
+ await insertMemoryCardsForBatch2(batch);
4618
+ } catch {
4619
+ }
4412
4620
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4413
4621
  _pendingRecords.splice(0, batch.length);
4414
4622
  try {
@@ -258,8 +258,8 @@ var init_config = __esm({
258
258
  rerankerAutoTrigger: {
259
259
  enabled: true,
260
260
  broadQueryMinCardinality: 5e4,
261
- fetchTopK: 150,
262
- returnTopK: 5
261
+ fetchTopK: 200,
262
+ returnTopK: 20
263
263
  }
264
264
  },
265
265
  graphRagEnabled: true,
@@ -2798,6 +2798,51 @@ async function ensureSchema() {
2798
2798
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2799
2799
  END;
2800
2800
  `);
2801
+ await client.executeMultiple(`
2802
+ CREATE TABLE IF NOT EXISTS memory_cards (
2803
+ id TEXT PRIMARY KEY,
2804
+ memory_id TEXT NOT NULL,
2805
+ agent_id TEXT NOT NULL,
2806
+ session_id TEXT NOT NULL,
2807
+ project_name TEXT,
2808
+ timestamp TEXT NOT NULL,
2809
+ card_type TEXT NOT NULL,
2810
+ subject TEXT,
2811
+ predicate TEXT,
2812
+ object TEXT,
2813
+ content TEXT NOT NULL,
2814
+ source_ref TEXT,
2815
+ confidence REAL DEFAULT 0.6,
2816
+ active INTEGER DEFAULT 1,
2817
+ created_at TEXT NOT NULL
2818
+ );
2819
+
2820
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2821
+ ON memory_cards(agent_id, active, timestamp);
2822
+
2823
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2824
+ ON memory_cards(memory_id);
2825
+
2826
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2827
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2828
+
2829
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2830
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2831
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2832
+ END;
2833
+
2834
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2835
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2836
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2837
+ END;
2838
+
2839
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2840
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2841
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2842
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2843
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2844
+ END;
2845
+ `);
2801
2846
  try {
2802
2847
  await client.execute({
2803
2848
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -90,8 +90,8 @@ var init_config = __esm({
90
90
  rerankerAutoTrigger: {
91
91
  enabled: true,
92
92
  broadQueryMinCardinality: 5e4,
93
- fetchTopK: 150,
94
- returnTopK: 5
93
+ fetchTopK: 200,
94
+ returnTopK: 20
95
95
  }
96
96
  },
97
97
  graphRagEnabled: true,
@@ -460,8 +460,8 @@ var init_config = __esm({
460
460
  rerankerAutoTrigger: {
461
461
  enabled: true,
462
462
  broadQueryMinCardinality: 5e4,
463
- fetchTopK: 150,
464
- returnTopK: 5
463
+ fetchTopK: 200,
464
+ returnTopK: 20
465
465
  }
466
466
  },
467
467
  graphRagEnabled: true,
@@ -550,8 +550,8 @@ var init_config = __esm({
550
550
  rerankerAutoTrigger: {
551
551
  enabled: true,
552
552
  broadQueryMinCardinality: 5e4,
553
- fetchTopK: 150,
554
- returnTopK: 5
553
+ fetchTopK: 200,
554
+ returnTopK: 20
555
555
  }
556
556
  },
557
557
  graphRagEnabled: true,
@@ -3300,6 +3300,51 @@ async function ensureSchema() {
3300
3300
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
3301
3301
  END;
3302
3302
  `);
3303
+ await client.executeMultiple(`
3304
+ CREATE TABLE IF NOT EXISTS memory_cards (
3305
+ id TEXT PRIMARY KEY,
3306
+ memory_id TEXT NOT NULL,
3307
+ agent_id TEXT NOT NULL,
3308
+ session_id TEXT NOT NULL,
3309
+ project_name TEXT,
3310
+ timestamp TEXT NOT NULL,
3311
+ card_type TEXT NOT NULL,
3312
+ subject TEXT,
3313
+ predicate TEXT,
3314
+ object TEXT,
3315
+ content TEXT NOT NULL,
3316
+ source_ref TEXT,
3317
+ confidence REAL DEFAULT 0.6,
3318
+ active INTEGER DEFAULT 1,
3319
+ created_at TEXT NOT NULL
3320
+ );
3321
+
3322
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
3323
+ ON memory_cards(agent_id, active, timestamp);
3324
+
3325
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
3326
+ ON memory_cards(memory_id);
3327
+
3328
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
3329
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
3330
+
3331
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
3332
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3333
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3334
+ END;
3335
+
3336
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
3337
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3338
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3339
+ END;
3340
+
3341
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
3342
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
3343
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
3344
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
3345
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
3346
+ END;
3347
+ `);
3303
3348
  try {
3304
3349
  await client.execute({
3305
3350
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -7766,6 +7811,164 @@ ${p.content}`).join("\n\n");
7766
7811
  }
7767
7812
  });
7768
7813
 
7814
+ // src/lib/memory-cards.ts
7815
+ var memory_cards_exports = {};
7816
+ __export(memory_cards_exports, {
7817
+ extractMemoryCards: () => extractMemoryCards,
7818
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
7819
+ searchMemoryCards: () => searchMemoryCards
7820
+ });
7821
+ import { createHash as createHash2 } from "crypto";
7822
+ function stableId(memoryId, type, content) {
7823
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
7824
+ }
7825
+ function cleanText(text) {
7826
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
7827
+ }
7828
+ function splitSentences(text) {
7829
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
7830
+ }
7831
+ function inferCardType(sentence, toolName) {
7832
+ const lower = sentence.toLowerCase();
7833
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
7834
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
7835
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
7836
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
7837
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
7838
+ return "fact";
7839
+ }
7840
+ function extractSubject(sentence, agentId) {
7841
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
7842
+ return explicit?.[1] ?? agentId;
7843
+ }
7844
+ function predicateFor(type) {
7845
+ switch (type) {
7846
+ case "preference":
7847
+ return "prefers";
7848
+ case "belief_update":
7849
+ return "updated";
7850
+ case "decision":
7851
+ return "decided";
7852
+ case "event":
7853
+ return "happened";
7854
+ case "code":
7855
+ return "implemented";
7856
+ default:
7857
+ return "states";
7858
+ }
7859
+ }
7860
+ function extractMemoryCards(row) {
7861
+ const sentences = splitSentences(row.raw_text);
7862
+ const cards = [];
7863
+ for (const sentence of sentences) {
7864
+ const type = inferCardType(sentence, row.tool_name);
7865
+ const subject = extractSubject(sentence, row.agent_id);
7866
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
7867
+ cards.push({
7868
+ id: stableId(row.id, type, content),
7869
+ memory_id: row.id,
7870
+ agent_id: row.agent_id,
7871
+ session_id: row.session_id,
7872
+ project_name: row.project_name ?? null,
7873
+ timestamp: row.timestamp,
7874
+ card_type: type,
7875
+ subject,
7876
+ predicate: predicateFor(type),
7877
+ object: content,
7878
+ content,
7879
+ source_ref: row.id,
7880
+ confidence: type === "fact" ? 0.55 : 0.65
7881
+ });
7882
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
7883
+ }
7884
+ return cards;
7885
+ }
7886
+ async function insertMemoryCardsForBatch(rows) {
7887
+ const cards = rows.flatMap(extractMemoryCards);
7888
+ if (cards.length === 0) return 0;
7889
+ const now = (/* @__PURE__ */ new Date()).toISOString();
7890
+ const client = getClient();
7891
+ const stmts = cards.map((card) => ({
7892
+ sql: `INSERT OR IGNORE INTO memory_cards
7893
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
7894
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
7895
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
7896
+ args: [
7897
+ card.id,
7898
+ card.memory_id,
7899
+ card.agent_id,
7900
+ card.session_id,
7901
+ card.project_name,
7902
+ card.timestamp,
7903
+ card.card_type,
7904
+ card.subject,
7905
+ card.predicate,
7906
+ card.object,
7907
+ card.content,
7908
+ card.source_ref,
7909
+ card.confidence,
7910
+ now
7911
+ ]
7912
+ }));
7913
+ await client.batch(stmts, "write");
7914
+ return cards.length;
7915
+ }
7916
+ function buildMatchExpr(queryText) {
7917
+ 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);
7918
+ if (terms.length === 0) return null;
7919
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
7920
+ }
7921
+ async function searchMemoryCards(queryText, agentId, options) {
7922
+ const limit = options?.limit ?? 10;
7923
+ const matchExpr = buildMatchExpr(queryText);
7924
+ if (!matchExpr) return [];
7925
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
7926
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
7927
+ FROM memory_cards c
7928
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
7929
+ WHERE memory_cards_fts MATCH ?
7930
+ AND c.agent_id = ?
7931
+ AND COALESCE(c.active, 1) = 1`;
7932
+ const args = [matchExpr, agentId];
7933
+ if (options?.projectName) {
7934
+ sql += ` AND c.project_name = ?`;
7935
+ args.push(options.projectName);
7936
+ }
7937
+ if (options?.since) {
7938
+ sql += ` AND c.timestamp >= ?`;
7939
+ args.push(options.since);
7940
+ }
7941
+ sql += ` ORDER BY rank LIMIT ?`;
7942
+ args.push(limit);
7943
+ const result2 = await getClient().execute({ sql, args });
7944
+ return result2.rows.map((row) => ({
7945
+ id: `card:${String(row.id)}`,
7946
+ agent_id: String(row.agent_id),
7947
+ agent_role: "memory_card",
7948
+ session_id: String(row.session_id),
7949
+ timestamp: String(row.timestamp),
7950
+ tool_name: `memory_card:${String(row.card_type)}`,
7951
+ project_name: row.project_name == null ? "" : String(row.project_name),
7952
+ has_error: false,
7953
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
7954
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
7955
+ vector: [],
7956
+ importance: 6,
7957
+ status: "active",
7958
+ confidence: Number(row.confidence ?? 0.6),
7959
+ last_accessed: String(row.timestamp)
7960
+ }));
7961
+ }
7962
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
7963
+ var init_memory_cards = __esm({
7964
+ "src/lib/memory-cards.ts"() {
7965
+ "use strict";
7966
+ init_database();
7967
+ MAX_CARDS_PER_MEMORY = 6;
7968
+ MAX_SENTENCE_CHARS = 360;
7969
+ }
7970
+ });
7971
+
7769
7972
  // src/lib/store.ts
7770
7973
  var store_exports = {};
7771
7974
  __export(store_exports, {
@@ -8104,6 +8307,11 @@ async function flushBatch() {
8104
8307
  const globalClient = getClient();
8105
8308
  const globalStmts = batch.map(buildStmt);
8106
8309
  await globalClient.batch(globalStmts, "write");
8310
+ try {
8311
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
8312
+ await insertMemoryCardsForBatch2(batch);
8313
+ } catch {
8314
+ }
8107
8315
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
8108
8316
  _pendingRecords.splice(0, batch.length);
8109
8317
  try {