@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/bin/exe-search.js
CHANGED
|
@@ -2277,6 +2277,14 @@ async function ensureSchema() {
|
|
|
2277
2277
|
);
|
|
2278
2278
|
} catch {
|
|
2279
2279
|
}
|
|
2280
|
+
try {
|
|
2281
|
+
await client.execute(
|
|
2282
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2283
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2284
|
+
WHERE content_hash IS NOT NULL`
|
|
2285
|
+
);
|
|
2286
|
+
} catch {
|
|
2287
|
+
}
|
|
2280
2288
|
await client.executeMultiple(`
|
|
2281
2289
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2282
2290
|
id TEXT PRIMARY KEY,
|
|
@@ -3022,6 +3030,196 @@ var init_state_bus = __esm({
|
|
|
3022
3030
|
}
|
|
3023
3031
|
});
|
|
3024
3032
|
|
|
3033
|
+
// src/lib/memory-write-governor.ts
|
|
3034
|
+
import { createHash } from "crypto";
|
|
3035
|
+
function normalizeMemoryText(text) {
|
|
3036
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3037
|
+
}
|
|
3038
|
+
function classifyMemoryType(input) {
|
|
3039
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3040
|
+
const tool = input.tool_name.toLowerCase();
|
|
3041
|
+
const text = input.raw_text.toLowerCase();
|
|
3042
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3043
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3044
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3045
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3046
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3047
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3048
|
+
return "raw";
|
|
3049
|
+
}
|
|
3050
|
+
function shouldDropMemory(text) {
|
|
3051
|
+
const normalized = normalizeMemoryText(text);
|
|
3052
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3053
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3054
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3055
|
+
}
|
|
3056
|
+
return { drop: false };
|
|
3057
|
+
}
|
|
3058
|
+
function shouldSkipEmbedding(input) {
|
|
3059
|
+
const type = classifyMemoryType(input);
|
|
3060
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3061
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3062
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3063
|
+
return false;
|
|
3064
|
+
}
|
|
3065
|
+
function hashMemoryContent(text) {
|
|
3066
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3067
|
+
}
|
|
3068
|
+
function scopedDedupArgs(input) {
|
|
3069
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3070
|
+
}
|
|
3071
|
+
function governMemoryRecord(record) {
|
|
3072
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3073
|
+
const memoryType = classifyMemoryType({
|
|
3074
|
+
raw_text: normalized,
|
|
3075
|
+
agent_id: record.agent_id,
|
|
3076
|
+
project_name: record.project_name,
|
|
3077
|
+
tool_name: record.tool_name,
|
|
3078
|
+
memory_type: record.memory_type
|
|
3079
|
+
});
|
|
3080
|
+
const drop = shouldDropMemory(normalized);
|
|
3081
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3082
|
+
raw_text: normalized,
|
|
3083
|
+
agent_id: record.agent_id,
|
|
3084
|
+
project_name: record.project_name,
|
|
3085
|
+
tool_name: record.tool_name,
|
|
3086
|
+
memory_type: memoryType
|
|
3087
|
+
});
|
|
3088
|
+
return {
|
|
3089
|
+
record: {
|
|
3090
|
+
...record,
|
|
3091
|
+
raw_text: normalized,
|
|
3092
|
+
memory_type: memoryType,
|
|
3093
|
+
vector: skipEmbedding ? null : record.vector
|
|
3094
|
+
},
|
|
3095
|
+
contentHash: hashMemoryContent(normalized),
|
|
3096
|
+
shouldDrop: drop.drop,
|
|
3097
|
+
dropReason: drop.reason,
|
|
3098
|
+
skipEmbedding,
|
|
3099
|
+
hygiene: {
|
|
3100
|
+
dedup: true,
|
|
3101
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3102
|
+
}
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
async function findScopedDuplicate(input) {
|
|
3106
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3107
|
+
const client = getClient2();
|
|
3108
|
+
const args = scopedDedupArgs(input);
|
|
3109
|
+
let sql = `SELECT id FROM memories
|
|
3110
|
+
WHERE content_hash = ?
|
|
3111
|
+
AND agent_id = ?
|
|
3112
|
+
AND project_name = ?
|
|
3113
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3114
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3115
|
+
if (input.excludeId) {
|
|
3116
|
+
sql += " AND id != ?";
|
|
3117
|
+
args.push(input.excludeId);
|
|
3118
|
+
}
|
|
3119
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3120
|
+
const result = await client.execute({ sql, args });
|
|
3121
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3122
|
+
}
|
|
3123
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3124
|
+
try {
|
|
3125
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3126
|
+
const client = getClient2();
|
|
3127
|
+
const current = await client.execute({
|
|
3128
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3129
|
+
importance, timestamp
|
|
3130
|
+
FROM memories
|
|
3131
|
+
WHERE id = ?
|
|
3132
|
+
LIMIT 1`,
|
|
3133
|
+
args: [memoryId]
|
|
3134
|
+
});
|
|
3135
|
+
const row = current.rows[0];
|
|
3136
|
+
if (!row) return;
|
|
3137
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3138
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3139
|
+
const agentId = String(row.agent_id);
|
|
3140
|
+
const projectName = String(row.project_name);
|
|
3141
|
+
if (contentHash) {
|
|
3142
|
+
await client.execute({
|
|
3143
|
+
sql: `UPDATE memories
|
|
3144
|
+
SET status = 'deleted',
|
|
3145
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3146
|
+
WHERE id != ?
|
|
3147
|
+
AND content_hash = ?
|
|
3148
|
+
AND agent_id = ?
|
|
3149
|
+
AND project_name = ?
|
|
3150
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3151
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3152
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3153
|
+
});
|
|
3154
|
+
}
|
|
3155
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3156
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3157
|
+
const old = await client.execute({
|
|
3158
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3159
|
+
args: [supersedesId]
|
|
3160
|
+
});
|
|
3161
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3162
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3163
|
+
await client.batch([
|
|
3164
|
+
{
|
|
3165
|
+
sql: `UPDATE memories
|
|
3166
|
+
SET status = 'archived',
|
|
3167
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3168
|
+
WHERE id = ?`,
|
|
3169
|
+
args: [supersedesId]
|
|
3170
|
+
},
|
|
3171
|
+
{
|
|
3172
|
+
sql: `UPDATE memories
|
|
3173
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3174
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3175
|
+
WHERE id = ?`,
|
|
3176
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3177
|
+
}
|
|
3178
|
+
], "write");
|
|
3179
|
+
}
|
|
3180
|
+
} catch (err) {
|
|
3181
|
+
process.stderr.write(
|
|
3182
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3183
|
+
`
|
|
3184
|
+
);
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3188
|
+
if (memoryIds.length === 0) return;
|
|
3189
|
+
const run = () => {
|
|
3190
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3191
|
+
};
|
|
3192
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3193
|
+
else setTimeout(run, 0);
|
|
3194
|
+
}
|
|
3195
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3196
|
+
var init_memory_write_governor = __esm({
|
|
3197
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3198
|
+
"use strict";
|
|
3199
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3200
|
+
"decision",
|
|
3201
|
+
"adr",
|
|
3202
|
+
"behavior",
|
|
3203
|
+
"procedure"
|
|
3204
|
+
]);
|
|
3205
|
+
NOISE_DROP_PATTERNS = [
|
|
3206
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3207
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3208
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3209
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3210
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3211
|
+
];
|
|
3212
|
+
SKIP_EMBED_PATTERNS = [
|
|
3213
|
+
/tmux capture-pane\b/i,
|
|
3214
|
+
/docker ps\b/i,
|
|
3215
|
+
/docker images\b/i,
|
|
3216
|
+
/git status\b/i,
|
|
3217
|
+
/grep .*node_modules/i,
|
|
3218
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3219
|
+
];
|
|
3220
|
+
}
|
|
3221
|
+
});
|
|
3222
|
+
|
|
3025
3223
|
// src/lib/shard-manager.ts
|
|
3026
3224
|
var shard_manager_exports = {};
|
|
3027
3225
|
__export(shard_manager_exports, {
|
|
@@ -3186,7 +3384,8 @@ async function ensureShardSchema(client) {
|
|
|
3186
3384
|
}
|
|
3187
3385
|
for (const idx of [
|
|
3188
3386
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3189
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3387
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3388
|
+
"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"
|
|
3190
3389
|
]) {
|
|
3191
3390
|
try {
|
|
3192
3391
|
await client.execute(idx);
|
|
@@ -3611,7 +3810,6 @@ __export(store_exports, {
|
|
|
3611
3810
|
vectorToBlob: () => vectorToBlob,
|
|
3612
3811
|
writeMemory: () => writeMemory
|
|
3613
3812
|
});
|
|
3614
|
-
import { createHash } from "crypto";
|
|
3615
3813
|
function isBusyError2(err) {
|
|
3616
3814
|
if (err instanceof Error) {
|
|
3617
3815
|
const msg = err.message.toLowerCase();
|
|
@@ -3725,17 +3923,24 @@ async function writeMemory(record) {
|
|
|
3725
3923
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3726
3924
|
);
|
|
3727
3925
|
}
|
|
3728
|
-
const
|
|
3729
|
-
if (
|
|
3926
|
+
const governed = governMemoryRecord(record);
|
|
3927
|
+
if (governed.shouldDrop) return;
|
|
3928
|
+
record = governed.record;
|
|
3929
|
+
const contentHash = governed.contentHash;
|
|
3930
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3931
|
+
if (_pendingRecords.some(
|
|
3932
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3933
|
+
)) {
|
|
3730
3934
|
return;
|
|
3731
3935
|
}
|
|
3732
3936
|
try {
|
|
3733
|
-
const
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3937
|
+
const existing = await findScopedDuplicate({
|
|
3938
|
+
contentHash,
|
|
3939
|
+
agentId: record.agent_id,
|
|
3940
|
+
projectName: record.project_name,
|
|
3941
|
+
memoryType
|
|
3737
3942
|
});
|
|
3738
|
-
if (existing
|
|
3943
|
+
if (existing) return;
|
|
3739
3944
|
} catch {
|
|
3740
3945
|
}
|
|
3741
3946
|
const dbRow = {
|
|
@@ -3766,7 +3971,7 @@ async function writeMemory(record) {
|
|
|
3766
3971
|
tier: record.tier ?? classifyTier(record),
|
|
3767
3972
|
supersedes_id: record.supersedes_id ?? null,
|
|
3768
3973
|
draft: record.draft ? 1 : 0,
|
|
3769
|
-
memory_type:
|
|
3974
|
+
memory_type: memoryType,
|
|
3770
3975
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3771
3976
|
content_hash: contentHash,
|
|
3772
3977
|
intent: record.intent ?? null,
|
|
@@ -3925,6 +4130,7 @@ async function flushBatch() {
|
|
|
3925
4130
|
const globalClient = getClient();
|
|
3926
4131
|
const globalStmts = batch.map(buildStmt);
|
|
3927
4132
|
await globalClient.batch(globalStmts, "write");
|
|
4133
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3928
4134
|
_pendingRecords.splice(0, batch.length);
|
|
3929
4135
|
try {
|
|
3930
4136
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4183,6 +4389,7 @@ var init_store = __esm({
|
|
|
4183
4389
|
init_keychain();
|
|
4184
4390
|
init_config();
|
|
4185
4391
|
init_state_bus();
|
|
4392
|
+
init_memory_write_governor();
|
|
4186
4393
|
INIT_MAX_RETRIES = 3;
|
|
4187
4394
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4188
4395
|
_pendingRecords = [];
|
|
@@ -2321,6 +2321,14 @@ async function ensureSchema() {
|
|
|
2321
2321
|
);
|
|
2322
2322
|
} catch {
|
|
2323
2323
|
}
|
|
2324
|
+
try {
|
|
2325
|
+
await client.execute(
|
|
2326
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2327
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2328
|
+
WHERE content_hash IS NOT NULL`
|
|
2329
|
+
);
|
|
2330
|
+
} catch {
|
|
2331
|
+
}
|
|
2324
2332
|
await client.executeMultiple(`
|
|
2325
2333
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2326
2334
|
id TEXT PRIMARY KEY,
|
|
@@ -3066,6 +3074,196 @@ var init_state_bus = __esm({
|
|
|
3066
3074
|
}
|
|
3067
3075
|
});
|
|
3068
3076
|
|
|
3077
|
+
// src/lib/memory-write-governor.ts
|
|
3078
|
+
import { createHash } from "crypto";
|
|
3079
|
+
function normalizeMemoryText(text) {
|
|
3080
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3081
|
+
}
|
|
3082
|
+
function classifyMemoryType(input) {
|
|
3083
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3084
|
+
const tool = input.tool_name.toLowerCase();
|
|
3085
|
+
const text = input.raw_text.toLowerCase();
|
|
3086
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3087
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3088
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3089
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3090
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3091
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3092
|
+
return "raw";
|
|
3093
|
+
}
|
|
3094
|
+
function shouldDropMemory(text) {
|
|
3095
|
+
const normalized = normalizeMemoryText(text);
|
|
3096
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3097
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3098
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3099
|
+
}
|
|
3100
|
+
return { drop: false };
|
|
3101
|
+
}
|
|
3102
|
+
function shouldSkipEmbedding(input) {
|
|
3103
|
+
const type = classifyMemoryType(input);
|
|
3104
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3105
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3106
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3107
|
+
return false;
|
|
3108
|
+
}
|
|
3109
|
+
function hashMemoryContent(text) {
|
|
3110
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3111
|
+
}
|
|
3112
|
+
function scopedDedupArgs(input) {
|
|
3113
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3114
|
+
}
|
|
3115
|
+
function governMemoryRecord(record) {
|
|
3116
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3117
|
+
const memoryType = classifyMemoryType({
|
|
3118
|
+
raw_text: normalized,
|
|
3119
|
+
agent_id: record.agent_id,
|
|
3120
|
+
project_name: record.project_name,
|
|
3121
|
+
tool_name: record.tool_name,
|
|
3122
|
+
memory_type: record.memory_type
|
|
3123
|
+
});
|
|
3124
|
+
const drop = shouldDropMemory(normalized);
|
|
3125
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3126
|
+
raw_text: normalized,
|
|
3127
|
+
agent_id: record.agent_id,
|
|
3128
|
+
project_name: record.project_name,
|
|
3129
|
+
tool_name: record.tool_name,
|
|
3130
|
+
memory_type: memoryType
|
|
3131
|
+
});
|
|
3132
|
+
return {
|
|
3133
|
+
record: {
|
|
3134
|
+
...record,
|
|
3135
|
+
raw_text: normalized,
|
|
3136
|
+
memory_type: memoryType,
|
|
3137
|
+
vector: skipEmbedding ? null : record.vector
|
|
3138
|
+
},
|
|
3139
|
+
contentHash: hashMemoryContent(normalized),
|
|
3140
|
+
shouldDrop: drop.drop,
|
|
3141
|
+
dropReason: drop.reason,
|
|
3142
|
+
skipEmbedding,
|
|
3143
|
+
hygiene: {
|
|
3144
|
+
dedup: true,
|
|
3145
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3146
|
+
}
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3149
|
+
async function findScopedDuplicate(input) {
|
|
3150
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3151
|
+
const client = getClient2();
|
|
3152
|
+
const args = scopedDedupArgs(input);
|
|
3153
|
+
let sql = `SELECT id FROM memories
|
|
3154
|
+
WHERE content_hash = ?
|
|
3155
|
+
AND agent_id = ?
|
|
3156
|
+
AND project_name = ?
|
|
3157
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3158
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3159
|
+
if (input.excludeId) {
|
|
3160
|
+
sql += " AND id != ?";
|
|
3161
|
+
args.push(input.excludeId);
|
|
3162
|
+
}
|
|
3163
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3164
|
+
const result = await client.execute({ sql, args });
|
|
3165
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3166
|
+
}
|
|
3167
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3168
|
+
try {
|
|
3169
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3170
|
+
const client = getClient2();
|
|
3171
|
+
const current = await client.execute({
|
|
3172
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3173
|
+
importance, timestamp
|
|
3174
|
+
FROM memories
|
|
3175
|
+
WHERE id = ?
|
|
3176
|
+
LIMIT 1`,
|
|
3177
|
+
args: [memoryId]
|
|
3178
|
+
});
|
|
3179
|
+
const row = current.rows[0];
|
|
3180
|
+
if (!row) return;
|
|
3181
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3182
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3183
|
+
const agentId = String(row.agent_id);
|
|
3184
|
+
const projectName = String(row.project_name);
|
|
3185
|
+
if (contentHash) {
|
|
3186
|
+
await client.execute({
|
|
3187
|
+
sql: `UPDATE memories
|
|
3188
|
+
SET status = 'deleted',
|
|
3189
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3190
|
+
WHERE id != ?
|
|
3191
|
+
AND content_hash = ?
|
|
3192
|
+
AND agent_id = ?
|
|
3193
|
+
AND project_name = ?
|
|
3194
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3195
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3196
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3197
|
+
});
|
|
3198
|
+
}
|
|
3199
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3200
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3201
|
+
const old = await client.execute({
|
|
3202
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3203
|
+
args: [supersedesId]
|
|
3204
|
+
});
|
|
3205
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3206
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3207
|
+
await client.batch([
|
|
3208
|
+
{
|
|
3209
|
+
sql: `UPDATE memories
|
|
3210
|
+
SET status = 'archived',
|
|
3211
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3212
|
+
WHERE id = ?`,
|
|
3213
|
+
args: [supersedesId]
|
|
3214
|
+
},
|
|
3215
|
+
{
|
|
3216
|
+
sql: `UPDATE memories
|
|
3217
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3218
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3219
|
+
WHERE id = ?`,
|
|
3220
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3221
|
+
}
|
|
3222
|
+
], "write");
|
|
3223
|
+
}
|
|
3224
|
+
} catch (err) {
|
|
3225
|
+
process.stderr.write(
|
|
3226
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3227
|
+
`
|
|
3228
|
+
);
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3232
|
+
if (memoryIds.length === 0) return;
|
|
3233
|
+
const run = () => {
|
|
3234
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3235
|
+
};
|
|
3236
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3237
|
+
else setTimeout(run, 0);
|
|
3238
|
+
}
|
|
3239
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3240
|
+
var init_memory_write_governor = __esm({
|
|
3241
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3242
|
+
"use strict";
|
|
3243
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3244
|
+
"decision",
|
|
3245
|
+
"adr",
|
|
3246
|
+
"behavior",
|
|
3247
|
+
"procedure"
|
|
3248
|
+
]);
|
|
3249
|
+
NOISE_DROP_PATTERNS = [
|
|
3250
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3251
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3252
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3253
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3254
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3255
|
+
];
|
|
3256
|
+
SKIP_EMBED_PATTERNS = [
|
|
3257
|
+
/tmux capture-pane\b/i,
|
|
3258
|
+
/docker ps\b/i,
|
|
3259
|
+
/docker images\b/i,
|
|
3260
|
+
/git status\b/i,
|
|
3261
|
+
/grep .*node_modules/i,
|
|
3262
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3263
|
+
];
|
|
3264
|
+
}
|
|
3265
|
+
});
|
|
3266
|
+
|
|
3069
3267
|
// src/lib/shard-manager.ts
|
|
3070
3268
|
var shard_manager_exports = {};
|
|
3071
3269
|
__export(shard_manager_exports, {
|
|
@@ -3230,7 +3428,8 @@ async function ensureShardSchema(client) {
|
|
|
3230
3428
|
}
|
|
3231
3429
|
for (const idx of [
|
|
3232
3430
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3233
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3431
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3432
|
+
"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"
|
|
3234
3433
|
]) {
|
|
3235
3434
|
try {
|
|
3236
3435
|
await client.execute(idx);
|
|
@@ -3655,7 +3854,6 @@ __export(store_exports, {
|
|
|
3655
3854
|
vectorToBlob: () => vectorToBlob,
|
|
3656
3855
|
writeMemory: () => writeMemory
|
|
3657
3856
|
});
|
|
3658
|
-
import { createHash } from "crypto";
|
|
3659
3857
|
function isBusyError2(err) {
|
|
3660
3858
|
if (err instanceof Error) {
|
|
3661
3859
|
const msg = err.message.toLowerCase();
|
|
@@ -3769,17 +3967,24 @@ async function writeMemory(record) {
|
|
|
3769
3967
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3770
3968
|
);
|
|
3771
3969
|
}
|
|
3772
|
-
const
|
|
3773
|
-
if (
|
|
3970
|
+
const governed = governMemoryRecord(record);
|
|
3971
|
+
if (governed.shouldDrop) return;
|
|
3972
|
+
record = governed.record;
|
|
3973
|
+
const contentHash = governed.contentHash;
|
|
3974
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3975
|
+
if (_pendingRecords.some(
|
|
3976
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3977
|
+
)) {
|
|
3774
3978
|
return;
|
|
3775
3979
|
}
|
|
3776
3980
|
try {
|
|
3777
|
-
const
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3981
|
+
const existing = await findScopedDuplicate({
|
|
3982
|
+
contentHash,
|
|
3983
|
+
agentId: record.agent_id,
|
|
3984
|
+
projectName: record.project_name,
|
|
3985
|
+
memoryType
|
|
3781
3986
|
});
|
|
3782
|
-
if (existing
|
|
3987
|
+
if (existing) return;
|
|
3783
3988
|
} catch {
|
|
3784
3989
|
}
|
|
3785
3990
|
const dbRow = {
|
|
@@ -3810,7 +4015,7 @@ async function writeMemory(record) {
|
|
|
3810
4015
|
tier: record.tier ?? classifyTier(record),
|
|
3811
4016
|
supersedes_id: record.supersedes_id ?? null,
|
|
3812
4017
|
draft: record.draft ? 1 : 0,
|
|
3813
|
-
memory_type:
|
|
4018
|
+
memory_type: memoryType,
|
|
3814
4019
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3815
4020
|
content_hash: contentHash,
|
|
3816
4021
|
intent: record.intent ?? null,
|
|
@@ -3969,6 +4174,7 @@ async function flushBatch() {
|
|
|
3969
4174
|
const globalClient = getClient();
|
|
3970
4175
|
const globalStmts = batch.map(buildStmt);
|
|
3971
4176
|
await globalClient.batch(globalStmts, "write");
|
|
4177
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3972
4178
|
_pendingRecords.splice(0, batch.length);
|
|
3973
4179
|
try {
|
|
3974
4180
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4227,6 +4433,7 @@ var init_store = __esm({
|
|
|
4227
4433
|
init_keychain();
|
|
4228
4434
|
init_config();
|
|
4229
4435
|
init_state_bus();
|
|
4436
|
+
init_memory_write_governor();
|
|
4230
4437
|
INIT_MAX_RETRIES = 3;
|
|
4231
4438
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4232
4439
|
_pendingRecords = [];
|
|
@@ -7673,7 +7880,11 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
7673
7880
|
}
|
|
7674
7881
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
7675
7882
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
7676
|
-
|
|
7883
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
7884
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
7885
|
+
ccModel += "[1m]";
|
|
7886
|
+
}
|
|
7887
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
7677
7888
|
}
|
|
7678
7889
|
}
|
|
7679
7890
|
let spawnCommand;
|