@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
package/dist/hooks/stop.js
CHANGED
|
@@ -2611,6 +2611,14 @@ async function ensureSchema() {
|
|
|
2611
2611
|
);
|
|
2612
2612
|
} catch {
|
|
2613
2613
|
}
|
|
2614
|
+
try {
|
|
2615
|
+
await client.execute(
|
|
2616
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2617
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2618
|
+
WHERE content_hash IS NOT NULL`
|
|
2619
|
+
);
|
|
2620
|
+
} catch {
|
|
2621
|
+
}
|
|
2614
2622
|
await client.executeMultiple(`
|
|
2615
2623
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2616
2624
|
id TEXT PRIMARY KEY,
|
|
@@ -3481,6 +3489,196 @@ var init_state_bus = __esm({
|
|
|
3481
3489
|
}
|
|
3482
3490
|
});
|
|
3483
3491
|
|
|
3492
|
+
// src/lib/memory-write-governor.ts
|
|
3493
|
+
import { createHash } from "crypto";
|
|
3494
|
+
function normalizeMemoryText(text) {
|
|
3495
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3496
|
+
}
|
|
3497
|
+
function classifyMemoryType(input2) {
|
|
3498
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3499
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3500
|
+
const text = input2.raw_text.toLowerCase();
|
|
3501
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3502
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3503
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3504
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3505
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3506
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3507
|
+
return "raw";
|
|
3508
|
+
}
|
|
3509
|
+
function shouldDropMemory(text) {
|
|
3510
|
+
const normalized = normalizeMemoryText(text);
|
|
3511
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3512
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3513
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3514
|
+
}
|
|
3515
|
+
return { drop: false };
|
|
3516
|
+
}
|
|
3517
|
+
function shouldSkipEmbedding(input2) {
|
|
3518
|
+
const type = classifyMemoryType(input2);
|
|
3519
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3520
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3521
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3522
|
+
return false;
|
|
3523
|
+
}
|
|
3524
|
+
function hashMemoryContent(text) {
|
|
3525
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3526
|
+
}
|
|
3527
|
+
function scopedDedupArgs(input2) {
|
|
3528
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3529
|
+
}
|
|
3530
|
+
function governMemoryRecord(record) {
|
|
3531
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3532
|
+
const memoryType = classifyMemoryType({
|
|
3533
|
+
raw_text: normalized,
|
|
3534
|
+
agent_id: record.agent_id,
|
|
3535
|
+
project_name: record.project_name,
|
|
3536
|
+
tool_name: record.tool_name,
|
|
3537
|
+
memory_type: record.memory_type
|
|
3538
|
+
});
|
|
3539
|
+
const drop = shouldDropMemory(normalized);
|
|
3540
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3541
|
+
raw_text: normalized,
|
|
3542
|
+
agent_id: record.agent_id,
|
|
3543
|
+
project_name: record.project_name,
|
|
3544
|
+
tool_name: record.tool_name,
|
|
3545
|
+
memory_type: memoryType
|
|
3546
|
+
});
|
|
3547
|
+
return {
|
|
3548
|
+
record: {
|
|
3549
|
+
...record,
|
|
3550
|
+
raw_text: normalized,
|
|
3551
|
+
memory_type: memoryType,
|
|
3552
|
+
vector: skipEmbedding ? null : record.vector
|
|
3553
|
+
},
|
|
3554
|
+
contentHash: hashMemoryContent(normalized),
|
|
3555
|
+
shouldDrop: drop.drop,
|
|
3556
|
+
dropReason: drop.reason,
|
|
3557
|
+
skipEmbedding,
|
|
3558
|
+
hygiene: {
|
|
3559
|
+
dedup: true,
|
|
3560
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3561
|
+
}
|
|
3562
|
+
};
|
|
3563
|
+
}
|
|
3564
|
+
async function findScopedDuplicate(input2) {
|
|
3565
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3566
|
+
const client = getClient2();
|
|
3567
|
+
const args = scopedDedupArgs(input2);
|
|
3568
|
+
let sql = `SELECT id FROM memories
|
|
3569
|
+
WHERE content_hash = ?
|
|
3570
|
+
AND agent_id = ?
|
|
3571
|
+
AND project_name = ?
|
|
3572
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3573
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3574
|
+
if (input2.excludeId) {
|
|
3575
|
+
sql += " AND id != ?";
|
|
3576
|
+
args.push(input2.excludeId);
|
|
3577
|
+
}
|
|
3578
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3579
|
+
const result = await client.execute({ sql, args });
|
|
3580
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3581
|
+
}
|
|
3582
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3583
|
+
try {
|
|
3584
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3585
|
+
const client = getClient2();
|
|
3586
|
+
const current = await client.execute({
|
|
3587
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3588
|
+
importance, timestamp
|
|
3589
|
+
FROM memories
|
|
3590
|
+
WHERE id = ?
|
|
3591
|
+
LIMIT 1`,
|
|
3592
|
+
args: [memoryId]
|
|
3593
|
+
});
|
|
3594
|
+
const row = current.rows[0];
|
|
3595
|
+
if (!row) return;
|
|
3596
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3597
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3598
|
+
const agentId = String(row.agent_id);
|
|
3599
|
+
const projectName = String(row.project_name);
|
|
3600
|
+
if (contentHash) {
|
|
3601
|
+
await client.execute({
|
|
3602
|
+
sql: `UPDATE memories
|
|
3603
|
+
SET status = 'deleted',
|
|
3604
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3605
|
+
WHERE id != ?
|
|
3606
|
+
AND content_hash = ?
|
|
3607
|
+
AND agent_id = ?
|
|
3608
|
+
AND project_name = ?
|
|
3609
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3610
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3611
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3612
|
+
});
|
|
3613
|
+
}
|
|
3614
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3615
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3616
|
+
const old = await client.execute({
|
|
3617
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3618
|
+
args: [supersedesId]
|
|
3619
|
+
});
|
|
3620
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3621
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3622
|
+
await client.batch([
|
|
3623
|
+
{
|
|
3624
|
+
sql: `UPDATE memories
|
|
3625
|
+
SET status = 'archived',
|
|
3626
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3627
|
+
WHERE id = ?`,
|
|
3628
|
+
args: [supersedesId]
|
|
3629
|
+
},
|
|
3630
|
+
{
|
|
3631
|
+
sql: `UPDATE memories
|
|
3632
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3633
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3634
|
+
WHERE id = ?`,
|
|
3635
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3636
|
+
}
|
|
3637
|
+
], "write");
|
|
3638
|
+
}
|
|
3639
|
+
} catch (err) {
|
|
3640
|
+
process.stderr.write(
|
|
3641
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3642
|
+
`
|
|
3643
|
+
);
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3647
|
+
if (memoryIds.length === 0) return;
|
|
3648
|
+
const run = () => {
|
|
3649
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3650
|
+
};
|
|
3651
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3652
|
+
else setTimeout(run, 0);
|
|
3653
|
+
}
|
|
3654
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3655
|
+
var init_memory_write_governor = __esm({
|
|
3656
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3657
|
+
"use strict";
|
|
3658
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3659
|
+
"decision",
|
|
3660
|
+
"adr",
|
|
3661
|
+
"behavior",
|
|
3662
|
+
"procedure"
|
|
3663
|
+
]);
|
|
3664
|
+
NOISE_DROP_PATTERNS = [
|
|
3665
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3666
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3667
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3668
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3669
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3670
|
+
];
|
|
3671
|
+
SKIP_EMBED_PATTERNS = [
|
|
3672
|
+
/tmux capture-pane\b/i,
|
|
3673
|
+
/docker ps\b/i,
|
|
3674
|
+
/docker images\b/i,
|
|
3675
|
+
/git status\b/i,
|
|
3676
|
+
/grep .*node_modules/i,
|
|
3677
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3678
|
+
];
|
|
3679
|
+
}
|
|
3680
|
+
});
|
|
3681
|
+
|
|
3484
3682
|
// src/lib/shard-manager.ts
|
|
3485
3683
|
var shard_manager_exports = {};
|
|
3486
3684
|
__export(shard_manager_exports, {
|
|
@@ -3645,7 +3843,8 @@ async function ensureShardSchema(client) {
|
|
|
3645
3843
|
}
|
|
3646
3844
|
for (const idx of [
|
|
3647
3845
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3648
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3846
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3847
|
+
"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"
|
|
3649
3848
|
]) {
|
|
3650
3849
|
try {
|
|
3651
3850
|
await client.execute(idx);
|
|
@@ -4070,7 +4269,6 @@ __export(store_exports, {
|
|
|
4070
4269
|
vectorToBlob: () => vectorToBlob,
|
|
4071
4270
|
writeMemory: () => writeMemory
|
|
4072
4271
|
});
|
|
4073
|
-
import { createHash } from "crypto";
|
|
4074
4272
|
function isBusyError2(err) {
|
|
4075
4273
|
if (err instanceof Error) {
|
|
4076
4274
|
const msg = err.message.toLowerCase();
|
|
@@ -4184,17 +4382,24 @@ async function writeMemory(record) {
|
|
|
4184
4382
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4185
4383
|
);
|
|
4186
4384
|
}
|
|
4187
|
-
const
|
|
4188
|
-
if (
|
|
4385
|
+
const governed = governMemoryRecord(record);
|
|
4386
|
+
if (governed.shouldDrop) return;
|
|
4387
|
+
record = governed.record;
|
|
4388
|
+
const contentHash = governed.contentHash;
|
|
4389
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4390
|
+
if (_pendingRecords.some(
|
|
4391
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4392
|
+
)) {
|
|
4189
4393
|
return;
|
|
4190
4394
|
}
|
|
4191
4395
|
try {
|
|
4192
|
-
const
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4396
|
+
const existing = await findScopedDuplicate({
|
|
4397
|
+
contentHash,
|
|
4398
|
+
agentId: record.agent_id,
|
|
4399
|
+
projectName: record.project_name,
|
|
4400
|
+
memoryType
|
|
4196
4401
|
});
|
|
4197
|
-
if (existing
|
|
4402
|
+
if (existing) return;
|
|
4198
4403
|
} catch {
|
|
4199
4404
|
}
|
|
4200
4405
|
const dbRow = {
|
|
@@ -4225,7 +4430,7 @@ async function writeMemory(record) {
|
|
|
4225
4430
|
tier: record.tier ?? classifyTier(record),
|
|
4226
4431
|
supersedes_id: record.supersedes_id ?? null,
|
|
4227
4432
|
draft: record.draft ? 1 : 0,
|
|
4228
|
-
memory_type:
|
|
4433
|
+
memory_type: memoryType,
|
|
4229
4434
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4230
4435
|
content_hash: contentHash,
|
|
4231
4436
|
intent: record.intent ?? null,
|
|
@@ -4384,6 +4589,7 @@ async function flushBatch() {
|
|
|
4384
4589
|
const globalClient = getClient();
|
|
4385
4590
|
const globalStmts = batch.map(buildStmt);
|
|
4386
4591
|
await globalClient.batch(globalStmts, "write");
|
|
4592
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4387
4593
|
_pendingRecords.splice(0, batch.length);
|
|
4388
4594
|
try {
|
|
4389
4595
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4642,6 +4848,7 @@ var init_store = __esm({
|
|
|
4642
4848
|
init_keychain();
|
|
4643
4849
|
init_config();
|
|
4644
4850
|
init_state_bus();
|
|
4851
|
+
init_memory_write_governor();
|
|
4645
4852
|
INIT_MAX_RETRIES = 3;
|
|
4646
4853
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4647
4854
|
_pendingRecords = [];
|
|
@@ -2592,6 +2592,14 @@ async function ensureSchema() {
|
|
|
2592
2592
|
);
|
|
2593
2593
|
} catch {
|
|
2594
2594
|
}
|
|
2595
|
+
try {
|
|
2596
|
+
await client.execute(
|
|
2597
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2598
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2599
|
+
WHERE content_hash IS NOT NULL`
|
|
2600
|
+
);
|
|
2601
|
+
} catch {
|
|
2602
|
+
}
|
|
2595
2603
|
await client.executeMultiple(`
|
|
2596
2604
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2597
2605
|
id TEXT PRIMARY KEY,
|
|
@@ -3462,6 +3470,196 @@ var init_state_bus = __esm({
|
|
|
3462
3470
|
}
|
|
3463
3471
|
});
|
|
3464
3472
|
|
|
3473
|
+
// src/lib/memory-write-governor.ts
|
|
3474
|
+
import { createHash } from "crypto";
|
|
3475
|
+
function normalizeMemoryText(text) {
|
|
3476
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3477
|
+
}
|
|
3478
|
+
function classifyMemoryType(input2) {
|
|
3479
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3480
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3481
|
+
const text = input2.raw_text.toLowerCase();
|
|
3482
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3483
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3484
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3485
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3486
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3487
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3488
|
+
return "raw";
|
|
3489
|
+
}
|
|
3490
|
+
function shouldDropMemory(text) {
|
|
3491
|
+
const normalized = normalizeMemoryText(text);
|
|
3492
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3493
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3494
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3495
|
+
}
|
|
3496
|
+
return { drop: false };
|
|
3497
|
+
}
|
|
3498
|
+
function shouldSkipEmbedding(input2) {
|
|
3499
|
+
const type = classifyMemoryType(input2);
|
|
3500
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3501
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3502
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3503
|
+
return false;
|
|
3504
|
+
}
|
|
3505
|
+
function hashMemoryContent(text) {
|
|
3506
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3507
|
+
}
|
|
3508
|
+
function scopedDedupArgs(input2) {
|
|
3509
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3510
|
+
}
|
|
3511
|
+
function governMemoryRecord(record) {
|
|
3512
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3513
|
+
const memoryType = classifyMemoryType({
|
|
3514
|
+
raw_text: normalized,
|
|
3515
|
+
agent_id: record.agent_id,
|
|
3516
|
+
project_name: record.project_name,
|
|
3517
|
+
tool_name: record.tool_name,
|
|
3518
|
+
memory_type: record.memory_type
|
|
3519
|
+
});
|
|
3520
|
+
const drop = shouldDropMemory(normalized);
|
|
3521
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3522
|
+
raw_text: normalized,
|
|
3523
|
+
agent_id: record.agent_id,
|
|
3524
|
+
project_name: record.project_name,
|
|
3525
|
+
tool_name: record.tool_name,
|
|
3526
|
+
memory_type: memoryType
|
|
3527
|
+
});
|
|
3528
|
+
return {
|
|
3529
|
+
record: {
|
|
3530
|
+
...record,
|
|
3531
|
+
raw_text: normalized,
|
|
3532
|
+
memory_type: memoryType,
|
|
3533
|
+
vector: skipEmbedding ? null : record.vector
|
|
3534
|
+
},
|
|
3535
|
+
contentHash: hashMemoryContent(normalized),
|
|
3536
|
+
shouldDrop: drop.drop,
|
|
3537
|
+
dropReason: drop.reason,
|
|
3538
|
+
skipEmbedding,
|
|
3539
|
+
hygiene: {
|
|
3540
|
+
dedup: true,
|
|
3541
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3542
|
+
}
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
async function findScopedDuplicate(input2) {
|
|
3546
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3547
|
+
const client = getClient2();
|
|
3548
|
+
const args = scopedDedupArgs(input2);
|
|
3549
|
+
let sql = `SELECT id FROM memories
|
|
3550
|
+
WHERE content_hash = ?
|
|
3551
|
+
AND agent_id = ?
|
|
3552
|
+
AND project_name = ?
|
|
3553
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3554
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3555
|
+
if (input2.excludeId) {
|
|
3556
|
+
sql += " AND id != ?";
|
|
3557
|
+
args.push(input2.excludeId);
|
|
3558
|
+
}
|
|
3559
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3560
|
+
const result = await client.execute({ sql, args });
|
|
3561
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3562
|
+
}
|
|
3563
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3564
|
+
try {
|
|
3565
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3566
|
+
const client = getClient2();
|
|
3567
|
+
const current = await client.execute({
|
|
3568
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3569
|
+
importance, timestamp
|
|
3570
|
+
FROM memories
|
|
3571
|
+
WHERE id = ?
|
|
3572
|
+
LIMIT 1`,
|
|
3573
|
+
args: [memoryId]
|
|
3574
|
+
});
|
|
3575
|
+
const row = current.rows[0];
|
|
3576
|
+
if (!row) return;
|
|
3577
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3578
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3579
|
+
const agentId = String(row.agent_id);
|
|
3580
|
+
const projectName = String(row.project_name);
|
|
3581
|
+
if (contentHash) {
|
|
3582
|
+
await client.execute({
|
|
3583
|
+
sql: `UPDATE memories
|
|
3584
|
+
SET status = 'deleted',
|
|
3585
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3586
|
+
WHERE id != ?
|
|
3587
|
+
AND content_hash = ?
|
|
3588
|
+
AND agent_id = ?
|
|
3589
|
+
AND project_name = ?
|
|
3590
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3591
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3592
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3595
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3596
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3597
|
+
const old = await client.execute({
|
|
3598
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3599
|
+
args: [supersedesId]
|
|
3600
|
+
});
|
|
3601
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3602
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3603
|
+
await client.batch([
|
|
3604
|
+
{
|
|
3605
|
+
sql: `UPDATE memories
|
|
3606
|
+
SET status = 'archived',
|
|
3607
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3608
|
+
WHERE id = ?`,
|
|
3609
|
+
args: [supersedesId]
|
|
3610
|
+
},
|
|
3611
|
+
{
|
|
3612
|
+
sql: `UPDATE memories
|
|
3613
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3614
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3615
|
+
WHERE id = ?`,
|
|
3616
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3617
|
+
}
|
|
3618
|
+
], "write");
|
|
3619
|
+
}
|
|
3620
|
+
} catch (err) {
|
|
3621
|
+
process.stderr.write(
|
|
3622
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3623
|
+
`
|
|
3624
|
+
);
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3628
|
+
if (memoryIds.length === 0) return;
|
|
3629
|
+
const run = () => {
|
|
3630
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3631
|
+
};
|
|
3632
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3633
|
+
else setTimeout(run, 0);
|
|
3634
|
+
}
|
|
3635
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3636
|
+
var init_memory_write_governor = __esm({
|
|
3637
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3638
|
+
"use strict";
|
|
3639
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3640
|
+
"decision",
|
|
3641
|
+
"adr",
|
|
3642
|
+
"behavior",
|
|
3643
|
+
"procedure"
|
|
3644
|
+
]);
|
|
3645
|
+
NOISE_DROP_PATTERNS = [
|
|
3646
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3647
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3648
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3649
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3650
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3651
|
+
];
|
|
3652
|
+
SKIP_EMBED_PATTERNS = [
|
|
3653
|
+
/tmux capture-pane\b/i,
|
|
3654
|
+
/docker ps\b/i,
|
|
3655
|
+
/docker images\b/i,
|
|
3656
|
+
/git status\b/i,
|
|
3657
|
+
/grep .*node_modules/i,
|
|
3658
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3659
|
+
];
|
|
3660
|
+
}
|
|
3661
|
+
});
|
|
3662
|
+
|
|
3465
3663
|
// src/lib/shard-manager.ts
|
|
3466
3664
|
var shard_manager_exports = {};
|
|
3467
3665
|
__export(shard_manager_exports, {
|
|
@@ -3626,7 +3824,8 @@ async function ensureShardSchema(client) {
|
|
|
3626
3824
|
}
|
|
3627
3825
|
for (const idx of [
|
|
3628
3826
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3629
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3827
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3828
|
+
"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"
|
|
3630
3829
|
]) {
|
|
3631
3830
|
try {
|
|
3632
3831
|
await client.execute(idx);
|
|
@@ -4051,7 +4250,6 @@ __export(store_exports, {
|
|
|
4051
4250
|
vectorToBlob: () => vectorToBlob,
|
|
4052
4251
|
writeMemory: () => writeMemory
|
|
4053
4252
|
});
|
|
4054
|
-
import { createHash } from "crypto";
|
|
4055
4253
|
function isBusyError2(err) {
|
|
4056
4254
|
if (err instanceof Error) {
|
|
4057
4255
|
const msg = err.message.toLowerCase();
|
|
@@ -4165,17 +4363,24 @@ async function writeMemory(record) {
|
|
|
4165
4363
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4166
4364
|
);
|
|
4167
4365
|
}
|
|
4168
|
-
const
|
|
4169
|
-
if (
|
|
4366
|
+
const governed = governMemoryRecord(record);
|
|
4367
|
+
if (governed.shouldDrop) return;
|
|
4368
|
+
record = governed.record;
|
|
4369
|
+
const contentHash = governed.contentHash;
|
|
4370
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4371
|
+
if (_pendingRecords.some(
|
|
4372
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4373
|
+
)) {
|
|
4170
4374
|
return;
|
|
4171
4375
|
}
|
|
4172
4376
|
try {
|
|
4173
|
-
const
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4377
|
+
const existing = await findScopedDuplicate({
|
|
4378
|
+
contentHash,
|
|
4379
|
+
agentId: record.agent_id,
|
|
4380
|
+
projectName: record.project_name,
|
|
4381
|
+
memoryType
|
|
4177
4382
|
});
|
|
4178
|
-
if (existing
|
|
4383
|
+
if (existing) return;
|
|
4179
4384
|
} catch {
|
|
4180
4385
|
}
|
|
4181
4386
|
const dbRow = {
|
|
@@ -4206,7 +4411,7 @@ async function writeMemory(record) {
|
|
|
4206
4411
|
tier: record.tier ?? classifyTier(record),
|
|
4207
4412
|
supersedes_id: record.supersedes_id ?? null,
|
|
4208
4413
|
draft: record.draft ? 1 : 0,
|
|
4209
|
-
memory_type:
|
|
4414
|
+
memory_type: memoryType,
|
|
4210
4415
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4211
4416
|
content_hash: contentHash,
|
|
4212
4417
|
intent: record.intent ?? null,
|
|
@@ -4365,6 +4570,7 @@ async function flushBatch() {
|
|
|
4365
4570
|
const globalClient = getClient();
|
|
4366
4571
|
const globalStmts = batch.map(buildStmt);
|
|
4367
4572
|
await globalClient.batch(globalStmts, "write");
|
|
4573
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4368
4574
|
_pendingRecords.splice(0, batch.length);
|
|
4369
4575
|
try {
|
|
4370
4576
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4623,6 +4829,7 @@ var init_store = __esm({
|
|
|
4623
4829
|
init_keychain();
|
|
4624
4830
|
init_config();
|
|
4625
4831
|
init_state_bus();
|
|
4832
|
+
init_memory_write_governor();
|
|
4626
4833
|
INIT_MAX_RETRIES = 3;
|
|
4627
4834
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4628
4835
|
_pendingRecords = [];
|
|
@@ -2349,6 +2349,14 @@ async function ensureSchema() {
|
|
|
2349
2349
|
);
|
|
2350
2350
|
} catch {
|
|
2351
2351
|
}
|
|
2352
|
+
try {
|
|
2353
|
+
await client.execute(
|
|
2354
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2355
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2356
|
+
WHERE content_hash IS NOT NULL`
|
|
2357
|
+
);
|
|
2358
|
+
} catch {
|
|
2359
|
+
}
|
|
2352
2360
|
await client.executeMultiple(`
|
|
2353
2361
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2354
2362
|
id TEXT PRIMARY KEY,
|
|
@@ -3373,7 +3381,8 @@ async function ensureShardSchema(client) {
|
|
|
3373
3381
|
}
|
|
3374
3382
|
for (const idx of [
|
|
3375
3383
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3376
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3384
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3385
|
+
"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"
|
|
3377
3386
|
]) {
|
|
3378
3387
|
try {
|
|
3379
3388
|
await client.execute(idx);
|
|
@@ -6598,7 +6607,11 @@ init_database();
|
|
|
6598
6607
|
init_keychain();
|
|
6599
6608
|
init_config();
|
|
6600
6609
|
init_state_bus();
|
|
6610
|
+
|
|
6611
|
+
// src/lib/memory-write-governor.ts
|
|
6601
6612
|
import { createHash } from "crypto";
|
|
6613
|
+
|
|
6614
|
+
// src/lib/store.ts
|
|
6602
6615
|
var INIT_MAX_RETRIES = 3;
|
|
6603
6616
|
var INIT_RETRY_DELAY_MS = 1e3;
|
|
6604
6617
|
function isBusyError2(err) {
|