@askexenow/exe-os 0.9.61 → 0.9.62

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 +298 -22
  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 +47 -2
  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 +2 -2
  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 +211 -3
  32. package/dist/bin/exe-start-opencode.js +211 -3
  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 +2 -2
  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 +79 -11
  44. package/dist/bin/update.js +2 -2
  45. package/dist/gateway/index.js +210 -2
  46. package/dist/hooks/bug-report-worker.js +210 -2
  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 +210 -2
  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 +210 -2
  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 +47 -2
  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 +236 -5
  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 +236 -5
  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
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "latest": "0.9.0",
3
+ "latest": "0.9.1",
4
4
  "stacks": {
5
5
  "0.9.0": {
6
6
  "version": "0.9.0",
7
7
  "releasedAt": "2026-05-10T00:00:00Z",
8
- "notes": "Initial omnibus stack manifest for self-hosted Exe OS v0.9 customer VPS deployments.",
8
+ "notes": "Private/customer pilot omnibus stack manifest for self-hosted Exe OS v0.9 VPS deployments. v1.0.0 is the public-beta/stable contract line.",
9
9
  "breakingChanges": [
10
10
  {
11
11
  "id": "whatsapp_relink_required",
@@ -19,36 +19,85 @@
19
19
  "services": {
20
20
  "crm": {
21
21
  "env": "CRM_IMAGE_TAG",
22
- "image": "ghcr.io/askexe/exe-crm:v0.2.1",
22
+ "image": "ghcr.io/askexe/exe-crm:v0.9.0",
23
23
  "healthUrl": "http://127.0.0.1:3000/healthz"
24
24
  },
25
25
  "wiki": {
26
26
  "env": "WIKI_IMAGE_TAG",
27
- "image": "ghcr.io/askexe/exe-wiki:v1.11.1",
27
+ "image": "ghcr.io/askexe/exe-wiki:v0.9.0",
28
28
  "healthUrl": "http://127.0.0.1:3001/api/ping"
29
29
  },
30
30
  "exed": {
31
31
  "env": "EXED_IMAGE_TAG",
32
- "image": "ghcr.io/askexe/exed:v0.9.37",
32
+ "image": "ghcr.io/askexe/exed:v0.9.61",
33
33
  "healthUrl": "http://127.0.0.1:8765/health"
34
34
  },
35
35
  "gateway": {
36
36
  "env": "GATEWAY_IMAGE_TAG",
37
- "image": "ghcr.io/askexe/exe-gateway:v0.3.0",
37
+ "image": "ghcr.io/askexe/exe-gateway:v0.9.0",
38
38
  "healthUrl": "http://127.0.0.1:3100/health"
39
39
  },
40
40
  "monitorAgent": {
41
41
  "env": "MONITOR_AGENT_IMAGE_TAG",
42
- "image": "ghcr.io/askexe/exe-monitor-agent:v0.1.0"
42
+ "image": "ghcr.io/askexe/exe-monitor-agent:v0.9.0"
43
43
  }
44
44
  },
45
45
  "releaseDescriptors": {
46
- "exed": "AskExe/exe-os@0.9.37:stack.release.json",
47
- "crm": "AskExe/exe-crm@0.2.1:stack.release.json",
48
- "wiki": "AskExe/exe-wiki@1.11.1:stack.release.json",
49
- "gateway": "AskExe/exe-gateway@0.3.0:stack.release.json",
50
- "db": "AskExe/exe-db@0.1.0:stack.release.json",
51
- "monitorAgent": "AskExe/exe-monitor@0.1.0:stack.release.json"
46
+ "exed": "AskExe/exe-os@0.9.61:stack.release.json",
47
+ "crm": "AskExe/exe-crm@0.9.0:stack.release.json",
48
+ "wiki": "AskExe/exe-wiki@0.9.0:stack.release.json",
49
+ "gateway": "AskExe/exe-gateway@0.9.0:stack.release.json",
50
+ "db": "AskExe/exe-db@0.9.0:stack.release.json",
51
+ "monitorAgent": "AskExe/exe-monitor@0.9.0:stack.release.json"
52
+ }
53
+ },
54
+ "0.9.1": {
55
+ "version": "0.9.1",
56
+ "releasedAt": "2026-05-10T00:00:00Z",
57
+ "notes": "High Ghost private/customer pilot stack release. Pins exact tested service images; v1.0.0 remains the public-beta/stable contract line.",
58
+ "breakingChanges": [
59
+ {
60
+ "id": "whatsapp_relink_required",
61
+ "title": "WhatsApp QR re-link required for Baileys v7",
62
+ "description": "exe-gateway uses Baileys v7. Existing WhatsApp 6.x linked-device auth state must be backed up and re-linked once with a new QR code.",
63
+ "requiredAction": "Open https://<gateway-domain>/pair/default?token=<admin-token> and scan from WhatsApp \u2192 Linked Devices after the update.",
64
+ "expectedDowntimeMinutes": "2-5",
65
+ "requiresConfirmation": true
66
+ }
67
+ ],
68
+ "services": {
69
+ "crm": {
70
+ "env": "CRM_IMAGE_TAG",
71
+ "image": "ghcr.io/askexe/exe-crm:v0.9.1",
72
+ "healthUrl": "http://127.0.0.1:3000/healthz"
73
+ },
74
+ "wiki": {
75
+ "env": "WIKI_IMAGE_TAG",
76
+ "image": "ghcr.io/askexe/exe-wiki:v0.9.1",
77
+ "healthUrl": "http://127.0.0.1:3001/api/ping"
78
+ },
79
+ "exed": {
80
+ "env": "EXED_IMAGE_TAG",
81
+ "image": "ghcr.io/askexe/exed:v0.9.61",
82
+ "healthUrl": "http://127.0.0.1:8765/health"
83
+ },
84
+ "gateway": {
85
+ "env": "GATEWAY_IMAGE_TAG",
86
+ "image": "ghcr.io/askexe/exe-gateway:v0.9.1",
87
+ "healthUrl": "http://127.0.0.1:3100/health"
88
+ },
89
+ "monitorAgent": {
90
+ "env": "MONITOR_AGENT_IMAGE_TAG",
91
+ "image": "ghcr.io/askexe/exe-monitor-agent:v0.9.1"
92
+ }
93
+ },
94
+ "releaseDescriptors": {
95
+ "exed": "AskExe/exe-os@0.9.61:stack.release.json",
96
+ "crm": "AskExe/exe-crm@0.9.1:stack.release.json",
97
+ "wiki": "AskExe/exe-wiki@0.9.1:stack.release.json",
98
+ "gateway": "AskExe/exe-gateway@0.9.1:stack.release.json",
99
+ "db": "AskExe/exe-db@0.9.1:stack.release.json",
100
+ "monitorAgent": "AskExe/exe-monitor@0.9.1:stack.release.json"
52
101
  }
53
102
  }
54
103
  }
@@ -256,8 +256,8 @@ var init_config = __esm({
256
256
  rerankerAutoTrigger: {
257
257
  enabled: true,
258
258
  broadQueryMinCardinality: 5e4,
259
- fetchTopK: 150,
260
- returnTopK: 5
259
+ fetchTopK: 200,
260
+ returnTopK: 20
261
261
  }
262
262
  },
263
263
  graphRagEnabled: true,
@@ -2646,6 +2646,51 @@ async function ensureSchema() {
2646
2646
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2647
2647
  END;
2648
2648
  `);
2649
+ await client.executeMultiple(`
2650
+ CREATE TABLE IF NOT EXISTS memory_cards (
2651
+ id TEXT PRIMARY KEY,
2652
+ memory_id TEXT NOT NULL,
2653
+ agent_id TEXT NOT NULL,
2654
+ session_id TEXT NOT NULL,
2655
+ project_name TEXT,
2656
+ timestamp TEXT NOT NULL,
2657
+ card_type TEXT NOT NULL,
2658
+ subject TEXT,
2659
+ predicate TEXT,
2660
+ object TEXT,
2661
+ content TEXT NOT NULL,
2662
+ source_ref TEXT,
2663
+ confidence REAL DEFAULT 0.6,
2664
+ active INTEGER DEFAULT 1,
2665
+ created_at TEXT NOT NULL
2666
+ );
2667
+
2668
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2669
+ ON memory_cards(agent_id, active, timestamp);
2670
+
2671
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2672
+ ON memory_cards(memory_id);
2673
+
2674
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2675
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2676
+
2677
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2678
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2679
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2680
+ END;
2681
+
2682
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2683
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2684
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2685
+ END;
2686
+
2687
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2688
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2689
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2690
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2691
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2692
+ END;
2693
+ `);
2649
2694
  try {
2650
2695
  await client.execute({
2651
2696
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -3463,6 +3508,164 @@ ${p.content}`).join("\n\n");
3463
3508
  }
3464
3509
  });
3465
3510
 
3511
+ // src/lib/memory-cards.ts
3512
+ var memory_cards_exports = {};
3513
+ __export(memory_cards_exports, {
3514
+ extractMemoryCards: () => extractMemoryCards,
3515
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
3516
+ searchMemoryCards: () => searchMemoryCards
3517
+ });
3518
+ import { createHash as createHash2 } from "crypto";
3519
+ function stableId(memoryId, type, content) {
3520
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
3521
+ }
3522
+ function cleanText(text) {
3523
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
3524
+ }
3525
+ function splitSentences(text) {
3526
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
3527
+ }
3528
+ function inferCardType(sentence, toolName) {
3529
+ const lower = sentence.toLowerCase();
3530
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
3531
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
3532
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
3533
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
3534
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
3535
+ return "fact";
3536
+ }
3537
+ function extractSubject(sentence, agentId) {
3538
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
3539
+ return explicit?.[1] ?? agentId;
3540
+ }
3541
+ function predicateFor(type) {
3542
+ switch (type) {
3543
+ case "preference":
3544
+ return "prefers";
3545
+ case "belief_update":
3546
+ return "updated";
3547
+ case "decision":
3548
+ return "decided";
3549
+ case "event":
3550
+ return "happened";
3551
+ case "code":
3552
+ return "implemented";
3553
+ default:
3554
+ return "states";
3555
+ }
3556
+ }
3557
+ function extractMemoryCards(row) {
3558
+ const sentences = splitSentences(row.raw_text);
3559
+ const cards = [];
3560
+ for (const sentence of sentences) {
3561
+ const type = inferCardType(sentence, row.tool_name);
3562
+ const subject = extractSubject(sentence, row.agent_id);
3563
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
3564
+ cards.push({
3565
+ id: stableId(row.id, type, content),
3566
+ memory_id: row.id,
3567
+ agent_id: row.agent_id,
3568
+ session_id: row.session_id,
3569
+ project_name: row.project_name ?? null,
3570
+ timestamp: row.timestamp,
3571
+ card_type: type,
3572
+ subject,
3573
+ predicate: predicateFor(type),
3574
+ object: content,
3575
+ content,
3576
+ source_ref: row.id,
3577
+ confidence: type === "fact" ? 0.55 : 0.65
3578
+ });
3579
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
3580
+ }
3581
+ return cards;
3582
+ }
3583
+ async function insertMemoryCardsForBatch(rows) {
3584
+ const cards = rows.flatMap(extractMemoryCards);
3585
+ if (cards.length === 0) return 0;
3586
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3587
+ const client = getClient();
3588
+ const stmts = cards.map((card) => ({
3589
+ sql: `INSERT OR IGNORE INTO memory_cards
3590
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
3591
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
3592
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
3593
+ args: [
3594
+ card.id,
3595
+ card.memory_id,
3596
+ card.agent_id,
3597
+ card.session_id,
3598
+ card.project_name,
3599
+ card.timestamp,
3600
+ card.card_type,
3601
+ card.subject,
3602
+ card.predicate,
3603
+ card.object,
3604
+ card.content,
3605
+ card.source_ref,
3606
+ card.confidence,
3607
+ now
3608
+ ]
3609
+ }));
3610
+ await client.batch(stmts, "write");
3611
+ return cards.length;
3612
+ }
3613
+ function buildMatchExpr(queryText) {
3614
+ 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);
3615
+ if (terms.length === 0) return null;
3616
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
3617
+ }
3618
+ async function searchMemoryCards(queryText, agentId, options) {
3619
+ const limit = options?.limit ?? 10;
3620
+ const matchExpr = buildMatchExpr(queryText);
3621
+ if (!matchExpr) return [];
3622
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
3623
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
3624
+ FROM memory_cards c
3625
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
3626
+ WHERE memory_cards_fts MATCH ?
3627
+ AND c.agent_id = ?
3628
+ AND COALESCE(c.active, 1) = 1`;
3629
+ const args = [matchExpr, agentId];
3630
+ if (options?.projectName) {
3631
+ sql += ` AND c.project_name = ?`;
3632
+ args.push(options.projectName);
3633
+ }
3634
+ if (options?.since) {
3635
+ sql += ` AND c.timestamp >= ?`;
3636
+ args.push(options.since);
3637
+ }
3638
+ sql += ` ORDER BY rank LIMIT ?`;
3639
+ args.push(limit);
3640
+ const result = await getClient().execute({ sql, args });
3641
+ return result.rows.map((row) => ({
3642
+ id: `card:${String(row.id)}`,
3643
+ agent_id: String(row.agent_id),
3644
+ agent_role: "memory_card",
3645
+ session_id: String(row.session_id),
3646
+ timestamp: String(row.timestamp),
3647
+ tool_name: `memory_card:${String(row.card_type)}`,
3648
+ project_name: row.project_name == null ? "" : String(row.project_name),
3649
+ has_error: false,
3650
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
3651
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
3652
+ vector: [],
3653
+ importance: 6,
3654
+ status: "active",
3655
+ confidence: Number(row.confidence ?? 0.6),
3656
+ last_accessed: String(row.timestamp)
3657
+ }));
3658
+ }
3659
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
3660
+ var init_memory_cards = __esm({
3661
+ "src/lib/memory-cards.ts"() {
3662
+ "use strict";
3663
+ init_database();
3664
+ MAX_CARDS_PER_MEMORY = 6;
3665
+ MAX_SENTENCE_CHARS = 360;
3666
+ }
3667
+ });
3668
+
3466
3669
  // src/bin/backfill-conversations.ts
3467
3670
  import crypto2 from "crypto";
3468
3671
  import { createReadStream } from "fs";
@@ -4262,6 +4465,11 @@ async function flushBatch() {
4262
4465
  const globalClient = getClient();
4263
4466
  const globalStmts = batch.map(buildStmt);
4264
4467
  await globalClient.batch(globalStmts, "write");
4468
+ try {
4469
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4470
+ await insertMemoryCardsForBatch2(batch);
4471
+ } catch {
4472
+ }
4265
4473
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4266
4474
  _pendingRecords.splice(0, batch.length);
4267
4475
  try {
@@ -256,8 +256,8 @@ var init_config = __esm({
256
256
  rerankerAutoTrigger: {
257
257
  enabled: true,
258
258
  broadQueryMinCardinality: 5e4,
259
- fetchTopK: 150,
260
- returnTopK: 5
259
+ fetchTopK: 200,
260
+ returnTopK: 20
261
261
  }
262
262
  },
263
263
  graphRagEnabled: true,
@@ -2646,6 +2646,51 @@ async function ensureSchema() {
2646
2646
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2647
2647
  END;
2648
2648
  `);
2649
+ await client.executeMultiple(`
2650
+ CREATE TABLE IF NOT EXISTS memory_cards (
2651
+ id TEXT PRIMARY KEY,
2652
+ memory_id TEXT NOT NULL,
2653
+ agent_id TEXT NOT NULL,
2654
+ session_id TEXT NOT NULL,
2655
+ project_name TEXT,
2656
+ timestamp TEXT NOT NULL,
2657
+ card_type TEXT NOT NULL,
2658
+ subject TEXT,
2659
+ predicate TEXT,
2660
+ object TEXT,
2661
+ content TEXT NOT NULL,
2662
+ source_ref TEXT,
2663
+ confidence REAL DEFAULT 0.6,
2664
+ active INTEGER DEFAULT 1,
2665
+ created_at TEXT NOT NULL
2666
+ );
2667
+
2668
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2669
+ ON memory_cards(agent_id, active, timestamp);
2670
+
2671
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2672
+ ON memory_cards(memory_id);
2673
+
2674
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2675
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2676
+
2677
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2678
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2679
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2680
+ END;
2681
+
2682
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2683
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2684
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2685
+ END;
2686
+
2687
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2688
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2689
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2690
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2691
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2692
+ END;
2693
+ `);
2649
2694
  try {
2650
2695
  await client.execute({
2651
2696
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -3463,6 +3508,164 @@ ${p.content}`).join("\n\n");
3463
3508
  }
3464
3509
  });
3465
3510
 
3511
+ // src/lib/memory-cards.ts
3512
+ var memory_cards_exports = {};
3513
+ __export(memory_cards_exports, {
3514
+ extractMemoryCards: () => extractMemoryCards,
3515
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
3516
+ searchMemoryCards: () => searchMemoryCards
3517
+ });
3518
+ import { createHash as createHash2 } from "crypto";
3519
+ function stableId(memoryId, type, content) {
3520
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
3521
+ }
3522
+ function cleanText(text) {
3523
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
3524
+ }
3525
+ function splitSentences(text) {
3526
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
3527
+ }
3528
+ function inferCardType(sentence, toolName) {
3529
+ const lower = sentence.toLowerCase();
3530
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
3531
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
3532
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
3533
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
3534
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
3535
+ return "fact";
3536
+ }
3537
+ function extractSubject(sentence, agentId) {
3538
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
3539
+ return explicit?.[1] ?? agentId;
3540
+ }
3541
+ function predicateFor(type) {
3542
+ switch (type) {
3543
+ case "preference":
3544
+ return "prefers";
3545
+ case "belief_update":
3546
+ return "updated";
3547
+ case "decision":
3548
+ return "decided";
3549
+ case "event":
3550
+ return "happened";
3551
+ case "code":
3552
+ return "implemented";
3553
+ default:
3554
+ return "states";
3555
+ }
3556
+ }
3557
+ function extractMemoryCards(row) {
3558
+ const sentences = splitSentences(row.raw_text);
3559
+ const cards = [];
3560
+ for (const sentence of sentences) {
3561
+ const type = inferCardType(sentence, row.tool_name);
3562
+ const subject = extractSubject(sentence, row.agent_id);
3563
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
3564
+ cards.push({
3565
+ id: stableId(row.id, type, content),
3566
+ memory_id: row.id,
3567
+ agent_id: row.agent_id,
3568
+ session_id: row.session_id,
3569
+ project_name: row.project_name ?? null,
3570
+ timestamp: row.timestamp,
3571
+ card_type: type,
3572
+ subject,
3573
+ predicate: predicateFor(type),
3574
+ object: content,
3575
+ content,
3576
+ source_ref: row.id,
3577
+ confidence: type === "fact" ? 0.55 : 0.65
3578
+ });
3579
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
3580
+ }
3581
+ return cards;
3582
+ }
3583
+ async function insertMemoryCardsForBatch(rows) {
3584
+ const cards = rows.flatMap(extractMemoryCards);
3585
+ if (cards.length === 0) return 0;
3586
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3587
+ const client = getClient();
3588
+ const stmts = cards.map((card) => ({
3589
+ sql: `INSERT OR IGNORE INTO memory_cards
3590
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
3591
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
3592
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
3593
+ args: [
3594
+ card.id,
3595
+ card.memory_id,
3596
+ card.agent_id,
3597
+ card.session_id,
3598
+ card.project_name,
3599
+ card.timestamp,
3600
+ card.card_type,
3601
+ card.subject,
3602
+ card.predicate,
3603
+ card.object,
3604
+ card.content,
3605
+ card.source_ref,
3606
+ card.confidence,
3607
+ now
3608
+ ]
3609
+ }));
3610
+ await client.batch(stmts, "write");
3611
+ return cards.length;
3612
+ }
3613
+ function buildMatchExpr(queryText) {
3614
+ 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);
3615
+ if (terms.length === 0) return null;
3616
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
3617
+ }
3618
+ async function searchMemoryCards(queryText, agentId, options) {
3619
+ const limit = options?.limit ?? 10;
3620
+ const matchExpr = buildMatchExpr(queryText);
3621
+ if (!matchExpr) return [];
3622
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
3623
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
3624
+ FROM memory_cards c
3625
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
3626
+ WHERE memory_cards_fts MATCH ?
3627
+ AND c.agent_id = ?
3628
+ AND COALESCE(c.active, 1) = 1`;
3629
+ const args = [matchExpr, agentId];
3630
+ if (options?.projectName) {
3631
+ sql += ` AND c.project_name = ?`;
3632
+ args.push(options.projectName);
3633
+ }
3634
+ if (options?.since) {
3635
+ sql += ` AND c.timestamp >= ?`;
3636
+ args.push(options.since);
3637
+ }
3638
+ sql += ` ORDER BY rank LIMIT ?`;
3639
+ args.push(limit);
3640
+ const result = await getClient().execute({ sql, args });
3641
+ return result.rows.map((row) => ({
3642
+ id: `card:${String(row.id)}`,
3643
+ agent_id: String(row.agent_id),
3644
+ agent_role: "memory_card",
3645
+ session_id: String(row.session_id),
3646
+ timestamp: String(row.timestamp),
3647
+ tool_name: `memory_card:${String(row.card_type)}`,
3648
+ project_name: row.project_name == null ? "" : String(row.project_name),
3649
+ has_error: false,
3650
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
3651
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
3652
+ vector: [],
3653
+ importance: 6,
3654
+ status: "active",
3655
+ confidence: Number(row.confidence ?? 0.6),
3656
+ last_accessed: String(row.timestamp)
3657
+ }));
3658
+ }
3659
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
3660
+ var init_memory_cards = __esm({
3661
+ "src/lib/memory-cards.ts"() {
3662
+ "use strict";
3663
+ init_database();
3664
+ MAX_CARDS_PER_MEMORY = 6;
3665
+ MAX_SENTENCE_CHARS = 360;
3666
+ }
3667
+ });
3668
+
3466
3669
  // src/bin/backfill-responses.ts
3467
3670
  import crypto2 from "crypto";
3468
3671
  import { createReadStream } from "fs";
@@ -4261,6 +4464,11 @@ async function flushBatch() {
4261
4464
  const globalClient = getClient();
4262
4465
  const globalStmts = batch.map(buildStmt);
4263
4466
  await globalClient.batch(globalStmts, "write");
4467
+ try {
4468
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4469
+ await insertMemoryCardsForBatch2(batch);
4470
+ } catch {
4471
+ }
4264
4472
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4265
4473
  _pendingRecords.splice(0, batch.length);
4266
4474
  try {
@@ -256,8 +256,8 @@ var init_config = __esm({
256
256
  rerankerAutoTrigger: {
257
257
  enabled: true,
258
258
  broadQueryMinCardinality: 5e4,
259
- fetchTopK: 150,
260
- returnTopK: 5
259
+ fetchTopK: 200,
260
+ returnTopK: 20
261
261
  }
262
262
  },
263
263
  graphRagEnabled: true,
@@ -2642,6 +2642,51 @@ async function ensureSchema() {
2642
2642
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2643
2643
  END;
2644
2644
  `);
2645
+ await client.executeMultiple(`
2646
+ CREATE TABLE IF NOT EXISTS memory_cards (
2647
+ id TEXT PRIMARY KEY,
2648
+ memory_id TEXT NOT NULL,
2649
+ agent_id TEXT NOT NULL,
2650
+ session_id TEXT NOT NULL,
2651
+ project_name TEXT,
2652
+ timestamp TEXT NOT NULL,
2653
+ card_type TEXT NOT NULL,
2654
+ subject TEXT,
2655
+ predicate TEXT,
2656
+ object TEXT,
2657
+ content TEXT NOT NULL,
2658
+ source_ref TEXT,
2659
+ confidence REAL DEFAULT 0.6,
2660
+ active INTEGER DEFAULT 1,
2661
+ created_at TEXT NOT NULL
2662
+ );
2663
+
2664
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2665
+ ON memory_cards(agent_id, active, timestamp);
2666
+
2667
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2668
+ ON memory_cards(memory_id);
2669
+
2670
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2671
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2672
+
2673
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2674
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2675
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2676
+ END;
2677
+
2678
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2679
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2680
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2681
+ END;
2682
+
2683
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2684
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2685
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2686
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2687
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2688
+ END;
2689
+ `);
2645
2690
  try {
2646
2691
  await client.execute({
2647
2692
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,