@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
|
@@ -2288,6 +2288,14 @@ async function ensureSchema() {
|
|
|
2288
2288
|
);
|
|
2289
2289
|
} catch {
|
|
2290
2290
|
}
|
|
2291
|
+
try {
|
|
2292
|
+
await client.execute(
|
|
2293
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2294
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2295
|
+
WHERE content_hash IS NOT NULL`
|
|
2296
|
+
);
|
|
2297
|
+
} catch {
|
|
2298
|
+
}
|
|
2291
2299
|
await client.executeMultiple(`
|
|
2292
2300
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2293
2301
|
id TEXT PRIMARY KEY,
|
|
@@ -3033,6 +3041,196 @@ var init_state_bus = __esm({
|
|
|
3033
3041
|
}
|
|
3034
3042
|
});
|
|
3035
3043
|
|
|
3044
|
+
// src/lib/memory-write-governor.ts
|
|
3045
|
+
import { createHash } from "crypto";
|
|
3046
|
+
function normalizeMemoryText(text) {
|
|
3047
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3048
|
+
}
|
|
3049
|
+
function classifyMemoryType(input) {
|
|
3050
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3051
|
+
const tool = input.tool_name.toLowerCase();
|
|
3052
|
+
const text = input.raw_text.toLowerCase();
|
|
3053
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3054
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3055
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3056
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3057
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3058
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3059
|
+
return "raw";
|
|
3060
|
+
}
|
|
3061
|
+
function shouldDropMemory(text) {
|
|
3062
|
+
const normalized = normalizeMemoryText(text);
|
|
3063
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3064
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3065
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3066
|
+
}
|
|
3067
|
+
return { drop: false };
|
|
3068
|
+
}
|
|
3069
|
+
function shouldSkipEmbedding(input) {
|
|
3070
|
+
const type = classifyMemoryType(input);
|
|
3071
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3072
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3073
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3074
|
+
return false;
|
|
3075
|
+
}
|
|
3076
|
+
function hashMemoryContent(text) {
|
|
3077
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3078
|
+
}
|
|
3079
|
+
function scopedDedupArgs(input) {
|
|
3080
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3081
|
+
}
|
|
3082
|
+
function governMemoryRecord(record) {
|
|
3083
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3084
|
+
const memoryType = classifyMemoryType({
|
|
3085
|
+
raw_text: normalized,
|
|
3086
|
+
agent_id: record.agent_id,
|
|
3087
|
+
project_name: record.project_name,
|
|
3088
|
+
tool_name: record.tool_name,
|
|
3089
|
+
memory_type: record.memory_type
|
|
3090
|
+
});
|
|
3091
|
+
const drop = shouldDropMemory(normalized);
|
|
3092
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3093
|
+
raw_text: normalized,
|
|
3094
|
+
agent_id: record.agent_id,
|
|
3095
|
+
project_name: record.project_name,
|
|
3096
|
+
tool_name: record.tool_name,
|
|
3097
|
+
memory_type: memoryType
|
|
3098
|
+
});
|
|
3099
|
+
return {
|
|
3100
|
+
record: {
|
|
3101
|
+
...record,
|
|
3102
|
+
raw_text: normalized,
|
|
3103
|
+
memory_type: memoryType,
|
|
3104
|
+
vector: skipEmbedding ? null : record.vector
|
|
3105
|
+
},
|
|
3106
|
+
contentHash: hashMemoryContent(normalized),
|
|
3107
|
+
shouldDrop: drop.drop,
|
|
3108
|
+
dropReason: drop.reason,
|
|
3109
|
+
skipEmbedding,
|
|
3110
|
+
hygiene: {
|
|
3111
|
+
dedup: true,
|
|
3112
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3113
|
+
}
|
|
3114
|
+
};
|
|
3115
|
+
}
|
|
3116
|
+
async function findScopedDuplicate(input) {
|
|
3117
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3118
|
+
const client = getClient2();
|
|
3119
|
+
const args = scopedDedupArgs(input);
|
|
3120
|
+
let sql = `SELECT id FROM memories
|
|
3121
|
+
WHERE content_hash = ?
|
|
3122
|
+
AND agent_id = ?
|
|
3123
|
+
AND project_name = ?
|
|
3124
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3125
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3126
|
+
if (input.excludeId) {
|
|
3127
|
+
sql += " AND id != ?";
|
|
3128
|
+
args.push(input.excludeId);
|
|
3129
|
+
}
|
|
3130
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3131
|
+
const result = await client.execute({ sql, args });
|
|
3132
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3133
|
+
}
|
|
3134
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3135
|
+
try {
|
|
3136
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3137
|
+
const client = getClient2();
|
|
3138
|
+
const current = await client.execute({
|
|
3139
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3140
|
+
importance, timestamp
|
|
3141
|
+
FROM memories
|
|
3142
|
+
WHERE id = ?
|
|
3143
|
+
LIMIT 1`,
|
|
3144
|
+
args: [memoryId]
|
|
3145
|
+
});
|
|
3146
|
+
const row = current.rows[0];
|
|
3147
|
+
if (!row) return;
|
|
3148
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3149
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3150
|
+
const agentId2 = String(row.agent_id);
|
|
3151
|
+
const projectName2 = String(row.project_name);
|
|
3152
|
+
if (contentHash) {
|
|
3153
|
+
await client.execute({
|
|
3154
|
+
sql: `UPDATE memories
|
|
3155
|
+
SET status = 'deleted',
|
|
3156
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3157
|
+
WHERE id != ?
|
|
3158
|
+
AND content_hash = ?
|
|
3159
|
+
AND agent_id = ?
|
|
3160
|
+
AND project_name = ?
|
|
3161
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3162
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3163
|
+
args: [memoryId, contentHash, agentId2, projectName2, memoryType]
|
|
3164
|
+
});
|
|
3165
|
+
}
|
|
3166
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3167
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3168
|
+
const old = await client.execute({
|
|
3169
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3170
|
+
args: [supersedesId]
|
|
3171
|
+
});
|
|
3172
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3173
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3174
|
+
await client.batch([
|
|
3175
|
+
{
|
|
3176
|
+
sql: `UPDATE memories
|
|
3177
|
+
SET status = 'archived',
|
|
3178
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3179
|
+
WHERE id = ?`,
|
|
3180
|
+
args: [supersedesId]
|
|
3181
|
+
},
|
|
3182
|
+
{
|
|
3183
|
+
sql: `UPDATE memories
|
|
3184
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3185
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3186
|
+
WHERE id = ?`,
|
|
3187
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3188
|
+
}
|
|
3189
|
+
], "write");
|
|
3190
|
+
}
|
|
3191
|
+
} catch (err) {
|
|
3192
|
+
process.stderr.write(
|
|
3193
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3194
|
+
`
|
|
3195
|
+
);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3199
|
+
if (memoryIds.length === 0) return;
|
|
3200
|
+
const run = () => {
|
|
3201
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3202
|
+
};
|
|
3203
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3204
|
+
else setTimeout(run, 0);
|
|
3205
|
+
}
|
|
3206
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3207
|
+
var init_memory_write_governor = __esm({
|
|
3208
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3209
|
+
"use strict";
|
|
3210
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3211
|
+
"decision",
|
|
3212
|
+
"adr",
|
|
3213
|
+
"behavior",
|
|
3214
|
+
"procedure"
|
|
3215
|
+
]);
|
|
3216
|
+
NOISE_DROP_PATTERNS = [
|
|
3217
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3218
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3219
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3220
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3221
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3222
|
+
];
|
|
3223
|
+
SKIP_EMBED_PATTERNS = [
|
|
3224
|
+
/tmux capture-pane\b/i,
|
|
3225
|
+
/docker ps\b/i,
|
|
3226
|
+
/docker images\b/i,
|
|
3227
|
+
/git status\b/i,
|
|
3228
|
+
/grep .*node_modules/i,
|
|
3229
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3230
|
+
];
|
|
3231
|
+
}
|
|
3232
|
+
});
|
|
3233
|
+
|
|
3036
3234
|
// src/lib/shard-manager.ts
|
|
3037
3235
|
var shard_manager_exports = {};
|
|
3038
3236
|
__export(shard_manager_exports, {
|
|
@@ -3197,7 +3395,8 @@ async function ensureShardSchema(client) {
|
|
|
3197
3395
|
}
|
|
3198
3396
|
for (const idx of [
|
|
3199
3397
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3200
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3398
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3399
|
+
"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"
|
|
3201
3400
|
]) {
|
|
3202
3401
|
try {
|
|
3203
3402
|
await client.execute(idx);
|
|
@@ -3622,7 +3821,6 @@ __export(store_exports, {
|
|
|
3622
3821
|
vectorToBlob: () => vectorToBlob,
|
|
3623
3822
|
writeMemory: () => writeMemory
|
|
3624
3823
|
});
|
|
3625
|
-
import { createHash } from "crypto";
|
|
3626
3824
|
function isBusyError2(err) {
|
|
3627
3825
|
if (err instanceof Error) {
|
|
3628
3826
|
const msg = err.message.toLowerCase();
|
|
@@ -3736,17 +3934,24 @@ async function writeMemory(record) {
|
|
|
3736
3934
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3737
3935
|
);
|
|
3738
3936
|
}
|
|
3739
|
-
const
|
|
3740
|
-
if (
|
|
3937
|
+
const governed = governMemoryRecord(record);
|
|
3938
|
+
if (governed.shouldDrop) return;
|
|
3939
|
+
record = governed.record;
|
|
3940
|
+
const contentHash = governed.contentHash;
|
|
3941
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3942
|
+
if (_pendingRecords.some(
|
|
3943
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3944
|
+
)) {
|
|
3741
3945
|
return;
|
|
3742
3946
|
}
|
|
3743
3947
|
try {
|
|
3744
|
-
const
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3948
|
+
const existing = await findScopedDuplicate({
|
|
3949
|
+
contentHash,
|
|
3950
|
+
agentId: record.agent_id,
|
|
3951
|
+
projectName: record.project_name,
|
|
3952
|
+
memoryType
|
|
3748
3953
|
});
|
|
3749
|
-
if (existing
|
|
3954
|
+
if (existing) return;
|
|
3750
3955
|
} catch {
|
|
3751
3956
|
}
|
|
3752
3957
|
const dbRow = {
|
|
@@ -3777,7 +3982,7 @@ async function writeMemory(record) {
|
|
|
3777
3982
|
tier: record.tier ?? classifyTier(record),
|
|
3778
3983
|
supersedes_id: record.supersedes_id ?? null,
|
|
3779
3984
|
draft: record.draft ? 1 : 0,
|
|
3780
|
-
memory_type:
|
|
3985
|
+
memory_type: memoryType,
|
|
3781
3986
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3782
3987
|
content_hash: contentHash,
|
|
3783
3988
|
intent: record.intent ?? null,
|
|
@@ -3936,6 +4141,7 @@ async function flushBatch() {
|
|
|
3936
4141
|
const globalClient = getClient();
|
|
3937
4142
|
const globalStmts = batch.map(buildStmt);
|
|
3938
4143
|
await globalClient.batch(globalStmts, "write");
|
|
4144
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3939
4145
|
_pendingRecords.splice(0, batch.length);
|
|
3940
4146
|
try {
|
|
3941
4147
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4194,6 +4400,7 @@ var init_store = __esm({
|
|
|
4194
4400
|
init_keychain();
|
|
4195
4401
|
init_config();
|
|
4196
4402
|
init_state_bus();
|
|
4403
|
+
init_memory_write_governor();
|
|
4197
4404
|
INIT_MAX_RETRIES = 3;
|
|
4198
4405
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4199
4406
|
_pendingRecords = [];
|
package/dist/bin/exe-forget.js
CHANGED
|
@@ -2288,6 +2288,14 @@ async function ensureSchema() {
|
|
|
2288
2288
|
);
|
|
2289
2289
|
} catch {
|
|
2290
2290
|
}
|
|
2291
|
+
try {
|
|
2292
|
+
await client.execute(
|
|
2293
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2294
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2295
|
+
WHERE content_hash IS NOT NULL`
|
|
2296
|
+
);
|
|
2297
|
+
} catch {
|
|
2298
|
+
}
|
|
2291
2299
|
await client.executeMultiple(`
|
|
2292
2300
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2293
2301
|
id TEXT PRIMARY KEY,
|
|
@@ -3033,6 +3041,196 @@ var init_state_bus = __esm({
|
|
|
3033
3041
|
}
|
|
3034
3042
|
});
|
|
3035
3043
|
|
|
3044
|
+
// src/lib/memory-write-governor.ts
|
|
3045
|
+
import { createHash } from "crypto";
|
|
3046
|
+
function normalizeMemoryText(text) {
|
|
3047
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3048
|
+
}
|
|
3049
|
+
function classifyMemoryType(input) {
|
|
3050
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3051
|
+
const tool = input.tool_name.toLowerCase();
|
|
3052
|
+
const text = input.raw_text.toLowerCase();
|
|
3053
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3054
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3055
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3056
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3057
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3058
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3059
|
+
return "raw";
|
|
3060
|
+
}
|
|
3061
|
+
function shouldDropMemory(text) {
|
|
3062
|
+
const normalized = normalizeMemoryText(text);
|
|
3063
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3064
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3065
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3066
|
+
}
|
|
3067
|
+
return { drop: false };
|
|
3068
|
+
}
|
|
3069
|
+
function shouldSkipEmbedding(input) {
|
|
3070
|
+
const type = classifyMemoryType(input);
|
|
3071
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3072
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3073
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3074
|
+
return false;
|
|
3075
|
+
}
|
|
3076
|
+
function hashMemoryContent(text) {
|
|
3077
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3078
|
+
}
|
|
3079
|
+
function scopedDedupArgs(input) {
|
|
3080
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3081
|
+
}
|
|
3082
|
+
function governMemoryRecord(record) {
|
|
3083
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3084
|
+
const memoryType = classifyMemoryType({
|
|
3085
|
+
raw_text: normalized,
|
|
3086
|
+
agent_id: record.agent_id,
|
|
3087
|
+
project_name: record.project_name,
|
|
3088
|
+
tool_name: record.tool_name,
|
|
3089
|
+
memory_type: record.memory_type
|
|
3090
|
+
});
|
|
3091
|
+
const drop = shouldDropMemory(normalized);
|
|
3092
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3093
|
+
raw_text: normalized,
|
|
3094
|
+
agent_id: record.agent_id,
|
|
3095
|
+
project_name: record.project_name,
|
|
3096
|
+
tool_name: record.tool_name,
|
|
3097
|
+
memory_type: memoryType
|
|
3098
|
+
});
|
|
3099
|
+
return {
|
|
3100
|
+
record: {
|
|
3101
|
+
...record,
|
|
3102
|
+
raw_text: normalized,
|
|
3103
|
+
memory_type: memoryType,
|
|
3104
|
+
vector: skipEmbedding ? null : record.vector
|
|
3105
|
+
},
|
|
3106
|
+
contentHash: hashMemoryContent(normalized),
|
|
3107
|
+
shouldDrop: drop.drop,
|
|
3108
|
+
dropReason: drop.reason,
|
|
3109
|
+
skipEmbedding,
|
|
3110
|
+
hygiene: {
|
|
3111
|
+
dedup: true,
|
|
3112
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3113
|
+
}
|
|
3114
|
+
};
|
|
3115
|
+
}
|
|
3116
|
+
async function findScopedDuplicate(input) {
|
|
3117
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3118
|
+
const client = getClient2();
|
|
3119
|
+
const args = scopedDedupArgs(input);
|
|
3120
|
+
let sql = `SELECT id FROM memories
|
|
3121
|
+
WHERE content_hash = ?
|
|
3122
|
+
AND agent_id = ?
|
|
3123
|
+
AND project_name = ?
|
|
3124
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3125
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3126
|
+
if (input.excludeId) {
|
|
3127
|
+
sql += " AND id != ?";
|
|
3128
|
+
args.push(input.excludeId);
|
|
3129
|
+
}
|
|
3130
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3131
|
+
const result = await client.execute({ sql, args });
|
|
3132
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3133
|
+
}
|
|
3134
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3135
|
+
try {
|
|
3136
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3137
|
+
const client = getClient2();
|
|
3138
|
+
const current = await client.execute({
|
|
3139
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3140
|
+
importance, timestamp
|
|
3141
|
+
FROM memories
|
|
3142
|
+
WHERE id = ?
|
|
3143
|
+
LIMIT 1`,
|
|
3144
|
+
args: [memoryId]
|
|
3145
|
+
});
|
|
3146
|
+
const row = current.rows[0];
|
|
3147
|
+
if (!row) return;
|
|
3148
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3149
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3150
|
+
const agentId = String(row.agent_id);
|
|
3151
|
+
const projectName = String(row.project_name);
|
|
3152
|
+
if (contentHash) {
|
|
3153
|
+
await client.execute({
|
|
3154
|
+
sql: `UPDATE memories
|
|
3155
|
+
SET status = 'deleted',
|
|
3156
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3157
|
+
WHERE id != ?
|
|
3158
|
+
AND content_hash = ?
|
|
3159
|
+
AND agent_id = ?
|
|
3160
|
+
AND project_name = ?
|
|
3161
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3162
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3163
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3164
|
+
});
|
|
3165
|
+
}
|
|
3166
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3167
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3168
|
+
const old = await client.execute({
|
|
3169
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3170
|
+
args: [supersedesId]
|
|
3171
|
+
});
|
|
3172
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3173
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3174
|
+
await client.batch([
|
|
3175
|
+
{
|
|
3176
|
+
sql: `UPDATE memories
|
|
3177
|
+
SET status = 'archived',
|
|
3178
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3179
|
+
WHERE id = ?`,
|
|
3180
|
+
args: [supersedesId]
|
|
3181
|
+
},
|
|
3182
|
+
{
|
|
3183
|
+
sql: `UPDATE memories
|
|
3184
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3185
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3186
|
+
WHERE id = ?`,
|
|
3187
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3188
|
+
}
|
|
3189
|
+
], "write");
|
|
3190
|
+
}
|
|
3191
|
+
} catch (err) {
|
|
3192
|
+
process.stderr.write(
|
|
3193
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3194
|
+
`
|
|
3195
|
+
);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3199
|
+
if (memoryIds.length === 0) return;
|
|
3200
|
+
const run = () => {
|
|
3201
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3202
|
+
};
|
|
3203
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3204
|
+
else setTimeout(run, 0);
|
|
3205
|
+
}
|
|
3206
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3207
|
+
var init_memory_write_governor = __esm({
|
|
3208
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3209
|
+
"use strict";
|
|
3210
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3211
|
+
"decision",
|
|
3212
|
+
"adr",
|
|
3213
|
+
"behavior",
|
|
3214
|
+
"procedure"
|
|
3215
|
+
]);
|
|
3216
|
+
NOISE_DROP_PATTERNS = [
|
|
3217
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3218
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3219
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3220
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3221
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3222
|
+
];
|
|
3223
|
+
SKIP_EMBED_PATTERNS = [
|
|
3224
|
+
/tmux capture-pane\b/i,
|
|
3225
|
+
/docker ps\b/i,
|
|
3226
|
+
/docker images\b/i,
|
|
3227
|
+
/git status\b/i,
|
|
3228
|
+
/grep .*node_modules/i,
|
|
3229
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3230
|
+
];
|
|
3231
|
+
}
|
|
3232
|
+
});
|
|
3233
|
+
|
|
3036
3234
|
// src/lib/shard-manager.ts
|
|
3037
3235
|
var shard_manager_exports = {};
|
|
3038
3236
|
__export(shard_manager_exports, {
|
|
@@ -3197,7 +3395,8 @@ async function ensureShardSchema(client) {
|
|
|
3197
3395
|
}
|
|
3198
3396
|
for (const idx of [
|
|
3199
3397
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3200
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3398
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3399
|
+
"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"
|
|
3201
3400
|
]) {
|
|
3202
3401
|
try {
|
|
3203
3402
|
await client.execute(idx);
|
|
@@ -3622,7 +3821,6 @@ __export(store_exports, {
|
|
|
3622
3821
|
vectorToBlob: () => vectorToBlob,
|
|
3623
3822
|
writeMemory: () => writeMemory
|
|
3624
3823
|
});
|
|
3625
|
-
import { createHash } from "crypto";
|
|
3626
3824
|
function isBusyError2(err) {
|
|
3627
3825
|
if (err instanceof Error) {
|
|
3628
3826
|
const msg = err.message.toLowerCase();
|
|
@@ -3736,17 +3934,24 @@ async function writeMemory(record) {
|
|
|
3736
3934
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3737
3935
|
);
|
|
3738
3936
|
}
|
|
3739
|
-
const
|
|
3740
|
-
if (
|
|
3937
|
+
const governed = governMemoryRecord(record);
|
|
3938
|
+
if (governed.shouldDrop) return;
|
|
3939
|
+
record = governed.record;
|
|
3940
|
+
const contentHash = governed.contentHash;
|
|
3941
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3942
|
+
if (_pendingRecords.some(
|
|
3943
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3944
|
+
)) {
|
|
3741
3945
|
return;
|
|
3742
3946
|
}
|
|
3743
3947
|
try {
|
|
3744
|
-
const
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3948
|
+
const existing = await findScopedDuplicate({
|
|
3949
|
+
contentHash,
|
|
3950
|
+
agentId: record.agent_id,
|
|
3951
|
+
projectName: record.project_name,
|
|
3952
|
+
memoryType
|
|
3748
3953
|
});
|
|
3749
|
-
if (existing
|
|
3954
|
+
if (existing) return;
|
|
3750
3955
|
} catch {
|
|
3751
3956
|
}
|
|
3752
3957
|
const dbRow = {
|
|
@@ -3777,7 +3982,7 @@ async function writeMemory(record) {
|
|
|
3777
3982
|
tier: record.tier ?? classifyTier(record),
|
|
3778
3983
|
supersedes_id: record.supersedes_id ?? null,
|
|
3779
3984
|
draft: record.draft ? 1 : 0,
|
|
3780
|
-
memory_type:
|
|
3985
|
+
memory_type: memoryType,
|
|
3781
3986
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3782
3987
|
content_hash: contentHash,
|
|
3783
3988
|
intent: record.intent ?? null,
|
|
@@ -3936,6 +4141,7 @@ async function flushBatch() {
|
|
|
3936
4141
|
const globalClient = getClient();
|
|
3937
4142
|
const globalStmts = batch.map(buildStmt);
|
|
3938
4143
|
await globalClient.batch(globalStmts, "write");
|
|
4144
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3939
4145
|
_pendingRecords.splice(0, batch.length);
|
|
3940
4146
|
try {
|
|
3941
4147
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4194,6 +4400,7 @@ var init_store = __esm({
|
|
|
4194
4400
|
init_keychain();
|
|
4195
4401
|
init_config();
|
|
4196
4402
|
init_state_bus();
|
|
4403
|
+
init_memory_write_governor();
|
|
4197
4404
|
INIT_MAX_RETRIES = 3;
|
|
4198
4405
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4199
4406
|
_pendingRecords = [];
|