@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
|
@@ -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,
|
|
@@ -3557,6 +3565,196 @@ var init_keychain = __esm({
|
|
|
3557
3565
|
}
|
|
3558
3566
|
});
|
|
3559
3567
|
|
|
3568
|
+
// src/lib/memory-write-governor.ts
|
|
3569
|
+
import { createHash } from "crypto";
|
|
3570
|
+
function normalizeMemoryText(text) {
|
|
3571
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3572
|
+
}
|
|
3573
|
+
function classifyMemoryType(input) {
|
|
3574
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3575
|
+
const tool = input.tool_name.toLowerCase();
|
|
3576
|
+
const text = input.raw_text.toLowerCase();
|
|
3577
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3578
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3579
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3580
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3581
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3582
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3583
|
+
return "raw";
|
|
3584
|
+
}
|
|
3585
|
+
function shouldDropMemory(text) {
|
|
3586
|
+
const normalized = normalizeMemoryText(text);
|
|
3587
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3588
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3589
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3590
|
+
}
|
|
3591
|
+
return { drop: false };
|
|
3592
|
+
}
|
|
3593
|
+
function shouldSkipEmbedding(input) {
|
|
3594
|
+
const type = classifyMemoryType(input);
|
|
3595
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3596
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3597
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3598
|
+
return false;
|
|
3599
|
+
}
|
|
3600
|
+
function hashMemoryContent(text) {
|
|
3601
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3602
|
+
}
|
|
3603
|
+
function scopedDedupArgs(input) {
|
|
3604
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3605
|
+
}
|
|
3606
|
+
function governMemoryRecord(record) {
|
|
3607
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3608
|
+
const memoryType = classifyMemoryType({
|
|
3609
|
+
raw_text: normalized,
|
|
3610
|
+
agent_id: record.agent_id,
|
|
3611
|
+
project_name: record.project_name,
|
|
3612
|
+
tool_name: record.tool_name,
|
|
3613
|
+
memory_type: record.memory_type
|
|
3614
|
+
});
|
|
3615
|
+
const drop = shouldDropMemory(normalized);
|
|
3616
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3617
|
+
raw_text: normalized,
|
|
3618
|
+
agent_id: record.agent_id,
|
|
3619
|
+
project_name: record.project_name,
|
|
3620
|
+
tool_name: record.tool_name,
|
|
3621
|
+
memory_type: memoryType
|
|
3622
|
+
});
|
|
3623
|
+
return {
|
|
3624
|
+
record: {
|
|
3625
|
+
...record,
|
|
3626
|
+
raw_text: normalized,
|
|
3627
|
+
memory_type: memoryType,
|
|
3628
|
+
vector: skipEmbedding ? null : record.vector
|
|
3629
|
+
},
|
|
3630
|
+
contentHash: hashMemoryContent(normalized),
|
|
3631
|
+
shouldDrop: drop.drop,
|
|
3632
|
+
dropReason: drop.reason,
|
|
3633
|
+
skipEmbedding,
|
|
3634
|
+
hygiene: {
|
|
3635
|
+
dedup: true,
|
|
3636
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3637
|
+
}
|
|
3638
|
+
};
|
|
3639
|
+
}
|
|
3640
|
+
async function findScopedDuplicate(input) {
|
|
3641
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3642
|
+
const client = getClient2();
|
|
3643
|
+
const args = scopedDedupArgs(input);
|
|
3644
|
+
let sql = `SELECT id FROM memories
|
|
3645
|
+
WHERE content_hash = ?
|
|
3646
|
+
AND agent_id = ?
|
|
3647
|
+
AND project_name = ?
|
|
3648
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3649
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3650
|
+
if (input.excludeId) {
|
|
3651
|
+
sql += " AND id != ?";
|
|
3652
|
+
args.push(input.excludeId);
|
|
3653
|
+
}
|
|
3654
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3655
|
+
const result = await client.execute({ sql, args });
|
|
3656
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3657
|
+
}
|
|
3658
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3659
|
+
try {
|
|
3660
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3661
|
+
const client = getClient2();
|
|
3662
|
+
const current = await client.execute({
|
|
3663
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3664
|
+
importance, timestamp
|
|
3665
|
+
FROM memories
|
|
3666
|
+
WHERE id = ?
|
|
3667
|
+
LIMIT 1`,
|
|
3668
|
+
args: [memoryId]
|
|
3669
|
+
});
|
|
3670
|
+
const row = current.rows[0];
|
|
3671
|
+
if (!row) return;
|
|
3672
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3673
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3674
|
+
const agentId = String(row.agent_id);
|
|
3675
|
+
const projectName = String(row.project_name);
|
|
3676
|
+
if (contentHash) {
|
|
3677
|
+
await client.execute({
|
|
3678
|
+
sql: `UPDATE memories
|
|
3679
|
+
SET status = 'deleted',
|
|
3680
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3681
|
+
WHERE id != ?
|
|
3682
|
+
AND content_hash = ?
|
|
3683
|
+
AND agent_id = ?
|
|
3684
|
+
AND project_name = ?
|
|
3685
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3686
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3687
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3688
|
+
});
|
|
3689
|
+
}
|
|
3690
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3691
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3692
|
+
const old = await client.execute({
|
|
3693
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3694
|
+
args: [supersedesId]
|
|
3695
|
+
});
|
|
3696
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3697
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3698
|
+
await client.batch([
|
|
3699
|
+
{
|
|
3700
|
+
sql: `UPDATE memories
|
|
3701
|
+
SET status = 'archived',
|
|
3702
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3703
|
+
WHERE id = ?`,
|
|
3704
|
+
args: [supersedesId]
|
|
3705
|
+
},
|
|
3706
|
+
{
|
|
3707
|
+
sql: `UPDATE memories
|
|
3708
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3709
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3710
|
+
WHERE id = ?`,
|
|
3711
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3712
|
+
}
|
|
3713
|
+
], "write");
|
|
3714
|
+
}
|
|
3715
|
+
} catch (err) {
|
|
3716
|
+
process.stderr.write(
|
|
3717
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3718
|
+
`
|
|
3719
|
+
);
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3723
|
+
if (memoryIds.length === 0) return;
|
|
3724
|
+
const run = () => {
|
|
3725
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3726
|
+
};
|
|
3727
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3728
|
+
else setTimeout(run, 0);
|
|
3729
|
+
}
|
|
3730
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3731
|
+
var init_memory_write_governor = __esm({
|
|
3732
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3733
|
+
"use strict";
|
|
3734
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3735
|
+
"decision",
|
|
3736
|
+
"adr",
|
|
3737
|
+
"behavior",
|
|
3738
|
+
"procedure"
|
|
3739
|
+
]);
|
|
3740
|
+
NOISE_DROP_PATTERNS = [
|
|
3741
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3742
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3743
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3744
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3745
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3746
|
+
];
|
|
3747
|
+
SKIP_EMBED_PATTERNS = [
|
|
3748
|
+
/tmux capture-pane\b/i,
|
|
3749
|
+
/docker ps\b/i,
|
|
3750
|
+
/docker images\b/i,
|
|
3751
|
+
/git status\b/i,
|
|
3752
|
+
/grep .*node_modules/i,
|
|
3753
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3754
|
+
];
|
|
3755
|
+
}
|
|
3756
|
+
});
|
|
3757
|
+
|
|
3560
3758
|
// src/lib/shard-manager.ts
|
|
3561
3759
|
var shard_manager_exports = {};
|
|
3562
3760
|
__export(shard_manager_exports, {
|
|
@@ -3721,7 +3919,8 @@ async function ensureShardSchema(client) {
|
|
|
3721
3919
|
}
|
|
3722
3920
|
for (const idx of [
|
|
3723
3921
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3724
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3922
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3923
|
+
"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"
|
|
3725
3924
|
]) {
|
|
3726
3925
|
try {
|
|
3727
3926
|
await client.execute(idx);
|
|
@@ -4146,7 +4345,6 @@ __export(store_exports, {
|
|
|
4146
4345
|
vectorToBlob: () => vectorToBlob,
|
|
4147
4346
|
writeMemory: () => writeMemory
|
|
4148
4347
|
});
|
|
4149
|
-
import { createHash } from "crypto";
|
|
4150
4348
|
function isBusyError2(err) {
|
|
4151
4349
|
if (err instanceof Error) {
|
|
4152
4350
|
const msg = err.message.toLowerCase();
|
|
@@ -4260,17 +4458,24 @@ async function writeMemory(record) {
|
|
|
4260
4458
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4261
4459
|
);
|
|
4262
4460
|
}
|
|
4263
|
-
const
|
|
4264
|
-
if (
|
|
4461
|
+
const governed = governMemoryRecord(record);
|
|
4462
|
+
if (governed.shouldDrop) return;
|
|
4463
|
+
record = governed.record;
|
|
4464
|
+
const contentHash = governed.contentHash;
|
|
4465
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4466
|
+
if (_pendingRecords.some(
|
|
4467
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4468
|
+
)) {
|
|
4265
4469
|
return;
|
|
4266
4470
|
}
|
|
4267
4471
|
try {
|
|
4268
|
-
const
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4472
|
+
const existing = await findScopedDuplicate({
|
|
4473
|
+
contentHash,
|
|
4474
|
+
agentId: record.agent_id,
|
|
4475
|
+
projectName: record.project_name,
|
|
4476
|
+
memoryType
|
|
4272
4477
|
});
|
|
4273
|
-
if (existing
|
|
4478
|
+
if (existing) return;
|
|
4274
4479
|
} catch {
|
|
4275
4480
|
}
|
|
4276
4481
|
const dbRow = {
|
|
@@ -4301,7 +4506,7 @@ async function writeMemory(record) {
|
|
|
4301
4506
|
tier: record.tier ?? classifyTier(record),
|
|
4302
4507
|
supersedes_id: record.supersedes_id ?? null,
|
|
4303
4508
|
draft: record.draft ? 1 : 0,
|
|
4304
|
-
memory_type:
|
|
4509
|
+
memory_type: memoryType,
|
|
4305
4510
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4306
4511
|
content_hash: contentHash,
|
|
4307
4512
|
intent: record.intent ?? null,
|
|
@@ -4460,6 +4665,7 @@ async function flushBatch() {
|
|
|
4460
4665
|
const globalClient = getClient();
|
|
4461
4666
|
const globalStmts = batch.map(buildStmt);
|
|
4462
4667
|
await globalClient.batch(globalStmts, "write");
|
|
4668
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4463
4669
|
_pendingRecords.splice(0, batch.length);
|
|
4464
4670
|
try {
|
|
4465
4671
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4718,6 +4924,7 @@ var init_store = __esm({
|
|
|
4718
4924
|
init_keychain();
|
|
4719
4925
|
init_config();
|
|
4720
4926
|
init_state_bus();
|
|
4927
|
+
init_memory_write_governor();
|
|
4721
4928
|
INIT_MAX_RETRIES = 3;
|
|
4722
4929
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4723
4930
|
_pendingRecords = [];
|
package/dist/bin/exe-rename.js
CHANGED
|
@@ -2037,6 +2037,14 @@ async function ensureSchema() {
|
|
|
2037
2037
|
);
|
|
2038
2038
|
} catch {
|
|
2039
2039
|
}
|
|
2040
|
+
try {
|
|
2041
|
+
await client.execute(
|
|
2042
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2043
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2044
|
+
WHERE content_hash IS NOT NULL`
|
|
2045
|
+
);
|
|
2046
|
+
} catch {
|
|
2047
|
+
}
|
|
2040
2048
|
await client.executeMultiple(`
|
|
2041
2049
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2042
2050
|
id TEXT PRIMARY KEY,
|
package/dist/bin/exe-review.js
CHANGED
|
@@ -2302,6 +2302,14 @@ async function ensureSchema() {
|
|
|
2302
2302
|
);
|
|
2303
2303
|
} catch {
|
|
2304
2304
|
}
|
|
2305
|
+
try {
|
|
2306
|
+
await client.execute(
|
|
2307
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2308
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2309
|
+
WHERE content_hash IS NOT NULL`
|
|
2310
|
+
);
|
|
2311
|
+
} catch {
|
|
2312
|
+
}
|
|
2305
2313
|
await client.executeMultiple(`
|
|
2306
2314
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2307
2315
|
id TEXT PRIMARY KEY,
|
|
@@ -3047,6 +3055,196 @@ var init_state_bus = __esm({
|
|
|
3047
3055
|
}
|
|
3048
3056
|
});
|
|
3049
3057
|
|
|
3058
|
+
// src/lib/memory-write-governor.ts
|
|
3059
|
+
import { createHash } from "crypto";
|
|
3060
|
+
function normalizeMemoryText(text) {
|
|
3061
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3062
|
+
}
|
|
3063
|
+
function classifyMemoryType(input) {
|
|
3064
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3065
|
+
const tool = input.tool_name.toLowerCase();
|
|
3066
|
+
const text = input.raw_text.toLowerCase();
|
|
3067
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3068
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3069
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3070
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3071
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3072
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3073
|
+
return "raw";
|
|
3074
|
+
}
|
|
3075
|
+
function shouldDropMemory(text) {
|
|
3076
|
+
const normalized = normalizeMemoryText(text);
|
|
3077
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3078
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3079
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3080
|
+
}
|
|
3081
|
+
return { drop: false };
|
|
3082
|
+
}
|
|
3083
|
+
function shouldSkipEmbedding(input) {
|
|
3084
|
+
const type = classifyMemoryType(input);
|
|
3085
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3086
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3087
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3088
|
+
return false;
|
|
3089
|
+
}
|
|
3090
|
+
function hashMemoryContent(text) {
|
|
3091
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3092
|
+
}
|
|
3093
|
+
function scopedDedupArgs(input) {
|
|
3094
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3095
|
+
}
|
|
3096
|
+
function governMemoryRecord(record) {
|
|
3097
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3098
|
+
const memoryType = classifyMemoryType({
|
|
3099
|
+
raw_text: normalized,
|
|
3100
|
+
agent_id: record.agent_id,
|
|
3101
|
+
project_name: record.project_name,
|
|
3102
|
+
tool_name: record.tool_name,
|
|
3103
|
+
memory_type: record.memory_type
|
|
3104
|
+
});
|
|
3105
|
+
const drop = shouldDropMemory(normalized);
|
|
3106
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3107
|
+
raw_text: normalized,
|
|
3108
|
+
agent_id: record.agent_id,
|
|
3109
|
+
project_name: record.project_name,
|
|
3110
|
+
tool_name: record.tool_name,
|
|
3111
|
+
memory_type: memoryType
|
|
3112
|
+
});
|
|
3113
|
+
return {
|
|
3114
|
+
record: {
|
|
3115
|
+
...record,
|
|
3116
|
+
raw_text: normalized,
|
|
3117
|
+
memory_type: memoryType,
|
|
3118
|
+
vector: skipEmbedding ? null : record.vector
|
|
3119
|
+
},
|
|
3120
|
+
contentHash: hashMemoryContent(normalized),
|
|
3121
|
+
shouldDrop: drop.drop,
|
|
3122
|
+
dropReason: drop.reason,
|
|
3123
|
+
skipEmbedding,
|
|
3124
|
+
hygiene: {
|
|
3125
|
+
dedup: true,
|
|
3126
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3127
|
+
}
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
async function findScopedDuplicate(input) {
|
|
3131
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3132
|
+
const client = getClient2();
|
|
3133
|
+
const args = scopedDedupArgs(input);
|
|
3134
|
+
let sql = `SELECT id FROM memories
|
|
3135
|
+
WHERE content_hash = ?
|
|
3136
|
+
AND agent_id = ?
|
|
3137
|
+
AND project_name = ?
|
|
3138
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3139
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3140
|
+
if (input.excludeId) {
|
|
3141
|
+
sql += " AND id != ?";
|
|
3142
|
+
args.push(input.excludeId);
|
|
3143
|
+
}
|
|
3144
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3145
|
+
const result = await client.execute({ sql, args });
|
|
3146
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3147
|
+
}
|
|
3148
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3149
|
+
try {
|
|
3150
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3151
|
+
const client = getClient2();
|
|
3152
|
+
const current = await client.execute({
|
|
3153
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3154
|
+
importance, timestamp
|
|
3155
|
+
FROM memories
|
|
3156
|
+
WHERE id = ?
|
|
3157
|
+
LIMIT 1`,
|
|
3158
|
+
args: [memoryId]
|
|
3159
|
+
});
|
|
3160
|
+
const row = current.rows[0];
|
|
3161
|
+
if (!row) return;
|
|
3162
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3163
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3164
|
+
const agentId = String(row.agent_id);
|
|
3165
|
+
const projectName = String(row.project_name);
|
|
3166
|
+
if (contentHash) {
|
|
3167
|
+
await client.execute({
|
|
3168
|
+
sql: `UPDATE memories
|
|
3169
|
+
SET status = 'deleted',
|
|
3170
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3171
|
+
WHERE id != ?
|
|
3172
|
+
AND content_hash = ?
|
|
3173
|
+
AND agent_id = ?
|
|
3174
|
+
AND project_name = ?
|
|
3175
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3176
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3177
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3178
|
+
});
|
|
3179
|
+
}
|
|
3180
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3181
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3182
|
+
const old = await client.execute({
|
|
3183
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3184
|
+
args: [supersedesId]
|
|
3185
|
+
});
|
|
3186
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3187
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3188
|
+
await client.batch([
|
|
3189
|
+
{
|
|
3190
|
+
sql: `UPDATE memories
|
|
3191
|
+
SET status = 'archived',
|
|
3192
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3193
|
+
WHERE id = ?`,
|
|
3194
|
+
args: [supersedesId]
|
|
3195
|
+
},
|
|
3196
|
+
{
|
|
3197
|
+
sql: `UPDATE memories
|
|
3198
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3199
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3200
|
+
WHERE id = ?`,
|
|
3201
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3202
|
+
}
|
|
3203
|
+
], "write");
|
|
3204
|
+
}
|
|
3205
|
+
} catch (err) {
|
|
3206
|
+
process.stderr.write(
|
|
3207
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3208
|
+
`
|
|
3209
|
+
);
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3213
|
+
if (memoryIds.length === 0) return;
|
|
3214
|
+
const run = () => {
|
|
3215
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3216
|
+
};
|
|
3217
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3218
|
+
else setTimeout(run, 0);
|
|
3219
|
+
}
|
|
3220
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3221
|
+
var init_memory_write_governor = __esm({
|
|
3222
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3223
|
+
"use strict";
|
|
3224
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3225
|
+
"decision",
|
|
3226
|
+
"adr",
|
|
3227
|
+
"behavior",
|
|
3228
|
+
"procedure"
|
|
3229
|
+
]);
|
|
3230
|
+
NOISE_DROP_PATTERNS = [
|
|
3231
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3232
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3233
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3234
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3235
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3236
|
+
];
|
|
3237
|
+
SKIP_EMBED_PATTERNS = [
|
|
3238
|
+
/tmux capture-pane\b/i,
|
|
3239
|
+
/docker ps\b/i,
|
|
3240
|
+
/docker images\b/i,
|
|
3241
|
+
/git status\b/i,
|
|
3242
|
+
/grep .*node_modules/i,
|
|
3243
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3244
|
+
];
|
|
3245
|
+
}
|
|
3246
|
+
});
|
|
3247
|
+
|
|
3050
3248
|
// src/lib/shard-manager.ts
|
|
3051
3249
|
var shard_manager_exports = {};
|
|
3052
3250
|
__export(shard_manager_exports, {
|
|
@@ -3211,7 +3409,8 @@ async function ensureShardSchema(client) {
|
|
|
3211
3409
|
}
|
|
3212
3410
|
for (const idx of [
|
|
3213
3411
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3214
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3412
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3413
|
+
"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"
|
|
3215
3414
|
]) {
|
|
3216
3415
|
try {
|
|
3217
3416
|
await client.execute(idx);
|
|
@@ -3636,7 +3835,6 @@ __export(store_exports, {
|
|
|
3636
3835
|
vectorToBlob: () => vectorToBlob,
|
|
3637
3836
|
writeMemory: () => writeMemory
|
|
3638
3837
|
});
|
|
3639
|
-
import { createHash } from "crypto";
|
|
3640
3838
|
function isBusyError2(err) {
|
|
3641
3839
|
if (err instanceof Error) {
|
|
3642
3840
|
const msg = err.message.toLowerCase();
|
|
@@ -3750,17 +3948,24 @@ async function writeMemory(record) {
|
|
|
3750
3948
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3751
3949
|
);
|
|
3752
3950
|
}
|
|
3753
|
-
const
|
|
3754
|
-
if (
|
|
3951
|
+
const governed = governMemoryRecord(record);
|
|
3952
|
+
if (governed.shouldDrop) return;
|
|
3953
|
+
record = governed.record;
|
|
3954
|
+
const contentHash = governed.contentHash;
|
|
3955
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3956
|
+
if (_pendingRecords.some(
|
|
3957
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3958
|
+
)) {
|
|
3755
3959
|
return;
|
|
3756
3960
|
}
|
|
3757
3961
|
try {
|
|
3758
|
-
const
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3962
|
+
const existing = await findScopedDuplicate({
|
|
3963
|
+
contentHash,
|
|
3964
|
+
agentId: record.agent_id,
|
|
3965
|
+
projectName: record.project_name,
|
|
3966
|
+
memoryType
|
|
3762
3967
|
});
|
|
3763
|
-
if (existing
|
|
3968
|
+
if (existing) return;
|
|
3764
3969
|
} catch {
|
|
3765
3970
|
}
|
|
3766
3971
|
const dbRow = {
|
|
@@ -3791,7 +3996,7 @@ async function writeMemory(record) {
|
|
|
3791
3996
|
tier: record.tier ?? classifyTier(record),
|
|
3792
3997
|
supersedes_id: record.supersedes_id ?? null,
|
|
3793
3998
|
draft: record.draft ? 1 : 0,
|
|
3794
|
-
memory_type:
|
|
3999
|
+
memory_type: memoryType,
|
|
3795
4000
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3796
4001
|
content_hash: contentHash,
|
|
3797
4002
|
intent: record.intent ?? null,
|
|
@@ -3950,6 +4155,7 @@ async function flushBatch() {
|
|
|
3950
4155
|
const globalClient = getClient();
|
|
3951
4156
|
const globalStmts = batch.map(buildStmt);
|
|
3952
4157
|
await globalClient.batch(globalStmts, "write");
|
|
4158
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3953
4159
|
_pendingRecords.splice(0, batch.length);
|
|
3954
4160
|
try {
|
|
3955
4161
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4208,6 +4414,7 @@ var init_store = __esm({
|
|
|
4208
4414
|
init_keychain();
|
|
4209
4415
|
init_config();
|
|
4210
4416
|
init_state_bus();
|
|
4417
|
+
init_memory_write_governor();
|
|
4211
4418
|
INIT_MAX_RETRIES = 3;
|
|
4212
4419
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4213
4420
|
_pendingRecords = [];
|