@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
|
@@ -2290,6 +2290,14 @@ async function ensureSchema() {
|
|
|
2290
2290
|
);
|
|
2291
2291
|
} catch {
|
|
2292
2292
|
}
|
|
2293
|
+
try {
|
|
2294
|
+
await client.execute(
|
|
2295
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2296
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2297
|
+
WHERE content_hash IS NOT NULL`
|
|
2298
|
+
);
|
|
2299
|
+
} catch {
|
|
2300
|
+
}
|
|
2293
2301
|
await client.executeMultiple(`
|
|
2294
2302
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2295
2303
|
id TEXT PRIMARY KEY,
|
|
@@ -3035,6 +3043,196 @@ var init_state_bus = __esm({
|
|
|
3035
3043
|
}
|
|
3036
3044
|
});
|
|
3037
3045
|
|
|
3046
|
+
// src/lib/memory-write-governor.ts
|
|
3047
|
+
import { createHash } from "crypto";
|
|
3048
|
+
function normalizeMemoryText(text) {
|
|
3049
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3050
|
+
}
|
|
3051
|
+
function classifyMemoryType(input2) {
|
|
3052
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3053
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3054
|
+
const text = input2.raw_text.toLowerCase();
|
|
3055
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3056
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3057
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3058
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3059
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3060
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3061
|
+
return "raw";
|
|
3062
|
+
}
|
|
3063
|
+
function shouldDropMemory(text) {
|
|
3064
|
+
const normalized = normalizeMemoryText(text);
|
|
3065
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3066
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3067
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3068
|
+
}
|
|
3069
|
+
return { drop: false };
|
|
3070
|
+
}
|
|
3071
|
+
function shouldSkipEmbedding(input2) {
|
|
3072
|
+
const type = classifyMemoryType(input2);
|
|
3073
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3074
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3075
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3076
|
+
return false;
|
|
3077
|
+
}
|
|
3078
|
+
function hashMemoryContent(text) {
|
|
3079
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3080
|
+
}
|
|
3081
|
+
function scopedDedupArgs(input2) {
|
|
3082
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3083
|
+
}
|
|
3084
|
+
function governMemoryRecord(record) {
|
|
3085
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3086
|
+
const memoryType = classifyMemoryType({
|
|
3087
|
+
raw_text: normalized,
|
|
3088
|
+
agent_id: record.agent_id,
|
|
3089
|
+
project_name: record.project_name,
|
|
3090
|
+
tool_name: record.tool_name,
|
|
3091
|
+
memory_type: record.memory_type
|
|
3092
|
+
});
|
|
3093
|
+
const drop = shouldDropMemory(normalized);
|
|
3094
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3095
|
+
raw_text: normalized,
|
|
3096
|
+
agent_id: record.agent_id,
|
|
3097
|
+
project_name: record.project_name,
|
|
3098
|
+
tool_name: record.tool_name,
|
|
3099
|
+
memory_type: memoryType
|
|
3100
|
+
});
|
|
3101
|
+
return {
|
|
3102
|
+
record: {
|
|
3103
|
+
...record,
|
|
3104
|
+
raw_text: normalized,
|
|
3105
|
+
memory_type: memoryType,
|
|
3106
|
+
vector: skipEmbedding ? null : record.vector
|
|
3107
|
+
},
|
|
3108
|
+
contentHash: hashMemoryContent(normalized),
|
|
3109
|
+
shouldDrop: drop.drop,
|
|
3110
|
+
dropReason: drop.reason,
|
|
3111
|
+
skipEmbedding,
|
|
3112
|
+
hygiene: {
|
|
3113
|
+
dedup: true,
|
|
3114
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3115
|
+
}
|
|
3116
|
+
};
|
|
3117
|
+
}
|
|
3118
|
+
async function findScopedDuplicate(input2) {
|
|
3119
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3120
|
+
const client = getClient2();
|
|
3121
|
+
const args = scopedDedupArgs(input2);
|
|
3122
|
+
let sql = `SELECT id FROM memories
|
|
3123
|
+
WHERE content_hash = ?
|
|
3124
|
+
AND agent_id = ?
|
|
3125
|
+
AND project_name = ?
|
|
3126
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3127
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3128
|
+
if (input2.excludeId) {
|
|
3129
|
+
sql += " AND id != ?";
|
|
3130
|
+
args.push(input2.excludeId);
|
|
3131
|
+
}
|
|
3132
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3133
|
+
const result = await client.execute({ sql, args });
|
|
3134
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3135
|
+
}
|
|
3136
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3137
|
+
try {
|
|
3138
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3139
|
+
const client = getClient2();
|
|
3140
|
+
const current = await client.execute({
|
|
3141
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3142
|
+
importance, timestamp
|
|
3143
|
+
FROM memories
|
|
3144
|
+
WHERE id = ?
|
|
3145
|
+
LIMIT 1`,
|
|
3146
|
+
args: [memoryId]
|
|
3147
|
+
});
|
|
3148
|
+
const row = current.rows[0];
|
|
3149
|
+
if (!row) return;
|
|
3150
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3151
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3152
|
+
const agentId = String(row.agent_id);
|
|
3153
|
+
const projectName = String(row.project_name);
|
|
3154
|
+
if (contentHash) {
|
|
3155
|
+
await client.execute({
|
|
3156
|
+
sql: `UPDATE memories
|
|
3157
|
+
SET status = 'deleted',
|
|
3158
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3159
|
+
WHERE id != ?
|
|
3160
|
+
AND content_hash = ?
|
|
3161
|
+
AND agent_id = ?
|
|
3162
|
+
AND project_name = ?
|
|
3163
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3164
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3165
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3166
|
+
});
|
|
3167
|
+
}
|
|
3168
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3169
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3170
|
+
const old = await client.execute({
|
|
3171
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3172
|
+
args: [supersedesId]
|
|
3173
|
+
});
|
|
3174
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3175
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3176
|
+
await client.batch([
|
|
3177
|
+
{
|
|
3178
|
+
sql: `UPDATE memories
|
|
3179
|
+
SET status = 'archived',
|
|
3180
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3181
|
+
WHERE id = ?`,
|
|
3182
|
+
args: [supersedesId]
|
|
3183
|
+
},
|
|
3184
|
+
{
|
|
3185
|
+
sql: `UPDATE memories
|
|
3186
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3187
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3188
|
+
WHERE id = ?`,
|
|
3189
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3190
|
+
}
|
|
3191
|
+
], "write");
|
|
3192
|
+
}
|
|
3193
|
+
} catch (err) {
|
|
3194
|
+
process.stderr.write(
|
|
3195
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3196
|
+
`
|
|
3197
|
+
);
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3201
|
+
if (memoryIds.length === 0) return;
|
|
3202
|
+
const run = () => {
|
|
3203
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3204
|
+
};
|
|
3205
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3206
|
+
else setTimeout(run, 0);
|
|
3207
|
+
}
|
|
3208
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3209
|
+
var init_memory_write_governor = __esm({
|
|
3210
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3211
|
+
"use strict";
|
|
3212
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3213
|
+
"decision",
|
|
3214
|
+
"adr",
|
|
3215
|
+
"behavior",
|
|
3216
|
+
"procedure"
|
|
3217
|
+
]);
|
|
3218
|
+
NOISE_DROP_PATTERNS = [
|
|
3219
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3220
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3221
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3222
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3223
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3224
|
+
];
|
|
3225
|
+
SKIP_EMBED_PATTERNS = [
|
|
3226
|
+
/tmux capture-pane\b/i,
|
|
3227
|
+
/docker ps\b/i,
|
|
3228
|
+
/docker images\b/i,
|
|
3229
|
+
/git status\b/i,
|
|
3230
|
+
/grep .*node_modules/i,
|
|
3231
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3232
|
+
];
|
|
3233
|
+
}
|
|
3234
|
+
});
|
|
3235
|
+
|
|
3038
3236
|
// src/lib/shard-manager.ts
|
|
3039
3237
|
var shard_manager_exports = {};
|
|
3040
3238
|
__export(shard_manager_exports, {
|
|
@@ -3199,7 +3397,8 @@ async function ensureShardSchema(client) {
|
|
|
3199
3397
|
}
|
|
3200
3398
|
for (const idx of [
|
|
3201
3399
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3202
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3400
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3401
|
+
"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"
|
|
3203
3402
|
]) {
|
|
3204
3403
|
try {
|
|
3205
3404
|
await client.execute(idx);
|
|
@@ -3624,7 +3823,6 @@ __export(store_exports, {
|
|
|
3624
3823
|
vectorToBlob: () => vectorToBlob,
|
|
3625
3824
|
writeMemory: () => writeMemory
|
|
3626
3825
|
});
|
|
3627
|
-
import { createHash } from "crypto";
|
|
3628
3826
|
function isBusyError2(err) {
|
|
3629
3827
|
if (err instanceof Error) {
|
|
3630
3828
|
const msg = err.message.toLowerCase();
|
|
@@ -3738,17 +3936,24 @@ async function writeMemory(record) {
|
|
|
3738
3936
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3739
3937
|
);
|
|
3740
3938
|
}
|
|
3741
|
-
const
|
|
3742
|
-
if (
|
|
3939
|
+
const governed = governMemoryRecord(record);
|
|
3940
|
+
if (governed.shouldDrop) return;
|
|
3941
|
+
record = governed.record;
|
|
3942
|
+
const contentHash = governed.contentHash;
|
|
3943
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3944
|
+
if (_pendingRecords.some(
|
|
3945
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3946
|
+
)) {
|
|
3743
3947
|
return;
|
|
3744
3948
|
}
|
|
3745
3949
|
try {
|
|
3746
|
-
const
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3950
|
+
const existing = await findScopedDuplicate({
|
|
3951
|
+
contentHash,
|
|
3952
|
+
agentId: record.agent_id,
|
|
3953
|
+
projectName: record.project_name,
|
|
3954
|
+
memoryType
|
|
3750
3955
|
});
|
|
3751
|
-
if (existing
|
|
3956
|
+
if (existing) return;
|
|
3752
3957
|
} catch {
|
|
3753
3958
|
}
|
|
3754
3959
|
const dbRow = {
|
|
@@ -3779,7 +3984,7 @@ async function writeMemory(record) {
|
|
|
3779
3984
|
tier: record.tier ?? classifyTier(record),
|
|
3780
3985
|
supersedes_id: record.supersedes_id ?? null,
|
|
3781
3986
|
draft: record.draft ? 1 : 0,
|
|
3782
|
-
memory_type:
|
|
3987
|
+
memory_type: memoryType,
|
|
3783
3988
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3784
3989
|
content_hash: contentHash,
|
|
3785
3990
|
intent: record.intent ?? null,
|
|
@@ -3938,6 +4143,7 @@ async function flushBatch() {
|
|
|
3938
4143
|
const globalClient = getClient();
|
|
3939
4144
|
const globalStmts = batch.map(buildStmt);
|
|
3940
4145
|
await globalClient.batch(globalStmts, "write");
|
|
4146
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3941
4147
|
_pendingRecords.splice(0, batch.length);
|
|
3942
4148
|
try {
|
|
3943
4149
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4196,6 +4402,7 @@ var init_store = __esm({
|
|
|
4196
4402
|
init_keychain();
|
|
4197
4403
|
init_config();
|
|
4198
4404
|
init_state_bus();
|
|
4405
|
+
init_memory_write_governor();
|
|
4199
4406
|
INIT_MAX_RETRIES = 3;
|
|
4200
4407
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4201
4408
|
_pendingRecords = [];
|
|
@@ -2585,6 +2585,14 @@ async function ensureSchema() {
|
|
|
2585
2585
|
);
|
|
2586
2586
|
} catch {
|
|
2587
2587
|
}
|
|
2588
|
+
try {
|
|
2589
|
+
await client.execute(
|
|
2590
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2591
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2592
|
+
WHERE content_hash IS NOT NULL`
|
|
2593
|
+
);
|
|
2594
|
+
} catch {
|
|
2595
|
+
}
|
|
2588
2596
|
await client.executeMultiple(`
|
|
2589
2597
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2590
2598
|
id TEXT PRIMARY KEY,
|
|
@@ -3455,6 +3463,196 @@ var init_state_bus = __esm({
|
|
|
3455
3463
|
}
|
|
3456
3464
|
});
|
|
3457
3465
|
|
|
3466
|
+
// src/lib/memory-write-governor.ts
|
|
3467
|
+
import { createHash } from "crypto";
|
|
3468
|
+
function normalizeMemoryText(text) {
|
|
3469
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3470
|
+
}
|
|
3471
|
+
function classifyMemoryType(input2) {
|
|
3472
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3473
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3474
|
+
const text = input2.raw_text.toLowerCase();
|
|
3475
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3476
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3477
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3478
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3479
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3480
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3481
|
+
return "raw";
|
|
3482
|
+
}
|
|
3483
|
+
function shouldDropMemory(text) {
|
|
3484
|
+
const normalized = normalizeMemoryText(text);
|
|
3485
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3486
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3487
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3488
|
+
}
|
|
3489
|
+
return { drop: false };
|
|
3490
|
+
}
|
|
3491
|
+
function shouldSkipEmbedding(input2) {
|
|
3492
|
+
const type = classifyMemoryType(input2);
|
|
3493
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3494
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3495
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3496
|
+
return false;
|
|
3497
|
+
}
|
|
3498
|
+
function hashMemoryContent(text) {
|
|
3499
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3500
|
+
}
|
|
3501
|
+
function scopedDedupArgs(input2) {
|
|
3502
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3503
|
+
}
|
|
3504
|
+
function governMemoryRecord(record) {
|
|
3505
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3506
|
+
const memoryType = classifyMemoryType({
|
|
3507
|
+
raw_text: normalized,
|
|
3508
|
+
agent_id: record.agent_id,
|
|
3509
|
+
project_name: record.project_name,
|
|
3510
|
+
tool_name: record.tool_name,
|
|
3511
|
+
memory_type: record.memory_type
|
|
3512
|
+
});
|
|
3513
|
+
const drop = shouldDropMemory(normalized);
|
|
3514
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3515
|
+
raw_text: normalized,
|
|
3516
|
+
agent_id: record.agent_id,
|
|
3517
|
+
project_name: record.project_name,
|
|
3518
|
+
tool_name: record.tool_name,
|
|
3519
|
+
memory_type: memoryType
|
|
3520
|
+
});
|
|
3521
|
+
return {
|
|
3522
|
+
record: {
|
|
3523
|
+
...record,
|
|
3524
|
+
raw_text: normalized,
|
|
3525
|
+
memory_type: memoryType,
|
|
3526
|
+
vector: skipEmbedding ? null : record.vector
|
|
3527
|
+
},
|
|
3528
|
+
contentHash: hashMemoryContent(normalized),
|
|
3529
|
+
shouldDrop: drop.drop,
|
|
3530
|
+
dropReason: drop.reason,
|
|
3531
|
+
skipEmbedding,
|
|
3532
|
+
hygiene: {
|
|
3533
|
+
dedup: true,
|
|
3534
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3535
|
+
}
|
|
3536
|
+
};
|
|
3537
|
+
}
|
|
3538
|
+
async function findScopedDuplicate(input2) {
|
|
3539
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3540
|
+
const client = getClient2();
|
|
3541
|
+
const args = scopedDedupArgs(input2);
|
|
3542
|
+
let sql = `SELECT id FROM memories
|
|
3543
|
+
WHERE content_hash = ?
|
|
3544
|
+
AND agent_id = ?
|
|
3545
|
+
AND project_name = ?
|
|
3546
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3547
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3548
|
+
if (input2.excludeId) {
|
|
3549
|
+
sql += " AND id != ?";
|
|
3550
|
+
args.push(input2.excludeId);
|
|
3551
|
+
}
|
|
3552
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3553
|
+
const result = await client.execute({ sql, args });
|
|
3554
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3555
|
+
}
|
|
3556
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3557
|
+
try {
|
|
3558
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3559
|
+
const client = getClient2();
|
|
3560
|
+
const current = await client.execute({
|
|
3561
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3562
|
+
importance, timestamp
|
|
3563
|
+
FROM memories
|
|
3564
|
+
WHERE id = ?
|
|
3565
|
+
LIMIT 1`,
|
|
3566
|
+
args: [memoryId]
|
|
3567
|
+
});
|
|
3568
|
+
const row = current.rows[0];
|
|
3569
|
+
if (!row) return;
|
|
3570
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3571
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3572
|
+
const agentId = String(row.agent_id);
|
|
3573
|
+
const projectName = String(row.project_name);
|
|
3574
|
+
if (contentHash) {
|
|
3575
|
+
await client.execute({
|
|
3576
|
+
sql: `UPDATE memories
|
|
3577
|
+
SET status = 'deleted',
|
|
3578
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3579
|
+
WHERE id != ?
|
|
3580
|
+
AND content_hash = ?
|
|
3581
|
+
AND agent_id = ?
|
|
3582
|
+
AND project_name = ?
|
|
3583
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3584
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3585
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3586
|
+
});
|
|
3587
|
+
}
|
|
3588
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3589
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3590
|
+
const old = await client.execute({
|
|
3591
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3592
|
+
args: [supersedesId]
|
|
3593
|
+
});
|
|
3594
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3595
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3596
|
+
await client.batch([
|
|
3597
|
+
{
|
|
3598
|
+
sql: `UPDATE memories
|
|
3599
|
+
SET status = 'archived',
|
|
3600
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3601
|
+
WHERE id = ?`,
|
|
3602
|
+
args: [supersedesId]
|
|
3603
|
+
},
|
|
3604
|
+
{
|
|
3605
|
+
sql: `UPDATE memories
|
|
3606
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3607
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3608
|
+
WHERE id = ?`,
|
|
3609
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3610
|
+
}
|
|
3611
|
+
], "write");
|
|
3612
|
+
}
|
|
3613
|
+
} catch (err) {
|
|
3614
|
+
process.stderr.write(
|
|
3615
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3616
|
+
`
|
|
3617
|
+
);
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3621
|
+
if (memoryIds.length === 0) return;
|
|
3622
|
+
const run = () => {
|
|
3623
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3624
|
+
};
|
|
3625
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3626
|
+
else setTimeout(run, 0);
|
|
3627
|
+
}
|
|
3628
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3629
|
+
var init_memory_write_governor = __esm({
|
|
3630
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3631
|
+
"use strict";
|
|
3632
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3633
|
+
"decision",
|
|
3634
|
+
"adr",
|
|
3635
|
+
"behavior",
|
|
3636
|
+
"procedure"
|
|
3637
|
+
]);
|
|
3638
|
+
NOISE_DROP_PATTERNS = [
|
|
3639
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3640
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3641
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3642
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3643
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3644
|
+
];
|
|
3645
|
+
SKIP_EMBED_PATTERNS = [
|
|
3646
|
+
/tmux capture-pane\b/i,
|
|
3647
|
+
/docker ps\b/i,
|
|
3648
|
+
/docker images\b/i,
|
|
3649
|
+
/git status\b/i,
|
|
3650
|
+
/grep .*node_modules/i,
|
|
3651
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3652
|
+
];
|
|
3653
|
+
}
|
|
3654
|
+
});
|
|
3655
|
+
|
|
3458
3656
|
// src/lib/shard-manager.ts
|
|
3459
3657
|
var shard_manager_exports = {};
|
|
3460
3658
|
__export(shard_manager_exports, {
|
|
@@ -3619,7 +3817,8 @@ async function ensureShardSchema(client) {
|
|
|
3619
3817
|
}
|
|
3620
3818
|
for (const idx of [
|
|
3621
3819
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3622
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3820
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3821
|
+
"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"
|
|
3623
3822
|
]) {
|
|
3624
3823
|
try {
|
|
3625
3824
|
await client.execute(idx);
|
|
@@ -4044,7 +4243,6 @@ __export(store_exports, {
|
|
|
4044
4243
|
vectorToBlob: () => vectorToBlob,
|
|
4045
4244
|
writeMemory: () => writeMemory
|
|
4046
4245
|
});
|
|
4047
|
-
import { createHash } from "crypto";
|
|
4048
4246
|
function isBusyError2(err) {
|
|
4049
4247
|
if (err instanceof Error) {
|
|
4050
4248
|
const msg = err.message.toLowerCase();
|
|
@@ -4158,17 +4356,24 @@ async function writeMemory(record) {
|
|
|
4158
4356
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4159
4357
|
);
|
|
4160
4358
|
}
|
|
4161
|
-
const
|
|
4162
|
-
if (
|
|
4359
|
+
const governed = governMemoryRecord(record);
|
|
4360
|
+
if (governed.shouldDrop) return;
|
|
4361
|
+
record = governed.record;
|
|
4362
|
+
const contentHash = governed.contentHash;
|
|
4363
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4364
|
+
if (_pendingRecords.some(
|
|
4365
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4366
|
+
)) {
|
|
4163
4367
|
return;
|
|
4164
4368
|
}
|
|
4165
4369
|
try {
|
|
4166
|
-
const
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4370
|
+
const existing = await findScopedDuplicate({
|
|
4371
|
+
contentHash,
|
|
4372
|
+
agentId: record.agent_id,
|
|
4373
|
+
projectName: record.project_name,
|
|
4374
|
+
memoryType
|
|
4170
4375
|
});
|
|
4171
|
-
if (existing
|
|
4376
|
+
if (existing) return;
|
|
4172
4377
|
} catch {
|
|
4173
4378
|
}
|
|
4174
4379
|
const dbRow = {
|
|
@@ -4199,7 +4404,7 @@ async function writeMemory(record) {
|
|
|
4199
4404
|
tier: record.tier ?? classifyTier(record),
|
|
4200
4405
|
supersedes_id: record.supersedes_id ?? null,
|
|
4201
4406
|
draft: record.draft ? 1 : 0,
|
|
4202
|
-
memory_type:
|
|
4407
|
+
memory_type: memoryType,
|
|
4203
4408
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4204
4409
|
content_hash: contentHash,
|
|
4205
4410
|
intent: record.intent ?? null,
|
|
@@ -4358,6 +4563,7 @@ async function flushBatch() {
|
|
|
4358
4563
|
const globalClient = getClient();
|
|
4359
4564
|
const globalStmts = batch.map(buildStmt);
|
|
4360
4565
|
await globalClient.batch(globalStmts, "write");
|
|
4566
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4361
4567
|
_pendingRecords.splice(0, batch.length);
|
|
4362
4568
|
try {
|
|
4363
4569
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4616,6 +4822,7 @@ var init_store = __esm({
|
|
|
4616
4822
|
init_keychain();
|
|
4617
4823
|
init_config();
|
|
4618
4824
|
init_state_bus();
|
|
4825
|
+
init_memory_write_governor();
|
|
4619
4826
|
INIT_MAX_RETRIES = 3;
|
|
4620
4827
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4621
4828
|
_pendingRecords = [];
|