@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
@@ -322,8 +322,8 @@ var init_config = __esm({
322
322
  rerankerAutoTrigger: {
323
323
  enabled: true,
324
324
  broadQueryMinCardinality: 5e4,
325
- fetchTopK: 150,
326
- returnTopK: 5
325
+ fetchTopK: 200,
326
+ returnTopK: 20
327
327
  }
328
328
  },
329
329
  graphRagEnabled: true,
@@ -2787,6 +2787,51 @@ async function ensureSchema() {
2787
2787
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2788
2788
  END;
2789
2789
  `);
2790
+ await client.executeMultiple(`
2791
+ CREATE TABLE IF NOT EXISTS memory_cards (
2792
+ id TEXT PRIMARY KEY,
2793
+ memory_id TEXT NOT NULL,
2794
+ agent_id TEXT NOT NULL,
2795
+ session_id TEXT NOT NULL,
2796
+ project_name TEXT,
2797
+ timestamp TEXT NOT NULL,
2798
+ card_type TEXT NOT NULL,
2799
+ subject TEXT,
2800
+ predicate TEXT,
2801
+ object TEXT,
2802
+ content TEXT NOT NULL,
2803
+ source_ref TEXT,
2804
+ confidence REAL DEFAULT 0.6,
2805
+ active INTEGER DEFAULT 1,
2806
+ created_at TEXT NOT NULL
2807
+ );
2808
+
2809
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2810
+ ON memory_cards(agent_id, active, timestamp);
2811
+
2812
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2813
+ ON memory_cards(memory_id);
2814
+
2815
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2816
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2817
+
2818
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2819
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2820
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2821
+ END;
2822
+
2823
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2824
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2825
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2826
+ END;
2827
+
2828
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2829
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2830
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2831
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2832
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2833
+ END;
2834
+ `);
2790
2835
  try {
2791
2836
  await client.execute({
2792
2837
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4076,6 +4121,164 @@ ${p.content}`).join("\n\n");
4076
4121
  }
4077
4122
  });
4078
4123
 
4124
+ // src/lib/memory-cards.ts
4125
+ var memory_cards_exports = {};
4126
+ __export(memory_cards_exports, {
4127
+ extractMemoryCards: () => extractMemoryCards,
4128
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4129
+ searchMemoryCards: () => searchMemoryCards
4130
+ });
4131
+ import { createHash as createHash2 } from "crypto";
4132
+ function stableId(memoryId, type, content) {
4133
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4134
+ }
4135
+ function cleanText(text) {
4136
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4137
+ }
4138
+ function splitSentences(text) {
4139
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4140
+ }
4141
+ function inferCardType(sentence, toolName) {
4142
+ const lower = sentence.toLowerCase();
4143
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4144
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4145
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4146
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4147
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4148
+ return "fact";
4149
+ }
4150
+ function extractSubject(sentence, agentId2) {
4151
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4152
+ return explicit?.[1] ?? agentId2;
4153
+ }
4154
+ function predicateFor(type) {
4155
+ switch (type) {
4156
+ case "preference":
4157
+ return "prefers";
4158
+ case "belief_update":
4159
+ return "updated";
4160
+ case "decision":
4161
+ return "decided";
4162
+ case "event":
4163
+ return "happened";
4164
+ case "code":
4165
+ return "implemented";
4166
+ default:
4167
+ return "states";
4168
+ }
4169
+ }
4170
+ function extractMemoryCards(row) {
4171
+ const sentences = splitSentences(row.raw_text);
4172
+ const cards = [];
4173
+ for (const sentence of sentences) {
4174
+ const type = inferCardType(sentence, row.tool_name);
4175
+ const subject = extractSubject(sentence, row.agent_id);
4176
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4177
+ cards.push({
4178
+ id: stableId(row.id, type, content),
4179
+ memory_id: row.id,
4180
+ agent_id: row.agent_id,
4181
+ session_id: row.session_id,
4182
+ project_name: row.project_name ?? null,
4183
+ timestamp: row.timestamp,
4184
+ card_type: type,
4185
+ subject,
4186
+ predicate: predicateFor(type),
4187
+ object: content,
4188
+ content,
4189
+ source_ref: row.id,
4190
+ confidence: type === "fact" ? 0.55 : 0.65
4191
+ });
4192
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4193
+ }
4194
+ return cards;
4195
+ }
4196
+ async function insertMemoryCardsForBatch(rows) {
4197
+ const cards = rows.flatMap(extractMemoryCards);
4198
+ if (cards.length === 0) return 0;
4199
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4200
+ const client = getClient();
4201
+ const stmts = cards.map((card) => ({
4202
+ sql: `INSERT OR IGNORE INTO memory_cards
4203
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4204
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4205
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4206
+ args: [
4207
+ card.id,
4208
+ card.memory_id,
4209
+ card.agent_id,
4210
+ card.session_id,
4211
+ card.project_name,
4212
+ card.timestamp,
4213
+ card.card_type,
4214
+ card.subject,
4215
+ card.predicate,
4216
+ card.object,
4217
+ card.content,
4218
+ card.source_ref,
4219
+ card.confidence,
4220
+ now
4221
+ ]
4222
+ }));
4223
+ await client.batch(stmts, "write");
4224
+ return cards.length;
4225
+ }
4226
+ function buildMatchExpr(queryText) {
4227
+ 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);
4228
+ if (terms.length === 0) return null;
4229
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4230
+ }
4231
+ async function searchMemoryCards(queryText, agentId2, options) {
4232
+ const limit = options?.limit ?? 10;
4233
+ const matchExpr = buildMatchExpr(queryText);
4234
+ if (!matchExpr) return [];
4235
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4236
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4237
+ FROM memory_cards c
4238
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4239
+ WHERE memory_cards_fts MATCH ?
4240
+ AND c.agent_id = ?
4241
+ AND COALESCE(c.active, 1) = 1`;
4242
+ const args = [matchExpr, agentId2];
4243
+ if (options?.projectName) {
4244
+ sql += ` AND c.project_name = ?`;
4245
+ args.push(options.projectName);
4246
+ }
4247
+ if (options?.since) {
4248
+ sql += ` AND c.timestamp >= ?`;
4249
+ args.push(options.since);
4250
+ }
4251
+ sql += ` ORDER BY rank LIMIT ?`;
4252
+ args.push(limit);
4253
+ const result = await getClient().execute({ sql, args });
4254
+ return result.rows.map((row) => ({
4255
+ id: `card:${String(row.id)}`,
4256
+ agent_id: String(row.agent_id),
4257
+ agent_role: "memory_card",
4258
+ session_id: String(row.session_id),
4259
+ timestamp: String(row.timestamp),
4260
+ tool_name: `memory_card:${String(row.card_type)}`,
4261
+ project_name: row.project_name == null ? "" : String(row.project_name),
4262
+ has_error: false,
4263
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4264
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4265
+ vector: [],
4266
+ importance: 6,
4267
+ status: "active",
4268
+ confidence: Number(row.confidence ?? 0.6),
4269
+ last_accessed: String(row.timestamp)
4270
+ }));
4271
+ }
4272
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4273
+ var init_memory_cards = __esm({
4274
+ "src/lib/memory-cards.ts"() {
4275
+ "use strict";
4276
+ init_database();
4277
+ MAX_CARDS_PER_MEMORY = 6;
4278
+ MAX_SENTENCE_CHARS = 360;
4279
+ }
4280
+ });
4281
+
4079
4282
  // src/lib/store.ts
4080
4283
  var store_exports = {};
4081
4284
  __export(store_exports, {
@@ -4414,6 +4617,11 @@ async function flushBatch() {
4414
4617
  const globalClient = getClient();
4415
4618
  const globalStmts = batch.map(buildStmt);
4416
4619
  await globalClient.batch(globalStmts, "write");
4620
+ try {
4621
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4622
+ await insertMemoryCardsForBatch2(batch);
4623
+ } catch {
4624
+ }
4417
4625
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4418
4626
  _pendingRecords.splice(0, batch.length);
4419
4627
  try {
@@ -247,8 +247,8 @@ var init_config = __esm({
247
247
  rerankerAutoTrigger: {
248
248
  enabled: true,
249
249
  broadQueryMinCardinality: 5e4,
250
- fetchTopK: 150,
251
- returnTopK: 5
250
+ fetchTopK: 200,
251
+ returnTopK: 20
252
252
  }
253
253
  },
254
254
  graphRagEnabled: true,
@@ -2721,6 +2721,51 @@ async function ensureSchema() {
2721
2721
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
2722
2722
  END;
2723
2723
  `);
2724
+ await client.executeMultiple(`
2725
+ CREATE TABLE IF NOT EXISTS memory_cards (
2726
+ id TEXT PRIMARY KEY,
2727
+ memory_id TEXT NOT NULL,
2728
+ agent_id TEXT NOT NULL,
2729
+ session_id TEXT NOT NULL,
2730
+ project_name TEXT,
2731
+ timestamp TEXT NOT NULL,
2732
+ card_type TEXT NOT NULL,
2733
+ subject TEXT,
2734
+ predicate TEXT,
2735
+ object TEXT,
2736
+ content TEXT NOT NULL,
2737
+ source_ref TEXT,
2738
+ confidence REAL DEFAULT 0.6,
2739
+ active INTEGER DEFAULT 1,
2740
+ created_at TEXT NOT NULL
2741
+ );
2742
+
2743
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_agent
2744
+ ON memory_cards(agent_id, active, timestamp);
2745
+
2746
+ CREATE INDEX IF NOT EXISTS idx_memory_cards_memory
2747
+ ON memory_cards(memory_id);
2748
+
2749
+ CREATE VIRTUAL TABLE IF NOT EXISTS memory_cards_fts
2750
+ USING fts5(content, subject, predicate, object, content='memory_cards', content_rowid='rowid');
2751
+
2752
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ai AFTER INSERT ON memory_cards BEGIN
2753
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2754
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2755
+ END;
2756
+
2757
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_ad AFTER DELETE ON memory_cards BEGIN
2758
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2759
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2760
+ END;
2761
+
2762
+ CREATE TRIGGER IF NOT EXISTS memory_cards_fts_au AFTER UPDATE ON memory_cards BEGIN
2763
+ INSERT INTO memory_cards_fts(memory_cards_fts, rowid, content, subject, predicate, object)
2764
+ VALUES('delete', old.rowid, old.content, old.subject, old.predicate, old.object);
2765
+ INSERT INTO memory_cards_fts(rowid, content, subject, predicate, object)
2766
+ VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
2767
+ END;
2768
+ `);
2724
2769
  try {
2725
2770
  await client.execute({
2726
2771
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
@@ -4010,6 +4055,164 @@ ${p.content}`).join("\n\n");
4010
4055
  }
4011
4056
  });
4012
4057
 
4058
+ // src/lib/memory-cards.ts
4059
+ var memory_cards_exports = {};
4060
+ __export(memory_cards_exports, {
4061
+ extractMemoryCards: () => extractMemoryCards,
4062
+ insertMemoryCardsForBatch: () => insertMemoryCardsForBatch,
4063
+ searchMemoryCards: () => searchMemoryCards
4064
+ });
4065
+ import { createHash as createHash2 } from "crypto";
4066
+ function stableId(memoryId, type, content) {
4067
+ return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
4068
+ }
4069
+ function cleanText(text) {
4070
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
4071
+ }
4072
+ function splitSentences(text) {
4073
+ return cleanText(text).split(/(?<=[.!?])\s+|\n+/).map((s) => s.trim()).filter((s) => s.length >= 24 && s.length <= MAX_SENTENCE_CHARS);
4074
+ }
4075
+ function inferCardType(sentence, toolName) {
4076
+ const lower = sentence.toLowerCase();
4077
+ if (toolName === "store_decision" || /\b(decided|decision|adr|approved|rejected)\b/.test(lower)) return "decision";
4078
+ if (/\b(prefers|preference|likes|dislikes|wants|doesn't want|does not want)\b/.test(lower)) return "preference";
4079
+ if (/\b(changed|updated|replaced|now|no longer|instead|supersedes)\b/.test(lower)) return "belief_update";
4080
+ if (toolName && ["Read", "Write", "Edit", "Bash"].includes(toolName)) return "code";
4081
+ if (/\b(meeting|deadline|shipped|launched|completed|failed|blocked|assigned|created)\b/.test(lower)) return "event";
4082
+ return "fact";
4083
+ }
4084
+ function extractSubject(sentence, agentId) {
4085
+ const explicit = sentence.match(/\b([A-Z][a-zA-Z0-9_-]{2,}(?:\s+[A-Z][a-zA-Z0-9_-]{2,})?)\b/);
4086
+ return explicit?.[1] ?? agentId;
4087
+ }
4088
+ function predicateFor(type) {
4089
+ switch (type) {
4090
+ case "preference":
4091
+ return "prefers";
4092
+ case "belief_update":
4093
+ return "updated";
4094
+ case "decision":
4095
+ return "decided";
4096
+ case "event":
4097
+ return "happened";
4098
+ case "code":
4099
+ return "implemented";
4100
+ default:
4101
+ return "states";
4102
+ }
4103
+ }
4104
+ function extractMemoryCards(row) {
4105
+ const sentences = splitSentences(row.raw_text);
4106
+ const cards = [];
4107
+ for (const sentence of sentences) {
4108
+ const type = inferCardType(sentence, row.tool_name);
4109
+ const subject = extractSubject(sentence, row.agent_id);
4110
+ const content = sentence.length > MAX_SENTENCE_CHARS ? `${sentence.slice(0, MAX_SENTENCE_CHARS - 1)}\u2026` : sentence;
4111
+ cards.push({
4112
+ id: stableId(row.id, type, content),
4113
+ memory_id: row.id,
4114
+ agent_id: row.agent_id,
4115
+ session_id: row.session_id,
4116
+ project_name: row.project_name ?? null,
4117
+ timestamp: row.timestamp,
4118
+ card_type: type,
4119
+ subject,
4120
+ predicate: predicateFor(type),
4121
+ object: content,
4122
+ content,
4123
+ source_ref: row.id,
4124
+ confidence: type === "fact" ? 0.55 : 0.65
4125
+ });
4126
+ if (cards.length >= MAX_CARDS_PER_MEMORY) break;
4127
+ }
4128
+ return cards;
4129
+ }
4130
+ async function insertMemoryCardsForBatch(rows) {
4131
+ const cards = rows.flatMap(extractMemoryCards);
4132
+ if (cards.length === 0) return 0;
4133
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4134
+ const client = getClient();
4135
+ const stmts = cards.map((card) => ({
4136
+ sql: `INSERT OR IGNORE INTO memory_cards
4137
+ (id, memory_id, agent_id, session_id, project_name, timestamp, card_type,
4138
+ subject, predicate, object, content, source_ref, confidence, active, created_at)
4139
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`,
4140
+ args: [
4141
+ card.id,
4142
+ card.memory_id,
4143
+ card.agent_id,
4144
+ card.session_id,
4145
+ card.project_name,
4146
+ card.timestamp,
4147
+ card.card_type,
4148
+ card.subject,
4149
+ card.predicate,
4150
+ card.object,
4151
+ card.content,
4152
+ card.source_ref,
4153
+ card.confidence,
4154
+ now
4155
+ ]
4156
+ }));
4157
+ await client.batch(stmts, "write");
4158
+ return cards.length;
4159
+ }
4160
+ function buildMatchExpr(queryText) {
4161
+ 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);
4162
+ if (terms.length === 0) return null;
4163
+ return terms.map((t) => `${t}*`).join(terms.length >= 3 ? " AND " : " OR ");
4164
+ }
4165
+ async function searchMemoryCards(queryText, agentId, options) {
4166
+ const limit = options?.limit ?? 10;
4167
+ const matchExpr = buildMatchExpr(queryText);
4168
+ if (!matchExpr) return [];
4169
+ let sql = `SELECT c.id, c.memory_id, c.agent_id, c.session_id, c.project_name,
4170
+ c.timestamp, c.card_type, c.content, c.source_ref, c.confidence
4171
+ FROM memory_cards c
4172
+ JOIN memory_cards_fts fts ON c.rowid = fts.rowid
4173
+ WHERE memory_cards_fts MATCH ?
4174
+ AND c.agent_id = ?
4175
+ AND COALESCE(c.active, 1) = 1`;
4176
+ const args = [matchExpr, agentId];
4177
+ if (options?.projectName) {
4178
+ sql += ` AND c.project_name = ?`;
4179
+ args.push(options.projectName);
4180
+ }
4181
+ if (options?.since) {
4182
+ sql += ` AND c.timestamp >= ?`;
4183
+ args.push(options.since);
4184
+ }
4185
+ sql += ` ORDER BY rank LIMIT ?`;
4186
+ args.push(limit);
4187
+ const result = await getClient().execute({ sql, args });
4188
+ return result.rows.map((row) => ({
4189
+ id: `card:${String(row.id)}`,
4190
+ agent_id: String(row.agent_id),
4191
+ agent_role: "memory_card",
4192
+ session_id: String(row.session_id),
4193
+ timestamp: String(row.timestamp),
4194
+ tool_name: `memory_card:${String(row.card_type)}`,
4195
+ project_name: row.project_name == null ? "" : String(row.project_name),
4196
+ has_error: false,
4197
+ raw_text: `[${String(row.card_type)}] ${String(row.content)}
4198
+ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
4199
+ vector: [],
4200
+ importance: 6,
4201
+ status: "active",
4202
+ confidence: Number(row.confidence ?? 0.6),
4203
+ last_accessed: String(row.timestamp)
4204
+ }));
4205
+ }
4206
+ var MAX_CARDS_PER_MEMORY, MAX_SENTENCE_CHARS;
4207
+ var init_memory_cards = __esm({
4208
+ "src/lib/memory-cards.ts"() {
4209
+ "use strict";
4210
+ init_database();
4211
+ MAX_CARDS_PER_MEMORY = 6;
4212
+ MAX_SENTENCE_CHARS = 360;
4213
+ }
4214
+ });
4215
+
4013
4216
  // src/lib/store.ts
4014
4217
  var store_exports = {};
4015
4218
  __export(store_exports, {
@@ -4348,6 +4551,11 @@ async function flushBatch() {
4348
4551
  const globalClient = getClient();
4349
4552
  const globalStmts = batch.map(buildStmt);
4350
4553
  await globalClient.batch(globalStmts, "write");
4554
+ try {
4555
+ const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4556
+ await insertMemoryCardsForBatch2(batch);
4557
+ } catch {
4558
+ }
4351
4559
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4352
4560
  _pendingRecords.splice(0, batch.length);
4353
4561
  try {
@@ -4695,6 +4903,7 @@ init_database();
4695
4903
  // src/lib/hybrid-search.ts
4696
4904
  init_store();
4697
4905
  init_database();
4906
+ var RRF_K = 60;
4698
4907
  function appendMemoryTypeFilter(sql, args, column, options) {
4699
4908
  if (options?.memoryTypes && options.memoryTypes.length > 0) {
4700
4909
  const uniqueTypes = [...new Set(options.memoryTypes)];
@@ -4706,6 +4915,50 @@ function appendMemoryTypeFilter(sql, args, column, options) {
4706
4915
  }
4707
4916
  return sql;
4708
4917
  }
4918
+ function recencyScore(timestamp) {
4919
+ const daysSince = (Date.now() - new Date(timestamp).getTime()) / (1e3 * 60 * 60 * 24);
4920
+ return 1 / (1 + daysSince * 0.01);
4921
+ }
4922
+ function normalizedImportance(importance) {
4923
+ return ((importance ?? 5) - 1) / 9;
4924
+ }
4925
+ function frecencyBoost(lastAccessed, timestamp) {
4926
+ const accessTime = lastAccessed ? new Date(lastAccessed).getTime() : new Date(timestamp).getTime();
4927
+ const hoursSince = Math.max(0, (Date.now() - accessTime) / (1e3 * 60 * 60));
4928
+ return Math.exp(-0.01 * hoursSince);
4929
+ }
4930
+ function sourceConfidence(record) {
4931
+ const tool = record.tool_name?.toLowerCase() ?? "";
4932
+ if (tool === "manual" || tool === "store_memory") return 0.95;
4933
+ if (tool === "auto-summary" || tool === "consolidation") return 0.9;
4934
+ if (tool === "userprompt" || tool === "assistantresponse") return 0.75;
4935
+ if (tool === "bash" || tool === "write" || tool === "edit") return 0.6;
4936
+ if (tool === "file_grep" || tool === "file_read") return 0.5;
4937
+ return record.confidence ?? 0.5;
4938
+ }
4939
+ function rrfMergeMulti(lists, limit, k = RRF_K, weights) {
4940
+ const scores = /* @__PURE__ */ new Map();
4941
+ for (let listIdx = 0; listIdx < lists.length; listIdx++) {
4942
+ const list = lists[listIdx];
4943
+ const weight = weights?.[listIdx] ?? 1;
4944
+ for (let i = 0; i < list.length; i++) {
4945
+ const rec = list[i];
4946
+ const entry = scores.get(rec.id) ?? { rrfScore: 0, record: rec };
4947
+ entry.rrfScore += weight * (1 / (k + i + 1));
4948
+ scores.set(rec.id, entry);
4949
+ }
4950
+ }
4951
+ const entries = Array.from(scores.values()).map((e) => {
4952
+ const recency = recencyScore(e.record.timestamp);
4953
+ const importance = normalizedImportance(e.record.importance);
4954
+ const confidence = sourceConfidence(e.record);
4955
+ const frecency = frecencyBoost(e.record.last_accessed, e.record.timestamp);
4956
+ const baseScore = e.rrfScore * 0.45 + recency * 0.24 + importance * 0.21 + confidence * 0.1;
4957
+ const finalScore = baseScore * (1 + 0.3 * frecency);
4958
+ return { score: finalScore, record: e.record };
4959
+ });
4960
+ return entries.sort((a, b) => b.score - a.score).slice(0, limit).map((e) => e.record);
4961
+ }
4709
4962
  async function lightweightSearch(queryText, agentId, options) {
4710
4963
  const client = getClient();
4711
4964
  const limit = options?.limit ?? 5;
@@ -4731,6 +4984,15 @@ async function lightweightSearch(queryText, agentId, options) {
4731
4984
  if (options?.includeSource && results.length > 0) {
4732
4985
  await attachDocumentMetadata(results);
4733
4986
  }
4987
+ if (options?.includeStructuredCards !== true) return results;
4988
+ try {
4989
+ const { searchMemoryCards: searchMemoryCards2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
4990
+ const cardResults = await searchMemoryCards2(queryText, agentId, options);
4991
+ if (cardResults.length > 0) {
4992
+ return rrfMergeMulti([results, cardResults], limit, RRF_K, [1, 1.2]);
4993
+ }
4994
+ } catch {
4995
+ }
4734
4996
  return results;
4735
4997
  }
4736
4998
  async function ftsQuery(client, matchExpr, agentId, options, limit) {