@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
|
@@ -322,8 +322,8 @@ var init_config = __esm({
|
|
|
322
322
|
rerankerAutoTrigger: {
|
|
323
323
|
enabled: true,
|
|
324
324
|
broadQueryMinCardinality: 5e4,
|
|
325
|
-
fetchTopK:
|
|
326
|
-
returnTopK:
|
|
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 {
|
package/dist/bin/exe-forget.js
CHANGED
|
@@ -247,8 +247,8 @@ var init_config = __esm({
|
|
|
247
247
|
rerankerAutoTrigger: {
|
|
248
248
|
enabled: true,
|
|
249
249
|
broadQueryMinCardinality: 5e4,
|
|
250
|
-
fetchTopK:
|
|
251
|
-
returnTopK:
|
|
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) {
|