@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.
- package/deploy/stack-manifests/v0.9.json +62 -13
- package/dist/bin/backfill-conversations.js +210 -2
- package/dist/bin/backfill-responses.js +210 -2
- package/dist/bin/backfill-vectors.js +47 -2
- package/dist/bin/cleanup-stale-review-tasks.js +210 -2
- package/dist/bin/cli.js +488 -25
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +2 -2
- package/dist/bin/exe-assign.js +210 -2
- package/dist/bin/exe-boot.js +47 -2
- package/dist/bin/exe-call.js +2 -2
- package/dist/bin/exe-cloud.js +2 -2
- package/dist/bin/exe-dispatch.js +210 -2
- package/dist/bin/exe-doctor.js +2691 -1212
- package/dist/bin/exe-export-behaviors.js +210 -2
- package/dist/bin/exe-forget.js +264 -2
- package/dist/bin/exe-gateway.js +210 -2
- package/dist/bin/exe-heartbeat.js +212 -4
- package/dist/bin/exe-kill.js +210 -2
- package/dist/bin/exe-launch-agent.js +210 -2
- package/dist/bin/exe-link.js +47 -2
- package/dist/bin/exe-new-employee.js +113 -3
- package/dist/bin/exe-pending-messages.js +210 -2
- package/dist/bin/exe-pending-notifications.js +210 -2
- package/dist/bin/exe-pending-reviews.js +210 -2
- package/dist/bin/exe-rename.js +210 -2
- package/dist/bin/exe-review.js +210 -2
- package/dist/bin/exe-search.js +234 -3
- package/dist/bin/exe-session-cleanup.js +210 -2
- package/dist/bin/exe-settings.js +2 -2
- package/dist/bin/exe-start-codex.js +322 -4
- package/dist/bin/exe-start-opencode.js +336 -4
- package/dist/bin/exe-status.js +210 -2
- package/dist/bin/exe-team.js +210 -2
- package/dist/bin/git-sweep.js +210 -2
- package/dist/bin/graph-backfill.js +210 -2
- package/dist/bin/graph-export.js +210 -2
- package/dist/bin/install.js +113 -3
- package/dist/bin/intercom-check.js +210 -2
- package/dist/bin/scan-tasks.js +210 -2
- package/dist/bin/setup.js +47 -2
- package/dist/bin/shard-migrate.js +210 -2
- package/dist/bin/stack-update.js +158 -13
- package/dist/bin/update.js +2 -2
- package/dist/gateway/index.js +210 -2
- package/dist/hooks/bug-report-worker.js +321 -44
- package/dist/hooks/codex-stop-task-finalizer.js +210 -2
- package/dist/hooks/commit-complete.js +210 -2
- package/dist/hooks/error-recall.js +234 -3
- package/dist/hooks/exe-heartbeat-hook.js +2 -2
- package/dist/hooks/ingest-worker.js +2 -2
- package/dist/hooks/ingest.js +210 -2
- package/dist/hooks/instructions-loaded.js +210 -2
- package/dist/hooks/notification.js +210 -2
- package/dist/hooks/post-compact.js +210 -2
- package/dist/hooks/post-tool-combined.js +234 -3
- package/dist/hooks/pre-compact.js +446 -111
- package/dist/hooks/pre-tool-use.js +210 -2
- package/dist/hooks/prompt-submit.js +234 -3
- package/dist/hooks/session-end.js +347 -124
- package/dist/hooks/session-start.js +236 -5
- package/dist/hooks/stop.js +210 -2
- package/dist/hooks/subagent-stop.js +210 -2
- package/dist/hooks/summary-worker.js +171 -74
- package/dist/index.js +210 -2
- package/dist/lib/agent-config.js +2 -2
- package/dist/lib/cloud-sync.js +47 -2
- package/dist/lib/config.js +2 -2
- package/dist/lib/consolidation.js +2 -2
- package/dist/lib/database.js +47 -2
- package/dist/lib/db-daemon-client.js +2 -2
- package/dist/lib/db.js +47 -2
- package/dist/lib/device-registry.js +47 -2
- package/dist/lib/embedder.js +2 -2
- package/dist/lib/employee-templates.js +2 -2
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon-client.js +2 -2
- package/dist/lib/exe-daemon.js +592 -10
- package/dist/lib/hybrid-search.js +234 -3
- package/dist/lib/identity.js +2 -2
- package/dist/lib/license.js +2 -2
- package/dist/lib/messaging.js +2 -2
- package/dist/lib/reminders.js +2 -2
- package/dist/lib/schedules.js +47 -2
- package/dist/lib/skill-learning.js +2 -2
- package/dist/lib/store.js +210 -2
- package/dist/lib/task-router.js +2 -2
- package/dist/lib/tasks.js +2 -2
- package/dist/lib/tmux-routing.js +2 -2
- package/dist/lib/token-spend.js +2 -2
- package/dist/mcp/server.js +608 -10
- package/dist/mcp/tools/complete-reminder.js +2 -2
- package/dist/mcp/tools/create-reminder.js +2 -2
- package/dist/mcp/tools/create-task.js +2 -2
- package/dist/mcp/tools/deactivate-behavior.js +2 -2
- package/dist/mcp/tools/list-reminders.js +2 -2
- package/dist/mcp/tools/list-tasks.js +2 -2
- package/dist/mcp/tools/send-message.js +2 -2
- package/dist/mcp/tools/update-task.js +2 -2
- package/dist/runtime/index.js +210 -2
- package/dist/tui/App.js +210 -2
- package/package.json +3 -2
- 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:
|
|
206
|
-
returnTopK:
|
|
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:
|
|
250
|
-
returnTopK:
|
|
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
|
|
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) {
|