@askexenow/exe-os 0.9.34 → 0.9.35
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/dist/bin/backfill-conversations.js +210 -10
- package/dist/bin/backfill-responses.js +210 -10
- package/dist/bin/backfill-vectors.js +13 -2
- package/dist/bin/cleanup-stale-review-tasks.js +217 -10
- package/dist/bin/cli.js +222 -11
- package/dist/bin/exe-assign.js +210 -10
- package/dist/bin/exe-boot.js +24 -3
- package/dist/bin/exe-dispatch.js +222 -11
- package/dist/bin/exe-doctor.js +13 -2
- package/dist/bin/exe-export-behaviors.js +217 -10
- package/dist/bin/exe-forget.js +217 -10
- package/dist/bin/exe-gateway.js +222 -11
- package/dist/bin/exe-heartbeat.js +217 -10
- package/dist/bin/exe-kill.js +217 -10
- package/dist/bin/exe-launch-agent.js +92 -2
- package/dist/bin/exe-link.js +8 -0
- package/dist/bin/exe-pending-messages.js +217 -10
- package/dist/bin/exe-pending-notifications.js +217 -10
- package/dist/bin/exe-pending-reviews.js +217 -10
- package/dist/bin/exe-rename.js +8 -0
- package/dist/bin/exe-review.js +217 -10
- package/dist/bin/exe-search.js +217 -10
- package/dist/bin/exe-session-cleanup.js +222 -11
- package/dist/bin/exe-start-codex.js +92 -2
- package/dist/bin/exe-start-opencode.js +92 -2
- package/dist/bin/exe-status.js +217 -10
- package/dist/bin/exe-team.js +217 -10
- package/dist/bin/git-sweep.js +222 -11
- package/dist/bin/graph-backfill.js +92 -2
- package/dist/bin/graph-export.js +217 -10
- package/dist/bin/intercom-check.js +222 -11
- package/dist/bin/scan-tasks.js +222 -11
- package/dist/bin/setup.js +8 -0
- package/dist/bin/shard-migrate.js +92 -2
- package/dist/gateway/index.js +222 -11
- package/dist/hooks/bug-report-worker.js +222 -11
- package/dist/hooks/codex-stop-task-finalizer.js +217 -10
- package/dist/hooks/commit-complete.js +222 -11
- package/dist/hooks/error-recall.js +217 -10
- package/dist/hooks/ingest.js +217 -10
- package/dist/hooks/instructions-loaded.js +217 -10
- package/dist/hooks/notification.js +217 -10
- package/dist/hooks/post-compact.js +217 -10
- package/dist/hooks/post-tool-combined.js +217 -10
- package/dist/hooks/pre-compact.js +222 -11
- package/dist/hooks/pre-tool-use.js +217 -10
- package/dist/hooks/prompt-submit.js +222 -11
- package/dist/hooks/session-end.js +222 -11
- package/dist/hooks/session-start.js +217 -10
- package/dist/hooks/stop.js +217 -10
- package/dist/hooks/subagent-stop.js +217 -10
- package/dist/hooks/summary-worker.js +14 -1
- package/dist/index.js +222 -11
- package/dist/lib/cloud-sync.js +8 -0
- package/dist/lib/consolidation.js +3 -1
- package/dist/lib/database.js +8 -0
- package/dist/lib/db.js +8 -0
- package/dist/lib/device-registry.js +8 -0
- package/dist/lib/exe-daemon.js +1667 -1413
- package/dist/lib/hybrid-search.js +217 -10
- package/dist/lib/schedules.js +13 -2
- package/dist/lib/store.js +210 -10
- package/dist/lib/tasks.js +5 -1
- package/dist/lib/tmux-routing.js +5 -1
- package/dist/mcp/server.js +222 -11
- package/dist/mcp/tools/create-task.js +5 -1
- package/dist/mcp/tools/update-task.js +5 -1
- package/dist/runtime/index.js +222 -11
- package/dist/tui/App.js +222 -11
- package/package.json +1 -1
|
@@ -2298,6 +2298,14 @@ async function ensureSchema() {
|
|
|
2298
2298
|
);
|
|
2299
2299
|
} catch {
|
|
2300
2300
|
}
|
|
2301
|
+
try {
|
|
2302
|
+
await client.execute(
|
|
2303
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2304
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2305
|
+
WHERE content_hash IS NOT NULL`
|
|
2306
|
+
);
|
|
2307
|
+
} catch {
|
|
2308
|
+
}
|
|
2301
2309
|
await client.executeMultiple(`
|
|
2302
2310
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2303
2311
|
id TEXT PRIMARY KEY,
|
|
@@ -3452,6 +3460,196 @@ var init_state_bus = __esm({
|
|
|
3452
3460
|
}
|
|
3453
3461
|
});
|
|
3454
3462
|
|
|
3463
|
+
// src/lib/memory-write-governor.ts
|
|
3464
|
+
import { createHash } from "crypto";
|
|
3465
|
+
function normalizeMemoryText(text) {
|
|
3466
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3467
|
+
}
|
|
3468
|
+
function classifyMemoryType(input) {
|
|
3469
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3470
|
+
const tool = input.tool_name.toLowerCase();
|
|
3471
|
+
const text = input.raw_text.toLowerCase();
|
|
3472
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3473
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3474
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3475
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3476
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3477
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3478
|
+
return "raw";
|
|
3479
|
+
}
|
|
3480
|
+
function shouldDropMemory(text) {
|
|
3481
|
+
const normalized = normalizeMemoryText(text);
|
|
3482
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3483
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3484
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3485
|
+
}
|
|
3486
|
+
return { drop: false };
|
|
3487
|
+
}
|
|
3488
|
+
function shouldSkipEmbedding(input) {
|
|
3489
|
+
const type = classifyMemoryType(input);
|
|
3490
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3491
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3492
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3493
|
+
return false;
|
|
3494
|
+
}
|
|
3495
|
+
function hashMemoryContent(text) {
|
|
3496
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3497
|
+
}
|
|
3498
|
+
function scopedDedupArgs(input) {
|
|
3499
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3500
|
+
}
|
|
3501
|
+
function governMemoryRecord(record) {
|
|
3502
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3503
|
+
const memoryType = classifyMemoryType({
|
|
3504
|
+
raw_text: normalized,
|
|
3505
|
+
agent_id: record.agent_id,
|
|
3506
|
+
project_name: record.project_name,
|
|
3507
|
+
tool_name: record.tool_name,
|
|
3508
|
+
memory_type: record.memory_type
|
|
3509
|
+
});
|
|
3510
|
+
const drop = shouldDropMemory(normalized);
|
|
3511
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3512
|
+
raw_text: normalized,
|
|
3513
|
+
agent_id: record.agent_id,
|
|
3514
|
+
project_name: record.project_name,
|
|
3515
|
+
tool_name: record.tool_name,
|
|
3516
|
+
memory_type: memoryType
|
|
3517
|
+
});
|
|
3518
|
+
return {
|
|
3519
|
+
record: {
|
|
3520
|
+
...record,
|
|
3521
|
+
raw_text: normalized,
|
|
3522
|
+
memory_type: memoryType,
|
|
3523
|
+
vector: skipEmbedding ? null : record.vector
|
|
3524
|
+
},
|
|
3525
|
+
contentHash: hashMemoryContent(normalized),
|
|
3526
|
+
shouldDrop: drop.drop,
|
|
3527
|
+
dropReason: drop.reason,
|
|
3528
|
+
skipEmbedding,
|
|
3529
|
+
hygiene: {
|
|
3530
|
+
dedup: true,
|
|
3531
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3532
|
+
}
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3535
|
+
async function findScopedDuplicate(input) {
|
|
3536
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3537
|
+
const client = getClient2();
|
|
3538
|
+
const args = scopedDedupArgs(input);
|
|
3539
|
+
let sql = `SELECT id FROM memories
|
|
3540
|
+
WHERE content_hash = ?
|
|
3541
|
+
AND agent_id = ?
|
|
3542
|
+
AND project_name = ?
|
|
3543
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3544
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3545
|
+
if (input.excludeId) {
|
|
3546
|
+
sql += " AND id != ?";
|
|
3547
|
+
args.push(input.excludeId);
|
|
3548
|
+
}
|
|
3549
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3550
|
+
const result = await client.execute({ sql, args });
|
|
3551
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3552
|
+
}
|
|
3553
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3554
|
+
try {
|
|
3555
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3556
|
+
const client = getClient2();
|
|
3557
|
+
const current = await client.execute({
|
|
3558
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3559
|
+
importance, timestamp
|
|
3560
|
+
FROM memories
|
|
3561
|
+
WHERE id = ?
|
|
3562
|
+
LIMIT 1`,
|
|
3563
|
+
args: [memoryId]
|
|
3564
|
+
});
|
|
3565
|
+
const row = current.rows[0];
|
|
3566
|
+
if (!row) return;
|
|
3567
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3568
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3569
|
+
const agentId = String(row.agent_id);
|
|
3570
|
+
const projectName = String(row.project_name);
|
|
3571
|
+
if (contentHash) {
|
|
3572
|
+
await client.execute({
|
|
3573
|
+
sql: `UPDATE memories
|
|
3574
|
+
SET status = 'deleted',
|
|
3575
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3576
|
+
WHERE id != ?
|
|
3577
|
+
AND content_hash = ?
|
|
3578
|
+
AND agent_id = ?
|
|
3579
|
+
AND project_name = ?
|
|
3580
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3581
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3582
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3585
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3586
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3587
|
+
const old = await client.execute({
|
|
3588
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3589
|
+
args: [supersedesId]
|
|
3590
|
+
});
|
|
3591
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3592
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3593
|
+
await client.batch([
|
|
3594
|
+
{
|
|
3595
|
+
sql: `UPDATE memories
|
|
3596
|
+
SET status = 'archived',
|
|
3597
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3598
|
+
WHERE id = ?`,
|
|
3599
|
+
args: [supersedesId]
|
|
3600
|
+
},
|
|
3601
|
+
{
|
|
3602
|
+
sql: `UPDATE memories
|
|
3603
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3604
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3605
|
+
WHERE id = ?`,
|
|
3606
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3607
|
+
}
|
|
3608
|
+
], "write");
|
|
3609
|
+
}
|
|
3610
|
+
} catch (err) {
|
|
3611
|
+
process.stderr.write(
|
|
3612
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3613
|
+
`
|
|
3614
|
+
);
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3618
|
+
if (memoryIds.length === 0) return;
|
|
3619
|
+
const run = () => {
|
|
3620
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3621
|
+
};
|
|
3622
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3623
|
+
else setTimeout(run, 0);
|
|
3624
|
+
}
|
|
3625
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3626
|
+
var init_memory_write_governor = __esm({
|
|
3627
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3628
|
+
"use strict";
|
|
3629
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3630
|
+
"decision",
|
|
3631
|
+
"adr",
|
|
3632
|
+
"behavior",
|
|
3633
|
+
"procedure"
|
|
3634
|
+
]);
|
|
3635
|
+
NOISE_DROP_PATTERNS = [
|
|
3636
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3637
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3638
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3639
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3640
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3641
|
+
];
|
|
3642
|
+
SKIP_EMBED_PATTERNS = [
|
|
3643
|
+
/tmux capture-pane\b/i,
|
|
3644
|
+
/docker ps\b/i,
|
|
3645
|
+
/docker images\b/i,
|
|
3646
|
+
/git status\b/i,
|
|
3647
|
+
/grep .*node_modules/i,
|
|
3648
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3649
|
+
];
|
|
3650
|
+
}
|
|
3651
|
+
});
|
|
3652
|
+
|
|
3455
3653
|
// src/lib/shard-manager.ts
|
|
3456
3654
|
var shard_manager_exports = {};
|
|
3457
3655
|
__export(shard_manager_exports, {
|
|
@@ -3616,7 +3814,8 @@ async function ensureShardSchema(client) {
|
|
|
3616
3814
|
}
|
|
3617
3815
|
for (const idx of [
|
|
3618
3816
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3619
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3817
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3818
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
|
|
3620
3819
|
]) {
|
|
3621
3820
|
try {
|
|
3622
3821
|
await client.execute(idx);
|
|
@@ -4041,7 +4240,6 @@ __export(store_exports, {
|
|
|
4041
4240
|
vectorToBlob: () => vectorToBlob,
|
|
4042
4241
|
writeMemory: () => writeMemory
|
|
4043
4242
|
});
|
|
4044
|
-
import { createHash } from "crypto";
|
|
4045
4243
|
function isBusyError2(err) {
|
|
4046
4244
|
if (err instanceof Error) {
|
|
4047
4245
|
const msg = err.message.toLowerCase();
|
|
@@ -4155,17 +4353,24 @@ async function writeMemory(record) {
|
|
|
4155
4353
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4156
4354
|
);
|
|
4157
4355
|
}
|
|
4158
|
-
const
|
|
4159
|
-
if (
|
|
4356
|
+
const governed = governMemoryRecord(record);
|
|
4357
|
+
if (governed.shouldDrop) return;
|
|
4358
|
+
record = governed.record;
|
|
4359
|
+
const contentHash = governed.contentHash;
|
|
4360
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4361
|
+
if (_pendingRecords.some(
|
|
4362
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4363
|
+
)) {
|
|
4160
4364
|
return;
|
|
4161
4365
|
}
|
|
4162
4366
|
try {
|
|
4163
|
-
const
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4367
|
+
const existing = await findScopedDuplicate({
|
|
4368
|
+
contentHash,
|
|
4369
|
+
agentId: record.agent_id,
|
|
4370
|
+
projectName: record.project_name,
|
|
4371
|
+
memoryType
|
|
4167
4372
|
});
|
|
4168
|
-
if (existing
|
|
4373
|
+
if (existing) return;
|
|
4169
4374
|
} catch {
|
|
4170
4375
|
}
|
|
4171
4376
|
const dbRow = {
|
|
@@ -4196,7 +4401,7 @@ async function writeMemory(record) {
|
|
|
4196
4401
|
tier: record.tier ?? classifyTier(record),
|
|
4197
4402
|
supersedes_id: record.supersedes_id ?? null,
|
|
4198
4403
|
draft: record.draft ? 1 : 0,
|
|
4199
|
-
memory_type:
|
|
4404
|
+
memory_type: memoryType,
|
|
4200
4405
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4201
4406
|
content_hash: contentHash,
|
|
4202
4407
|
intent: record.intent ?? null,
|
|
@@ -4355,6 +4560,7 @@ async function flushBatch() {
|
|
|
4355
4560
|
const globalClient = getClient();
|
|
4356
4561
|
const globalStmts = batch.map(buildStmt);
|
|
4357
4562
|
await globalClient.batch(globalStmts, "write");
|
|
4563
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4358
4564
|
_pendingRecords.splice(0, batch.length);
|
|
4359
4565
|
try {
|
|
4360
4566
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4613,6 +4819,7 @@ var init_store = __esm({
|
|
|
4613
4819
|
init_keychain();
|
|
4614
4820
|
init_config();
|
|
4615
4821
|
init_state_bus();
|
|
4822
|
+
init_memory_write_governor();
|
|
4616
4823
|
INIT_MAX_RETRIES = 3;
|
|
4617
4824
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4618
4825
|
_pendingRecords = [];
|
|
@@ -2299,6 +2299,14 @@ async function ensureSchema() {
|
|
|
2299
2299
|
);
|
|
2300
2300
|
} catch {
|
|
2301
2301
|
}
|
|
2302
|
+
try {
|
|
2303
|
+
await client.execute(
|
|
2304
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2305
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2306
|
+
WHERE content_hash IS NOT NULL`
|
|
2307
|
+
);
|
|
2308
|
+
} catch {
|
|
2309
|
+
}
|
|
2302
2310
|
await client.executeMultiple(`
|
|
2303
2311
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2304
2312
|
id TEXT PRIMARY KEY,
|
|
@@ -3518,6 +3526,196 @@ var init_state_bus = __esm({
|
|
|
3518
3526
|
}
|
|
3519
3527
|
});
|
|
3520
3528
|
|
|
3529
|
+
// src/lib/memory-write-governor.ts
|
|
3530
|
+
import { createHash } from "crypto";
|
|
3531
|
+
function normalizeMemoryText(text) {
|
|
3532
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3533
|
+
}
|
|
3534
|
+
function classifyMemoryType(input) {
|
|
3535
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3536
|
+
const tool = input.tool_name.toLowerCase();
|
|
3537
|
+
const text = input.raw_text.toLowerCase();
|
|
3538
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3539
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3540
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3541
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3542
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3543
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3544
|
+
return "raw";
|
|
3545
|
+
}
|
|
3546
|
+
function shouldDropMemory(text) {
|
|
3547
|
+
const normalized = normalizeMemoryText(text);
|
|
3548
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3549
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3550
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3551
|
+
}
|
|
3552
|
+
return { drop: false };
|
|
3553
|
+
}
|
|
3554
|
+
function shouldSkipEmbedding(input) {
|
|
3555
|
+
const type = classifyMemoryType(input);
|
|
3556
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3557
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3558
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3559
|
+
return false;
|
|
3560
|
+
}
|
|
3561
|
+
function hashMemoryContent(text) {
|
|
3562
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3563
|
+
}
|
|
3564
|
+
function scopedDedupArgs(input) {
|
|
3565
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3566
|
+
}
|
|
3567
|
+
function governMemoryRecord(record) {
|
|
3568
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3569
|
+
const memoryType = classifyMemoryType({
|
|
3570
|
+
raw_text: normalized,
|
|
3571
|
+
agent_id: record.agent_id,
|
|
3572
|
+
project_name: record.project_name,
|
|
3573
|
+
tool_name: record.tool_name,
|
|
3574
|
+
memory_type: record.memory_type
|
|
3575
|
+
});
|
|
3576
|
+
const drop = shouldDropMemory(normalized);
|
|
3577
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3578
|
+
raw_text: normalized,
|
|
3579
|
+
agent_id: record.agent_id,
|
|
3580
|
+
project_name: record.project_name,
|
|
3581
|
+
tool_name: record.tool_name,
|
|
3582
|
+
memory_type: memoryType
|
|
3583
|
+
});
|
|
3584
|
+
return {
|
|
3585
|
+
record: {
|
|
3586
|
+
...record,
|
|
3587
|
+
raw_text: normalized,
|
|
3588
|
+
memory_type: memoryType,
|
|
3589
|
+
vector: skipEmbedding ? null : record.vector
|
|
3590
|
+
},
|
|
3591
|
+
contentHash: hashMemoryContent(normalized),
|
|
3592
|
+
shouldDrop: drop.drop,
|
|
3593
|
+
dropReason: drop.reason,
|
|
3594
|
+
skipEmbedding,
|
|
3595
|
+
hygiene: {
|
|
3596
|
+
dedup: true,
|
|
3597
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3598
|
+
}
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3601
|
+
async function findScopedDuplicate(input) {
|
|
3602
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3603
|
+
const client = getClient2();
|
|
3604
|
+
const args = scopedDedupArgs(input);
|
|
3605
|
+
let sql = `SELECT id FROM memories
|
|
3606
|
+
WHERE content_hash = ?
|
|
3607
|
+
AND agent_id = ?
|
|
3608
|
+
AND project_name = ?
|
|
3609
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3610
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3611
|
+
if (input.excludeId) {
|
|
3612
|
+
sql += " AND id != ?";
|
|
3613
|
+
args.push(input.excludeId);
|
|
3614
|
+
}
|
|
3615
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3616
|
+
const result = await client.execute({ sql, args });
|
|
3617
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3618
|
+
}
|
|
3619
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3620
|
+
try {
|
|
3621
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3622
|
+
const client = getClient2();
|
|
3623
|
+
const current = await client.execute({
|
|
3624
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3625
|
+
importance, timestamp
|
|
3626
|
+
FROM memories
|
|
3627
|
+
WHERE id = ?
|
|
3628
|
+
LIMIT 1`,
|
|
3629
|
+
args: [memoryId]
|
|
3630
|
+
});
|
|
3631
|
+
const row = current.rows[0];
|
|
3632
|
+
if (!row) return;
|
|
3633
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3634
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3635
|
+
const agentId = String(row.agent_id);
|
|
3636
|
+
const projectName = String(row.project_name);
|
|
3637
|
+
if (contentHash) {
|
|
3638
|
+
await client.execute({
|
|
3639
|
+
sql: `UPDATE memories
|
|
3640
|
+
SET status = 'deleted',
|
|
3641
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3642
|
+
WHERE id != ?
|
|
3643
|
+
AND content_hash = ?
|
|
3644
|
+
AND agent_id = ?
|
|
3645
|
+
AND project_name = ?
|
|
3646
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3647
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3648
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3649
|
+
});
|
|
3650
|
+
}
|
|
3651
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3652
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3653
|
+
const old = await client.execute({
|
|
3654
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3655
|
+
args: [supersedesId]
|
|
3656
|
+
});
|
|
3657
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3658
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3659
|
+
await client.batch([
|
|
3660
|
+
{
|
|
3661
|
+
sql: `UPDATE memories
|
|
3662
|
+
SET status = 'archived',
|
|
3663
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3664
|
+
WHERE id = ?`,
|
|
3665
|
+
args: [supersedesId]
|
|
3666
|
+
},
|
|
3667
|
+
{
|
|
3668
|
+
sql: `UPDATE memories
|
|
3669
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3670
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3671
|
+
WHERE id = ?`,
|
|
3672
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3673
|
+
}
|
|
3674
|
+
], "write");
|
|
3675
|
+
}
|
|
3676
|
+
} catch (err) {
|
|
3677
|
+
process.stderr.write(
|
|
3678
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3679
|
+
`
|
|
3680
|
+
);
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3684
|
+
if (memoryIds.length === 0) return;
|
|
3685
|
+
const run = () => {
|
|
3686
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3687
|
+
};
|
|
3688
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3689
|
+
else setTimeout(run, 0);
|
|
3690
|
+
}
|
|
3691
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3692
|
+
var init_memory_write_governor = __esm({
|
|
3693
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3694
|
+
"use strict";
|
|
3695
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3696
|
+
"decision",
|
|
3697
|
+
"adr",
|
|
3698
|
+
"behavior",
|
|
3699
|
+
"procedure"
|
|
3700
|
+
]);
|
|
3701
|
+
NOISE_DROP_PATTERNS = [
|
|
3702
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3703
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3704
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3705
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3706
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3707
|
+
];
|
|
3708
|
+
SKIP_EMBED_PATTERNS = [
|
|
3709
|
+
/tmux capture-pane\b/i,
|
|
3710
|
+
/docker ps\b/i,
|
|
3711
|
+
/docker images\b/i,
|
|
3712
|
+
/git status\b/i,
|
|
3713
|
+
/grep .*node_modules/i,
|
|
3714
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3715
|
+
];
|
|
3716
|
+
}
|
|
3717
|
+
});
|
|
3718
|
+
|
|
3521
3719
|
// src/lib/shard-manager.ts
|
|
3522
3720
|
var shard_manager_exports = {};
|
|
3523
3721
|
__export(shard_manager_exports, {
|
|
@@ -3682,7 +3880,8 @@ async function ensureShardSchema(client) {
|
|
|
3682
3880
|
}
|
|
3683
3881
|
for (const idx of [
|
|
3684
3882
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3685
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3883
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3884
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
|
|
3686
3885
|
]) {
|
|
3687
3886
|
try {
|
|
3688
3887
|
await client.execute(idx);
|
|
@@ -4107,7 +4306,6 @@ __export(store_exports, {
|
|
|
4107
4306
|
vectorToBlob: () => vectorToBlob,
|
|
4108
4307
|
writeMemory: () => writeMemory
|
|
4109
4308
|
});
|
|
4110
|
-
import { createHash } from "crypto";
|
|
4111
4309
|
function isBusyError2(err) {
|
|
4112
4310
|
if (err instanceof Error) {
|
|
4113
4311
|
const msg = err.message.toLowerCase();
|
|
@@ -4221,17 +4419,24 @@ async function writeMemory(record) {
|
|
|
4221
4419
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4222
4420
|
);
|
|
4223
4421
|
}
|
|
4224
|
-
const
|
|
4225
|
-
if (
|
|
4422
|
+
const governed = governMemoryRecord(record);
|
|
4423
|
+
if (governed.shouldDrop) return;
|
|
4424
|
+
record = governed.record;
|
|
4425
|
+
const contentHash = governed.contentHash;
|
|
4426
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4427
|
+
if (_pendingRecords.some(
|
|
4428
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4429
|
+
)) {
|
|
4226
4430
|
return;
|
|
4227
4431
|
}
|
|
4228
4432
|
try {
|
|
4229
|
-
const
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4433
|
+
const existing = await findScopedDuplicate({
|
|
4434
|
+
contentHash,
|
|
4435
|
+
agentId: record.agent_id,
|
|
4436
|
+
projectName: record.project_name,
|
|
4437
|
+
memoryType
|
|
4233
4438
|
});
|
|
4234
|
-
if (existing
|
|
4439
|
+
if (existing) return;
|
|
4235
4440
|
} catch {
|
|
4236
4441
|
}
|
|
4237
4442
|
const dbRow = {
|
|
@@ -4262,7 +4467,7 @@ async function writeMemory(record) {
|
|
|
4262
4467
|
tier: record.tier ?? classifyTier(record),
|
|
4263
4468
|
supersedes_id: record.supersedes_id ?? null,
|
|
4264
4469
|
draft: record.draft ? 1 : 0,
|
|
4265
|
-
memory_type:
|
|
4470
|
+
memory_type: memoryType,
|
|
4266
4471
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4267
4472
|
content_hash: contentHash,
|
|
4268
4473
|
intent: record.intent ?? null,
|
|
@@ -4421,6 +4626,7 @@ async function flushBatch() {
|
|
|
4421
4626
|
const globalClient = getClient();
|
|
4422
4627
|
const globalStmts = batch.map(buildStmt);
|
|
4423
4628
|
await globalClient.batch(globalStmts, "write");
|
|
4629
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4424
4630
|
_pendingRecords.splice(0, batch.length);
|
|
4425
4631
|
try {
|
|
4426
4632
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4679,6 +4885,7 @@ var init_store = __esm({
|
|
|
4679
4885
|
init_keychain();
|
|
4680
4886
|
init_config();
|
|
4681
4887
|
init_state_bus();
|
|
4888
|
+
init_memory_write_governor();
|
|
4682
4889
|
INIT_MAX_RETRIES = 3;
|
|
4683
4890
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4684
4891
|
_pendingRecords = [];
|