@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/ingest.js
CHANGED
|
@@ -2455,6 +2455,14 @@ async function ensureSchema() {
|
|
|
2455
2455
|
);
|
|
2456
2456
|
} catch {
|
|
2457
2457
|
}
|
|
2458
|
+
try {
|
|
2459
|
+
await client.execute(
|
|
2460
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2461
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2462
|
+
WHERE content_hash IS NOT NULL`
|
|
2463
|
+
);
|
|
2464
|
+
} catch {
|
|
2465
|
+
}
|
|
2458
2466
|
await client.executeMultiple(`
|
|
2459
2467
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2460
2468
|
id TEXT PRIMARY KEY,
|
|
@@ -3200,6 +3208,196 @@ var init_state_bus = __esm({
|
|
|
3200
3208
|
}
|
|
3201
3209
|
});
|
|
3202
3210
|
|
|
3211
|
+
// src/lib/memory-write-governor.ts
|
|
3212
|
+
import { createHash } from "crypto";
|
|
3213
|
+
function normalizeMemoryText(text) {
|
|
3214
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3215
|
+
}
|
|
3216
|
+
function classifyMemoryType(input2) {
|
|
3217
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3218
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3219
|
+
const text = input2.raw_text.toLowerCase();
|
|
3220
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3221
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3222
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3223
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3224
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3225
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3226
|
+
return "raw";
|
|
3227
|
+
}
|
|
3228
|
+
function shouldDropMemory(text) {
|
|
3229
|
+
const normalized = normalizeMemoryText(text);
|
|
3230
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3231
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3232
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3233
|
+
}
|
|
3234
|
+
return { drop: false };
|
|
3235
|
+
}
|
|
3236
|
+
function shouldSkipEmbedding(input2) {
|
|
3237
|
+
const type = classifyMemoryType(input2);
|
|
3238
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3239
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3240
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3241
|
+
return false;
|
|
3242
|
+
}
|
|
3243
|
+
function hashMemoryContent(text) {
|
|
3244
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3245
|
+
}
|
|
3246
|
+
function scopedDedupArgs(input2) {
|
|
3247
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3248
|
+
}
|
|
3249
|
+
function governMemoryRecord(record) {
|
|
3250
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3251
|
+
const memoryType = classifyMemoryType({
|
|
3252
|
+
raw_text: normalized,
|
|
3253
|
+
agent_id: record.agent_id,
|
|
3254
|
+
project_name: record.project_name,
|
|
3255
|
+
tool_name: record.tool_name,
|
|
3256
|
+
memory_type: record.memory_type
|
|
3257
|
+
});
|
|
3258
|
+
const drop = shouldDropMemory(normalized);
|
|
3259
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3260
|
+
raw_text: normalized,
|
|
3261
|
+
agent_id: record.agent_id,
|
|
3262
|
+
project_name: record.project_name,
|
|
3263
|
+
tool_name: record.tool_name,
|
|
3264
|
+
memory_type: memoryType
|
|
3265
|
+
});
|
|
3266
|
+
return {
|
|
3267
|
+
record: {
|
|
3268
|
+
...record,
|
|
3269
|
+
raw_text: normalized,
|
|
3270
|
+
memory_type: memoryType,
|
|
3271
|
+
vector: skipEmbedding ? null : record.vector
|
|
3272
|
+
},
|
|
3273
|
+
contentHash: hashMemoryContent(normalized),
|
|
3274
|
+
shouldDrop: drop.drop,
|
|
3275
|
+
dropReason: drop.reason,
|
|
3276
|
+
skipEmbedding,
|
|
3277
|
+
hygiene: {
|
|
3278
|
+
dedup: true,
|
|
3279
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3280
|
+
}
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
async function findScopedDuplicate(input2) {
|
|
3284
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3285
|
+
const client = getClient2();
|
|
3286
|
+
const args = scopedDedupArgs(input2);
|
|
3287
|
+
let sql = `SELECT id FROM memories
|
|
3288
|
+
WHERE content_hash = ?
|
|
3289
|
+
AND agent_id = ?
|
|
3290
|
+
AND project_name = ?
|
|
3291
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3292
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3293
|
+
if (input2.excludeId) {
|
|
3294
|
+
sql += " AND id != ?";
|
|
3295
|
+
args.push(input2.excludeId);
|
|
3296
|
+
}
|
|
3297
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3298
|
+
const result = await client.execute({ sql, args });
|
|
3299
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3300
|
+
}
|
|
3301
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3302
|
+
try {
|
|
3303
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3304
|
+
const client = getClient2();
|
|
3305
|
+
const current = await client.execute({
|
|
3306
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3307
|
+
importance, timestamp
|
|
3308
|
+
FROM memories
|
|
3309
|
+
WHERE id = ?
|
|
3310
|
+
LIMIT 1`,
|
|
3311
|
+
args: [memoryId]
|
|
3312
|
+
});
|
|
3313
|
+
const row = current.rows[0];
|
|
3314
|
+
if (!row) return;
|
|
3315
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3316
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3317
|
+
const agentId = String(row.agent_id);
|
|
3318
|
+
const projectName = String(row.project_name);
|
|
3319
|
+
if (contentHash) {
|
|
3320
|
+
await client.execute({
|
|
3321
|
+
sql: `UPDATE memories
|
|
3322
|
+
SET status = 'deleted',
|
|
3323
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3324
|
+
WHERE id != ?
|
|
3325
|
+
AND content_hash = ?
|
|
3326
|
+
AND agent_id = ?
|
|
3327
|
+
AND project_name = ?
|
|
3328
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3329
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3330
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3331
|
+
});
|
|
3332
|
+
}
|
|
3333
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3334
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3335
|
+
const old = await client.execute({
|
|
3336
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3337
|
+
args: [supersedesId]
|
|
3338
|
+
});
|
|
3339
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3340
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3341
|
+
await client.batch([
|
|
3342
|
+
{
|
|
3343
|
+
sql: `UPDATE memories
|
|
3344
|
+
SET status = 'archived',
|
|
3345
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3346
|
+
WHERE id = ?`,
|
|
3347
|
+
args: [supersedesId]
|
|
3348
|
+
},
|
|
3349
|
+
{
|
|
3350
|
+
sql: `UPDATE memories
|
|
3351
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3352
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3353
|
+
WHERE id = ?`,
|
|
3354
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3355
|
+
}
|
|
3356
|
+
], "write");
|
|
3357
|
+
}
|
|
3358
|
+
} catch (err) {
|
|
3359
|
+
process.stderr.write(
|
|
3360
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3361
|
+
`
|
|
3362
|
+
);
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3366
|
+
if (memoryIds.length === 0) return;
|
|
3367
|
+
const run = () => {
|
|
3368
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3369
|
+
};
|
|
3370
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3371
|
+
else setTimeout(run, 0);
|
|
3372
|
+
}
|
|
3373
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3374
|
+
var init_memory_write_governor = __esm({
|
|
3375
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3376
|
+
"use strict";
|
|
3377
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3378
|
+
"decision",
|
|
3379
|
+
"adr",
|
|
3380
|
+
"behavior",
|
|
3381
|
+
"procedure"
|
|
3382
|
+
]);
|
|
3383
|
+
NOISE_DROP_PATTERNS = [
|
|
3384
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3385
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3386
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3387
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3388
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3389
|
+
];
|
|
3390
|
+
SKIP_EMBED_PATTERNS = [
|
|
3391
|
+
/tmux capture-pane\b/i,
|
|
3392
|
+
/docker ps\b/i,
|
|
3393
|
+
/docker images\b/i,
|
|
3394
|
+
/git status\b/i,
|
|
3395
|
+
/grep .*node_modules/i,
|
|
3396
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3397
|
+
];
|
|
3398
|
+
}
|
|
3399
|
+
});
|
|
3400
|
+
|
|
3203
3401
|
// src/lib/shard-manager.ts
|
|
3204
3402
|
var shard_manager_exports = {};
|
|
3205
3403
|
__export(shard_manager_exports, {
|
|
@@ -3364,7 +3562,8 @@ async function ensureShardSchema(client) {
|
|
|
3364
3562
|
}
|
|
3365
3563
|
for (const idx of [
|
|
3366
3564
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3367
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3565
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3566
|
+
"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"
|
|
3368
3567
|
]) {
|
|
3369
3568
|
try {
|
|
3370
3569
|
await client.execute(idx);
|
|
@@ -3789,7 +3988,6 @@ __export(store_exports, {
|
|
|
3789
3988
|
vectorToBlob: () => vectorToBlob,
|
|
3790
3989
|
writeMemory: () => writeMemory
|
|
3791
3990
|
});
|
|
3792
|
-
import { createHash } from "crypto";
|
|
3793
3991
|
function isBusyError2(err) {
|
|
3794
3992
|
if (err instanceof Error) {
|
|
3795
3993
|
const msg = err.message.toLowerCase();
|
|
@@ -3903,17 +4101,24 @@ async function writeMemory(record) {
|
|
|
3903
4101
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3904
4102
|
);
|
|
3905
4103
|
}
|
|
3906
|
-
const
|
|
3907
|
-
if (
|
|
4104
|
+
const governed = governMemoryRecord(record);
|
|
4105
|
+
if (governed.shouldDrop) return;
|
|
4106
|
+
record = governed.record;
|
|
4107
|
+
const contentHash = governed.contentHash;
|
|
4108
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4109
|
+
if (_pendingRecords.some(
|
|
4110
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4111
|
+
)) {
|
|
3908
4112
|
return;
|
|
3909
4113
|
}
|
|
3910
4114
|
try {
|
|
3911
|
-
const
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
4115
|
+
const existing = await findScopedDuplicate({
|
|
4116
|
+
contentHash,
|
|
4117
|
+
agentId: record.agent_id,
|
|
4118
|
+
projectName: record.project_name,
|
|
4119
|
+
memoryType
|
|
3915
4120
|
});
|
|
3916
|
-
if (existing
|
|
4121
|
+
if (existing) return;
|
|
3917
4122
|
} catch {
|
|
3918
4123
|
}
|
|
3919
4124
|
const dbRow = {
|
|
@@ -3944,7 +4149,7 @@ async function writeMemory(record) {
|
|
|
3944
4149
|
tier: record.tier ?? classifyTier(record),
|
|
3945
4150
|
supersedes_id: record.supersedes_id ?? null,
|
|
3946
4151
|
draft: record.draft ? 1 : 0,
|
|
3947
|
-
memory_type:
|
|
4152
|
+
memory_type: memoryType,
|
|
3948
4153
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3949
4154
|
content_hash: contentHash,
|
|
3950
4155
|
intent: record.intent ?? null,
|
|
@@ -4103,6 +4308,7 @@ async function flushBatch() {
|
|
|
4103
4308
|
const globalClient = getClient();
|
|
4104
4309
|
const globalStmts = batch.map(buildStmt);
|
|
4105
4310
|
await globalClient.batch(globalStmts, "write");
|
|
4311
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4106
4312
|
_pendingRecords.splice(0, batch.length);
|
|
4107
4313
|
try {
|
|
4108
4314
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4361,6 +4567,7 @@ var init_store = __esm({
|
|
|
4361
4567
|
init_keychain();
|
|
4362
4568
|
init_config();
|
|
4363
4569
|
init_state_bus();
|
|
4570
|
+
init_memory_write_governor();
|
|
4364
4571
|
INIT_MAX_RETRIES = 3;
|
|
4365
4572
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4366
4573
|
_pendingRecords = [];
|
|
@@ -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 as createHash2 } 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 createHash2("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 contentHash2 = row.content_hash ? String(row.content_hash) : null;
|
|
3152
|
+
const agentId = String(row.agent_id);
|
|
3153
|
+
const projectName = String(row.project_name);
|
|
3154
|
+
if (contentHash2) {
|
|
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, contentHash2, 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 as createHash2 } 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 contentHash2 = governed.contentHash;
|
|
3943
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3944
|
+
if (_pendingRecords.some(
|
|
3945
|
+
(r) => r.content_hash === contentHash2 && 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: contentHash2,
|
|
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: contentHash2,
|
|
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 = [];
|