@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
|
@@ -2454,6 +2454,14 @@ async function ensureSchema() {
|
|
|
2454
2454
|
);
|
|
2455
2455
|
} catch {
|
|
2456
2456
|
}
|
|
2457
|
+
try {
|
|
2458
|
+
await client.execute(
|
|
2459
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2460
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2461
|
+
WHERE content_hash IS NOT NULL`
|
|
2462
|
+
);
|
|
2463
|
+
} catch {
|
|
2464
|
+
}
|
|
2457
2465
|
await client.executeMultiple(`
|
|
2458
2466
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2459
2467
|
id TEXT PRIMARY KEY,
|
|
@@ -3199,6 +3207,196 @@ var init_state_bus = __esm({
|
|
|
3199
3207
|
}
|
|
3200
3208
|
});
|
|
3201
3209
|
|
|
3210
|
+
// src/lib/memory-write-governor.ts
|
|
3211
|
+
import { createHash } from "crypto";
|
|
3212
|
+
function normalizeMemoryText(text) {
|
|
3213
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3214
|
+
}
|
|
3215
|
+
function classifyMemoryType(input2) {
|
|
3216
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3217
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3218
|
+
const text = input2.raw_text.toLowerCase();
|
|
3219
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3220
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3221
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3222
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3223
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3224
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3225
|
+
return "raw";
|
|
3226
|
+
}
|
|
3227
|
+
function shouldDropMemory(text) {
|
|
3228
|
+
const normalized = normalizeMemoryText(text);
|
|
3229
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3230
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3231
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3232
|
+
}
|
|
3233
|
+
return { drop: false };
|
|
3234
|
+
}
|
|
3235
|
+
function shouldSkipEmbedding(input2) {
|
|
3236
|
+
const type = classifyMemoryType(input2);
|
|
3237
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3238
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3239
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3240
|
+
return false;
|
|
3241
|
+
}
|
|
3242
|
+
function hashMemoryContent(text) {
|
|
3243
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3244
|
+
}
|
|
3245
|
+
function scopedDedupArgs(input2) {
|
|
3246
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3247
|
+
}
|
|
3248
|
+
function governMemoryRecord(record) {
|
|
3249
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3250
|
+
const memoryType = classifyMemoryType({
|
|
3251
|
+
raw_text: normalized,
|
|
3252
|
+
agent_id: record.agent_id,
|
|
3253
|
+
project_name: record.project_name,
|
|
3254
|
+
tool_name: record.tool_name,
|
|
3255
|
+
memory_type: record.memory_type
|
|
3256
|
+
});
|
|
3257
|
+
const drop = shouldDropMemory(normalized);
|
|
3258
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3259
|
+
raw_text: normalized,
|
|
3260
|
+
agent_id: record.agent_id,
|
|
3261
|
+
project_name: record.project_name,
|
|
3262
|
+
tool_name: record.tool_name,
|
|
3263
|
+
memory_type: memoryType
|
|
3264
|
+
});
|
|
3265
|
+
return {
|
|
3266
|
+
record: {
|
|
3267
|
+
...record,
|
|
3268
|
+
raw_text: normalized,
|
|
3269
|
+
memory_type: memoryType,
|
|
3270
|
+
vector: skipEmbedding ? null : record.vector
|
|
3271
|
+
},
|
|
3272
|
+
contentHash: hashMemoryContent(normalized),
|
|
3273
|
+
shouldDrop: drop.drop,
|
|
3274
|
+
dropReason: drop.reason,
|
|
3275
|
+
skipEmbedding,
|
|
3276
|
+
hygiene: {
|
|
3277
|
+
dedup: true,
|
|
3278
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3279
|
+
}
|
|
3280
|
+
};
|
|
3281
|
+
}
|
|
3282
|
+
async function findScopedDuplicate(input2) {
|
|
3283
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3284
|
+
const client = getClient2();
|
|
3285
|
+
const args = scopedDedupArgs(input2);
|
|
3286
|
+
let sql = `SELECT id FROM memories
|
|
3287
|
+
WHERE content_hash = ?
|
|
3288
|
+
AND agent_id = ?
|
|
3289
|
+
AND project_name = ?
|
|
3290
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3291
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3292
|
+
if (input2.excludeId) {
|
|
3293
|
+
sql += " AND id != ?";
|
|
3294
|
+
args.push(input2.excludeId);
|
|
3295
|
+
}
|
|
3296
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3297
|
+
const result = await client.execute({ sql, args });
|
|
3298
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3299
|
+
}
|
|
3300
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3301
|
+
try {
|
|
3302
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3303
|
+
const client = getClient2();
|
|
3304
|
+
const current = await client.execute({
|
|
3305
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3306
|
+
importance, timestamp
|
|
3307
|
+
FROM memories
|
|
3308
|
+
WHERE id = ?
|
|
3309
|
+
LIMIT 1`,
|
|
3310
|
+
args: [memoryId]
|
|
3311
|
+
});
|
|
3312
|
+
const row = current.rows[0];
|
|
3313
|
+
if (!row) return;
|
|
3314
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3315
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3316
|
+
const agentId = String(row.agent_id);
|
|
3317
|
+
const projectName = String(row.project_name);
|
|
3318
|
+
if (contentHash) {
|
|
3319
|
+
await client.execute({
|
|
3320
|
+
sql: `UPDATE memories
|
|
3321
|
+
SET status = 'deleted',
|
|
3322
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3323
|
+
WHERE id != ?
|
|
3324
|
+
AND content_hash = ?
|
|
3325
|
+
AND agent_id = ?
|
|
3326
|
+
AND project_name = ?
|
|
3327
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3328
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3329
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3330
|
+
});
|
|
3331
|
+
}
|
|
3332
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3333
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3334
|
+
const old = await client.execute({
|
|
3335
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3336
|
+
args: [supersedesId]
|
|
3337
|
+
});
|
|
3338
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3339
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3340
|
+
await client.batch([
|
|
3341
|
+
{
|
|
3342
|
+
sql: `UPDATE memories
|
|
3343
|
+
SET status = 'archived',
|
|
3344
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3345
|
+
WHERE id = ?`,
|
|
3346
|
+
args: [supersedesId]
|
|
3347
|
+
},
|
|
3348
|
+
{
|
|
3349
|
+
sql: `UPDATE memories
|
|
3350
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3351
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3352
|
+
WHERE id = ?`,
|
|
3353
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3354
|
+
}
|
|
3355
|
+
], "write");
|
|
3356
|
+
}
|
|
3357
|
+
} catch (err) {
|
|
3358
|
+
process.stderr.write(
|
|
3359
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3360
|
+
`
|
|
3361
|
+
);
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3365
|
+
if (memoryIds.length === 0) return;
|
|
3366
|
+
const run = () => {
|
|
3367
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3368
|
+
};
|
|
3369
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3370
|
+
else setTimeout(run, 0);
|
|
3371
|
+
}
|
|
3372
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3373
|
+
var init_memory_write_governor = __esm({
|
|
3374
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3375
|
+
"use strict";
|
|
3376
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3377
|
+
"decision",
|
|
3378
|
+
"adr",
|
|
3379
|
+
"behavior",
|
|
3380
|
+
"procedure"
|
|
3381
|
+
]);
|
|
3382
|
+
NOISE_DROP_PATTERNS = [
|
|
3383
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3384
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3385
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3386
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3387
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3388
|
+
];
|
|
3389
|
+
SKIP_EMBED_PATTERNS = [
|
|
3390
|
+
/tmux capture-pane\b/i,
|
|
3391
|
+
/docker ps\b/i,
|
|
3392
|
+
/docker images\b/i,
|
|
3393
|
+
/git status\b/i,
|
|
3394
|
+
/grep .*node_modules/i,
|
|
3395
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3396
|
+
];
|
|
3397
|
+
}
|
|
3398
|
+
});
|
|
3399
|
+
|
|
3202
3400
|
// src/lib/shard-manager.ts
|
|
3203
3401
|
var shard_manager_exports = {};
|
|
3204
3402
|
__export(shard_manager_exports, {
|
|
@@ -3363,7 +3561,8 @@ async function ensureShardSchema(client) {
|
|
|
3363
3561
|
}
|
|
3364
3562
|
for (const idx of [
|
|
3365
3563
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3366
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3564
|
+
"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_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
|
|
3367
3566
|
]) {
|
|
3368
3567
|
try {
|
|
3369
3568
|
await client.execute(idx);
|
|
@@ -3788,7 +3987,6 @@ __export(store_exports, {
|
|
|
3788
3987
|
vectorToBlob: () => vectorToBlob,
|
|
3789
3988
|
writeMemory: () => writeMemory
|
|
3790
3989
|
});
|
|
3791
|
-
import { createHash } from "crypto";
|
|
3792
3990
|
function isBusyError2(err) {
|
|
3793
3991
|
if (err instanceof Error) {
|
|
3794
3992
|
const msg = err.message.toLowerCase();
|
|
@@ -3902,17 +4100,24 @@ async function writeMemory(record) {
|
|
|
3902
4100
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3903
4101
|
);
|
|
3904
4102
|
}
|
|
3905
|
-
const
|
|
3906
|
-
if (
|
|
4103
|
+
const governed = governMemoryRecord(record);
|
|
4104
|
+
if (governed.shouldDrop) return;
|
|
4105
|
+
record = governed.record;
|
|
4106
|
+
const contentHash = governed.contentHash;
|
|
4107
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4108
|
+
if (_pendingRecords.some(
|
|
4109
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4110
|
+
)) {
|
|
3907
4111
|
return;
|
|
3908
4112
|
}
|
|
3909
4113
|
try {
|
|
3910
|
-
const
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
4114
|
+
const existing = await findScopedDuplicate({
|
|
4115
|
+
contentHash,
|
|
4116
|
+
agentId: record.agent_id,
|
|
4117
|
+
projectName: record.project_name,
|
|
4118
|
+
memoryType
|
|
3914
4119
|
});
|
|
3915
|
-
if (existing
|
|
4120
|
+
if (existing) return;
|
|
3916
4121
|
} catch {
|
|
3917
4122
|
}
|
|
3918
4123
|
const dbRow = {
|
|
@@ -3943,7 +4148,7 @@ async function writeMemory(record) {
|
|
|
3943
4148
|
tier: record.tier ?? classifyTier(record),
|
|
3944
4149
|
supersedes_id: record.supersedes_id ?? null,
|
|
3945
4150
|
draft: record.draft ? 1 : 0,
|
|
3946
|
-
memory_type:
|
|
4151
|
+
memory_type: memoryType,
|
|
3947
4152
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3948
4153
|
content_hash: contentHash,
|
|
3949
4154
|
intent: record.intent ?? null,
|
|
@@ -4102,6 +4307,7 @@ async function flushBatch() {
|
|
|
4102
4307
|
const globalClient = getClient();
|
|
4103
4308
|
const globalStmts = batch.map(buildStmt);
|
|
4104
4309
|
await globalClient.batch(globalStmts, "write");
|
|
4310
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4105
4311
|
_pendingRecords.splice(0, batch.length);
|
|
4106
4312
|
try {
|
|
4107
4313
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4360,6 +4566,7 @@ var init_store = __esm({
|
|
|
4360
4566
|
init_keychain();
|
|
4361
4567
|
init_config();
|
|
4362
4568
|
init_state_bus();
|
|
4569
|
+
init_memory_write_governor();
|
|
4363
4570
|
INIT_MAX_RETRIES = 3;
|
|
4364
4571
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4365
4572
|
_pendingRecords = [];
|
|
@@ -2866,6 +2866,14 @@ async function ensureSchema() {
|
|
|
2866
2866
|
);
|
|
2867
2867
|
} catch {
|
|
2868
2868
|
}
|
|
2869
|
+
try {
|
|
2870
|
+
await client.execute(
|
|
2871
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2872
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2873
|
+
WHERE content_hash IS NOT NULL`
|
|
2874
|
+
);
|
|
2875
|
+
} catch {
|
|
2876
|
+
}
|
|
2869
2877
|
await client.executeMultiple(`
|
|
2870
2878
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2871
2879
|
id TEXT PRIMARY KEY,
|
|
@@ -6365,7 +6373,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6365
6373
|
}
|
|
6366
6374
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6367
6375
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
6368
|
-
|
|
6376
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
6377
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
6378
|
+
ccModel += "[1m]";
|
|
6379
|
+
}
|
|
6380
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
6369
6381
|
}
|
|
6370
6382
|
}
|
|
6371
6383
|
let spawnCommand;
|
|
@@ -6712,6 +6724,196 @@ var init_keychain = __esm({
|
|
|
6712
6724
|
}
|
|
6713
6725
|
});
|
|
6714
6726
|
|
|
6727
|
+
// src/lib/memory-write-governor.ts
|
|
6728
|
+
import { createHash } from "crypto";
|
|
6729
|
+
function normalizeMemoryText(text) {
|
|
6730
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
6731
|
+
}
|
|
6732
|
+
function classifyMemoryType(input2) {
|
|
6733
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
6734
|
+
const tool = input2.tool_name.toLowerCase();
|
|
6735
|
+
const text = input2.raw_text.toLowerCase();
|
|
6736
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
6737
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
6738
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
6739
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
6740
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
6741
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
6742
|
+
return "raw";
|
|
6743
|
+
}
|
|
6744
|
+
function shouldDropMemory(text) {
|
|
6745
|
+
const normalized = normalizeMemoryText(text);
|
|
6746
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
6747
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
6748
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
6749
|
+
}
|
|
6750
|
+
return { drop: false };
|
|
6751
|
+
}
|
|
6752
|
+
function shouldSkipEmbedding(input2) {
|
|
6753
|
+
const type = classifyMemoryType(input2);
|
|
6754
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
6755
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
6756
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
6757
|
+
return false;
|
|
6758
|
+
}
|
|
6759
|
+
function hashMemoryContent(text) {
|
|
6760
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
6761
|
+
}
|
|
6762
|
+
function scopedDedupArgs(input2) {
|
|
6763
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
6764
|
+
}
|
|
6765
|
+
function governMemoryRecord(record) {
|
|
6766
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
6767
|
+
const memoryType = classifyMemoryType({
|
|
6768
|
+
raw_text: normalized,
|
|
6769
|
+
agent_id: record.agent_id,
|
|
6770
|
+
project_name: record.project_name,
|
|
6771
|
+
tool_name: record.tool_name,
|
|
6772
|
+
memory_type: record.memory_type
|
|
6773
|
+
});
|
|
6774
|
+
const drop = shouldDropMemory(normalized);
|
|
6775
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
6776
|
+
raw_text: normalized,
|
|
6777
|
+
agent_id: record.agent_id,
|
|
6778
|
+
project_name: record.project_name,
|
|
6779
|
+
tool_name: record.tool_name,
|
|
6780
|
+
memory_type: memoryType
|
|
6781
|
+
});
|
|
6782
|
+
return {
|
|
6783
|
+
record: {
|
|
6784
|
+
...record,
|
|
6785
|
+
raw_text: normalized,
|
|
6786
|
+
memory_type: memoryType,
|
|
6787
|
+
vector: skipEmbedding ? null : record.vector
|
|
6788
|
+
},
|
|
6789
|
+
contentHash: hashMemoryContent(normalized),
|
|
6790
|
+
shouldDrop: drop.drop,
|
|
6791
|
+
dropReason: drop.reason,
|
|
6792
|
+
skipEmbedding,
|
|
6793
|
+
hygiene: {
|
|
6794
|
+
dedup: true,
|
|
6795
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
6796
|
+
}
|
|
6797
|
+
};
|
|
6798
|
+
}
|
|
6799
|
+
async function findScopedDuplicate(input2) {
|
|
6800
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6801
|
+
const client = getClient2();
|
|
6802
|
+
const args = scopedDedupArgs(input2);
|
|
6803
|
+
let sql = `SELECT id FROM memories
|
|
6804
|
+
WHERE content_hash = ?
|
|
6805
|
+
AND agent_id = ?
|
|
6806
|
+
AND project_name = ?
|
|
6807
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6808
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
6809
|
+
if (input2.excludeId) {
|
|
6810
|
+
sql += " AND id != ?";
|
|
6811
|
+
args.push(input2.excludeId);
|
|
6812
|
+
}
|
|
6813
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
6814
|
+
const result = await client.execute({ sql, args });
|
|
6815
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
6816
|
+
}
|
|
6817
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
6818
|
+
try {
|
|
6819
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6820
|
+
const client = getClient2();
|
|
6821
|
+
const current = await client.execute({
|
|
6822
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
6823
|
+
importance, timestamp
|
|
6824
|
+
FROM memories
|
|
6825
|
+
WHERE id = ?
|
|
6826
|
+
LIMIT 1`,
|
|
6827
|
+
args: [memoryId]
|
|
6828
|
+
});
|
|
6829
|
+
const row = current.rows[0];
|
|
6830
|
+
if (!row) return;
|
|
6831
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
6832
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
6833
|
+
const agentId = String(row.agent_id);
|
|
6834
|
+
const projectName = String(row.project_name);
|
|
6835
|
+
if (contentHash) {
|
|
6836
|
+
await client.execute({
|
|
6837
|
+
sql: `UPDATE memories
|
|
6838
|
+
SET status = 'deleted',
|
|
6839
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6840
|
+
WHERE id != ?
|
|
6841
|
+
AND content_hash = ?
|
|
6842
|
+
AND agent_id = ?
|
|
6843
|
+
AND project_name = ?
|
|
6844
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6845
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
6846
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
6847
|
+
});
|
|
6848
|
+
}
|
|
6849
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
6850
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
6851
|
+
const old = await client.execute({
|
|
6852
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
6853
|
+
args: [supersedesId]
|
|
6854
|
+
});
|
|
6855
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
6856
|
+
const newImportance = Number(row.importance ?? 0);
|
|
6857
|
+
await client.batch([
|
|
6858
|
+
{
|
|
6859
|
+
sql: `UPDATE memories
|
|
6860
|
+
SET status = 'archived',
|
|
6861
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6862
|
+
WHERE id = ?`,
|
|
6863
|
+
args: [supersedesId]
|
|
6864
|
+
},
|
|
6865
|
+
{
|
|
6866
|
+
sql: `UPDATE memories
|
|
6867
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
6868
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
6869
|
+
WHERE id = ?`,
|
|
6870
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
6871
|
+
}
|
|
6872
|
+
], "write");
|
|
6873
|
+
}
|
|
6874
|
+
} catch (err) {
|
|
6875
|
+
process.stderr.write(
|
|
6876
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
6877
|
+
`
|
|
6878
|
+
);
|
|
6879
|
+
}
|
|
6880
|
+
}
|
|
6881
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
6882
|
+
if (memoryIds.length === 0) return;
|
|
6883
|
+
const run = () => {
|
|
6884
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
6885
|
+
};
|
|
6886
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
6887
|
+
else setTimeout(run, 0);
|
|
6888
|
+
}
|
|
6889
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
6890
|
+
var init_memory_write_governor = __esm({
|
|
6891
|
+
"src/lib/memory-write-governor.ts"() {
|
|
6892
|
+
"use strict";
|
|
6893
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
6894
|
+
"decision",
|
|
6895
|
+
"adr",
|
|
6896
|
+
"behavior",
|
|
6897
|
+
"procedure"
|
|
6898
|
+
]);
|
|
6899
|
+
NOISE_DROP_PATTERNS = [
|
|
6900
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
6901
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
6902
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
6903
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
6904
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
6905
|
+
];
|
|
6906
|
+
SKIP_EMBED_PATTERNS = [
|
|
6907
|
+
/tmux capture-pane\b/i,
|
|
6908
|
+
/docker ps\b/i,
|
|
6909
|
+
/docker images\b/i,
|
|
6910
|
+
/git status\b/i,
|
|
6911
|
+
/grep .*node_modules/i,
|
|
6912
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
6913
|
+
];
|
|
6914
|
+
}
|
|
6915
|
+
});
|
|
6916
|
+
|
|
6715
6917
|
// src/lib/shard-manager.ts
|
|
6716
6918
|
var shard_manager_exports = {};
|
|
6717
6919
|
__export(shard_manager_exports, {
|
|
@@ -6876,7 +7078,8 @@ async function ensureShardSchema(client) {
|
|
|
6876
7078
|
}
|
|
6877
7079
|
for (const idx of [
|
|
6878
7080
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
6879
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
7081
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
7082
|
+
"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"
|
|
6880
7083
|
]) {
|
|
6881
7084
|
try {
|
|
6882
7085
|
await client.execute(idx);
|
|
@@ -7301,7 +7504,6 @@ __export(store_exports, {
|
|
|
7301
7504
|
vectorToBlob: () => vectorToBlob,
|
|
7302
7505
|
writeMemory: () => writeMemory
|
|
7303
7506
|
});
|
|
7304
|
-
import { createHash } from "crypto";
|
|
7305
7507
|
function isBusyError2(err) {
|
|
7306
7508
|
if (err instanceof Error) {
|
|
7307
7509
|
const msg = err.message.toLowerCase();
|
|
@@ -7415,17 +7617,24 @@ async function writeMemory(record) {
|
|
|
7415
7617
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
7416
7618
|
);
|
|
7417
7619
|
}
|
|
7418
|
-
const
|
|
7419
|
-
if (
|
|
7620
|
+
const governed = governMemoryRecord(record);
|
|
7621
|
+
if (governed.shouldDrop) return;
|
|
7622
|
+
record = governed.record;
|
|
7623
|
+
const contentHash = governed.contentHash;
|
|
7624
|
+
const memoryType = record.memory_type ?? "raw";
|
|
7625
|
+
if (_pendingRecords.some(
|
|
7626
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
7627
|
+
)) {
|
|
7420
7628
|
return;
|
|
7421
7629
|
}
|
|
7422
7630
|
try {
|
|
7423
|
-
const
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7631
|
+
const existing = await findScopedDuplicate({
|
|
7632
|
+
contentHash,
|
|
7633
|
+
agentId: record.agent_id,
|
|
7634
|
+
projectName: record.project_name,
|
|
7635
|
+
memoryType
|
|
7427
7636
|
});
|
|
7428
|
-
if (existing
|
|
7637
|
+
if (existing) return;
|
|
7429
7638
|
} catch {
|
|
7430
7639
|
}
|
|
7431
7640
|
const dbRow = {
|
|
@@ -7456,7 +7665,7 @@ async function writeMemory(record) {
|
|
|
7456
7665
|
tier: record.tier ?? classifyTier(record),
|
|
7457
7666
|
supersedes_id: record.supersedes_id ?? null,
|
|
7458
7667
|
draft: record.draft ? 1 : 0,
|
|
7459
|
-
memory_type:
|
|
7668
|
+
memory_type: memoryType,
|
|
7460
7669
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
7461
7670
|
content_hash: contentHash,
|
|
7462
7671
|
intent: record.intent ?? null,
|
|
@@ -7615,6 +7824,7 @@ async function flushBatch() {
|
|
|
7615
7824
|
const globalClient = getClient();
|
|
7616
7825
|
const globalStmts = batch.map(buildStmt);
|
|
7617
7826
|
await globalClient.batch(globalStmts, "write");
|
|
7827
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
7618
7828
|
_pendingRecords.splice(0, batch.length);
|
|
7619
7829
|
try {
|
|
7620
7830
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -7873,6 +8083,7 @@ var init_store = __esm({
|
|
|
7873
8083
|
init_keychain();
|
|
7874
8084
|
init_config();
|
|
7875
8085
|
init_state_bus();
|
|
8086
|
+
init_memory_write_governor();
|
|
7876
8087
|
INIT_MAX_RETRIES = 3;
|
|
7877
8088
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
7878
8089
|
_pendingRecords = [];
|