@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-team.js
CHANGED
|
@@ -2299,6 +2299,14 @@ async function ensureSchema() {
|
|
|
2299
2299
|
);
|
|
2300
2300
|
} catch {
|
|
2301
2301
|
}
|
|
2302
|
+
try {
|
|
2303
|
+
await client.execute(
|
|
2304
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2305
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2306
|
+
WHERE content_hash IS NOT NULL`
|
|
2307
|
+
);
|
|
2308
|
+
} catch {
|
|
2309
|
+
}
|
|
2302
2310
|
await client.executeMultiple(`
|
|
2303
2311
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2304
2312
|
id TEXT PRIMARY KEY,
|
|
@@ -3044,6 +3052,196 @@ var init_state_bus = __esm({
|
|
|
3044
3052
|
}
|
|
3045
3053
|
});
|
|
3046
3054
|
|
|
3055
|
+
// src/lib/memory-write-governor.ts
|
|
3056
|
+
import { createHash } from "crypto";
|
|
3057
|
+
function normalizeMemoryText(text) {
|
|
3058
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3059
|
+
}
|
|
3060
|
+
function classifyMemoryType(input) {
|
|
3061
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3062
|
+
const tool = input.tool_name.toLowerCase();
|
|
3063
|
+
const text = input.raw_text.toLowerCase();
|
|
3064
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3065
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3066
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3067
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3068
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3069
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3070
|
+
return "raw";
|
|
3071
|
+
}
|
|
3072
|
+
function shouldDropMemory(text) {
|
|
3073
|
+
const normalized = normalizeMemoryText(text);
|
|
3074
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3075
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3076
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3077
|
+
}
|
|
3078
|
+
return { drop: false };
|
|
3079
|
+
}
|
|
3080
|
+
function shouldSkipEmbedding(input) {
|
|
3081
|
+
const type = classifyMemoryType(input);
|
|
3082
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3083
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3084
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3085
|
+
return false;
|
|
3086
|
+
}
|
|
3087
|
+
function hashMemoryContent(text) {
|
|
3088
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3089
|
+
}
|
|
3090
|
+
function scopedDedupArgs(input) {
|
|
3091
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3092
|
+
}
|
|
3093
|
+
function governMemoryRecord(record) {
|
|
3094
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3095
|
+
const memoryType = classifyMemoryType({
|
|
3096
|
+
raw_text: normalized,
|
|
3097
|
+
agent_id: record.agent_id,
|
|
3098
|
+
project_name: record.project_name,
|
|
3099
|
+
tool_name: record.tool_name,
|
|
3100
|
+
memory_type: record.memory_type
|
|
3101
|
+
});
|
|
3102
|
+
const drop = shouldDropMemory(normalized);
|
|
3103
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3104
|
+
raw_text: normalized,
|
|
3105
|
+
agent_id: record.agent_id,
|
|
3106
|
+
project_name: record.project_name,
|
|
3107
|
+
tool_name: record.tool_name,
|
|
3108
|
+
memory_type: memoryType
|
|
3109
|
+
});
|
|
3110
|
+
return {
|
|
3111
|
+
record: {
|
|
3112
|
+
...record,
|
|
3113
|
+
raw_text: normalized,
|
|
3114
|
+
memory_type: memoryType,
|
|
3115
|
+
vector: skipEmbedding ? null : record.vector
|
|
3116
|
+
},
|
|
3117
|
+
contentHash: hashMemoryContent(normalized),
|
|
3118
|
+
shouldDrop: drop.drop,
|
|
3119
|
+
dropReason: drop.reason,
|
|
3120
|
+
skipEmbedding,
|
|
3121
|
+
hygiene: {
|
|
3122
|
+
dedup: true,
|
|
3123
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3124
|
+
}
|
|
3125
|
+
};
|
|
3126
|
+
}
|
|
3127
|
+
async function findScopedDuplicate(input) {
|
|
3128
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3129
|
+
const client = getClient2();
|
|
3130
|
+
const args = scopedDedupArgs(input);
|
|
3131
|
+
let sql = `SELECT id FROM memories
|
|
3132
|
+
WHERE content_hash = ?
|
|
3133
|
+
AND agent_id = ?
|
|
3134
|
+
AND project_name = ?
|
|
3135
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3136
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3137
|
+
if (input.excludeId) {
|
|
3138
|
+
sql += " AND id != ?";
|
|
3139
|
+
args.push(input.excludeId);
|
|
3140
|
+
}
|
|
3141
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3142
|
+
const result = await client.execute({ sql, args });
|
|
3143
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3144
|
+
}
|
|
3145
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3146
|
+
try {
|
|
3147
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3148
|
+
const client = getClient2();
|
|
3149
|
+
const current = await client.execute({
|
|
3150
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3151
|
+
importance, timestamp
|
|
3152
|
+
FROM memories
|
|
3153
|
+
WHERE id = ?
|
|
3154
|
+
LIMIT 1`,
|
|
3155
|
+
args: [memoryId]
|
|
3156
|
+
});
|
|
3157
|
+
const row = current.rows[0];
|
|
3158
|
+
if (!row) return;
|
|
3159
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3160
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3161
|
+
const agentId = String(row.agent_id);
|
|
3162
|
+
const projectName = String(row.project_name);
|
|
3163
|
+
if (contentHash) {
|
|
3164
|
+
await client.execute({
|
|
3165
|
+
sql: `UPDATE memories
|
|
3166
|
+
SET status = 'deleted',
|
|
3167
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3168
|
+
WHERE id != ?
|
|
3169
|
+
AND content_hash = ?
|
|
3170
|
+
AND agent_id = ?
|
|
3171
|
+
AND project_name = ?
|
|
3172
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3173
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3174
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3175
|
+
});
|
|
3176
|
+
}
|
|
3177
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3178
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3179
|
+
const old = await client.execute({
|
|
3180
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3181
|
+
args: [supersedesId]
|
|
3182
|
+
});
|
|
3183
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3184
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3185
|
+
await client.batch([
|
|
3186
|
+
{
|
|
3187
|
+
sql: `UPDATE memories
|
|
3188
|
+
SET status = 'archived',
|
|
3189
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3190
|
+
WHERE id = ?`,
|
|
3191
|
+
args: [supersedesId]
|
|
3192
|
+
},
|
|
3193
|
+
{
|
|
3194
|
+
sql: `UPDATE memories
|
|
3195
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3196
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3197
|
+
WHERE id = ?`,
|
|
3198
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3199
|
+
}
|
|
3200
|
+
], "write");
|
|
3201
|
+
}
|
|
3202
|
+
} catch (err) {
|
|
3203
|
+
process.stderr.write(
|
|
3204
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3205
|
+
`
|
|
3206
|
+
);
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3210
|
+
if (memoryIds.length === 0) return;
|
|
3211
|
+
const run = () => {
|
|
3212
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3213
|
+
};
|
|
3214
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3215
|
+
else setTimeout(run, 0);
|
|
3216
|
+
}
|
|
3217
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3218
|
+
var init_memory_write_governor = __esm({
|
|
3219
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3220
|
+
"use strict";
|
|
3221
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3222
|
+
"decision",
|
|
3223
|
+
"adr",
|
|
3224
|
+
"behavior",
|
|
3225
|
+
"procedure"
|
|
3226
|
+
]);
|
|
3227
|
+
NOISE_DROP_PATTERNS = [
|
|
3228
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3229
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3230
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3231
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3232
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3233
|
+
];
|
|
3234
|
+
SKIP_EMBED_PATTERNS = [
|
|
3235
|
+
/tmux capture-pane\b/i,
|
|
3236
|
+
/docker ps\b/i,
|
|
3237
|
+
/docker images\b/i,
|
|
3238
|
+
/git status\b/i,
|
|
3239
|
+
/grep .*node_modules/i,
|
|
3240
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3241
|
+
];
|
|
3242
|
+
}
|
|
3243
|
+
});
|
|
3244
|
+
|
|
3047
3245
|
// src/lib/shard-manager.ts
|
|
3048
3246
|
var shard_manager_exports = {};
|
|
3049
3247
|
__export(shard_manager_exports, {
|
|
@@ -3208,7 +3406,8 @@ async function ensureShardSchema(client) {
|
|
|
3208
3406
|
}
|
|
3209
3407
|
for (const idx of [
|
|
3210
3408
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3211
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3409
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3410
|
+
"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"
|
|
3212
3411
|
]) {
|
|
3213
3412
|
try {
|
|
3214
3413
|
await client.execute(idx);
|
|
@@ -3633,7 +3832,6 @@ __export(store_exports, {
|
|
|
3633
3832
|
vectorToBlob: () => vectorToBlob,
|
|
3634
3833
|
writeMemory: () => writeMemory
|
|
3635
3834
|
});
|
|
3636
|
-
import { createHash } from "crypto";
|
|
3637
3835
|
function isBusyError2(err) {
|
|
3638
3836
|
if (err instanceof Error) {
|
|
3639
3837
|
const msg = err.message.toLowerCase();
|
|
@@ -3747,17 +3945,24 @@ async function writeMemory(record) {
|
|
|
3747
3945
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3748
3946
|
);
|
|
3749
3947
|
}
|
|
3750
|
-
const
|
|
3751
|
-
if (
|
|
3948
|
+
const governed = governMemoryRecord(record);
|
|
3949
|
+
if (governed.shouldDrop) return;
|
|
3950
|
+
record = governed.record;
|
|
3951
|
+
const contentHash = governed.contentHash;
|
|
3952
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3953
|
+
if (_pendingRecords.some(
|
|
3954
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3955
|
+
)) {
|
|
3752
3956
|
return;
|
|
3753
3957
|
}
|
|
3754
3958
|
try {
|
|
3755
|
-
const
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3959
|
+
const existing = await findScopedDuplicate({
|
|
3960
|
+
contentHash,
|
|
3961
|
+
agentId: record.agent_id,
|
|
3962
|
+
projectName: record.project_name,
|
|
3963
|
+
memoryType
|
|
3759
3964
|
});
|
|
3760
|
-
if (existing
|
|
3965
|
+
if (existing) return;
|
|
3761
3966
|
} catch {
|
|
3762
3967
|
}
|
|
3763
3968
|
const dbRow = {
|
|
@@ -3788,7 +3993,7 @@ async function writeMemory(record) {
|
|
|
3788
3993
|
tier: record.tier ?? classifyTier(record),
|
|
3789
3994
|
supersedes_id: record.supersedes_id ?? null,
|
|
3790
3995
|
draft: record.draft ? 1 : 0,
|
|
3791
|
-
memory_type:
|
|
3996
|
+
memory_type: memoryType,
|
|
3792
3997
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3793
3998
|
content_hash: contentHash,
|
|
3794
3999
|
intent: record.intent ?? null,
|
|
@@ -3947,6 +4152,7 @@ async function flushBatch() {
|
|
|
3947
4152
|
const globalClient = getClient();
|
|
3948
4153
|
const globalStmts = batch.map(buildStmt);
|
|
3949
4154
|
await globalClient.batch(globalStmts, "write");
|
|
4155
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3950
4156
|
_pendingRecords.splice(0, batch.length);
|
|
3951
4157
|
try {
|
|
3952
4158
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4205,6 +4411,7 @@ var init_store = __esm({
|
|
|
4205
4411
|
init_keychain();
|
|
4206
4412
|
init_config();
|
|
4207
4413
|
init_state_bus();
|
|
4414
|
+
init_memory_write_governor();
|
|
4208
4415
|
INIT_MAX_RETRIES = 3;
|
|
4209
4416
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4210
4417
|
_pendingRecords = [];
|
package/dist/bin/git-sweep.js
CHANGED
|
@@ -2801,6 +2801,14 @@ async function ensureSchema() {
|
|
|
2801
2801
|
);
|
|
2802
2802
|
} catch {
|
|
2803
2803
|
}
|
|
2804
|
+
try {
|
|
2805
|
+
await client.execute(
|
|
2806
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2807
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2808
|
+
WHERE content_hash IS NOT NULL`
|
|
2809
|
+
);
|
|
2810
|
+
} catch {
|
|
2811
|
+
}
|
|
2804
2812
|
await client.executeMultiple(`
|
|
2805
2813
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2806
2814
|
id TEXT PRIMARY KEY,
|
|
@@ -6316,7 +6324,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6316
6324
|
}
|
|
6317
6325
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6318
6326
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
6319
|
-
|
|
6327
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
6328
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
6329
|
+
ccModel += "[1m]";
|
|
6330
|
+
}
|
|
6331
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
6320
6332
|
}
|
|
6321
6333
|
}
|
|
6322
6334
|
let spawnCommand;
|
|
@@ -6663,6 +6675,196 @@ var init_keychain = __esm({
|
|
|
6663
6675
|
}
|
|
6664
6676
|
});
|
|
6665
6677
|
|
|
6678
|
+
// src/lib/memory-write-governor.ts
|
|
6679
|
+
import { createHash } from "crypto";
|
|
6680
|
+
function normalizeMemoryText(text) {
|
|
6681
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
6682
|
+
}
|
|
6683
|
+
function classifyMemoryType(input) {
|
|
6684
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
6685
|
+
const tool = input.tool_name.toLowerCase();
|
|
6686
|
+
const text = input.raw_text.toLowerCase();
|
|
6687
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
6688
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
6689
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
6690
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
6691
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
6692
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
6693
|
+
return "raw";
|
|
6694
|
+
}
|
|
6695
|
+
function shouldDropMemory(text) {
|
|
6696
|
+
const normalized = normalizeMemoryText(text);
|
|
6697
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
6698
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
6699
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
6700
|
+
}
|
|
6701
|
+
return { drop: false };
|
|
6702
|
+
}
|
|
6703
|
+
function shouldSkipEmbedding(input) {
|
|
6704
|
+
const type = classifyMemoryType(input);
|
|
6705
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
6706
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
6707
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
6708
|
+
return false;
|
|
6709
|
+
}
|
|
6710
|
+
function hashMemoryContent(text) {
|
|
6711
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
6712
|
+
}
|
|
6713
|
+
function scopedDedupArgs(input) {
|
|
6714
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
6715
|
+
}
|
|
6716
|
+
function governMemoryRecord(record) {
|
|
6717
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
6718
|
+
const memoryType = classifyMemoryType({
|
|
6719
|
+
raw_text: normalized,
|
|
6720
|
+
agent_id: record.agent_id,
|
|
6721
|
+
project_name: record.project_name,
|
|
6722
|
+
tool_name: record.tool_name,
|
|
6723
|
+
memory_type: record.memory_type
|
|
6724
|
+
});
|
|
6725
|
+
const drop = shouldDropMemory(normalized);
|
|
6726
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
6727
|
+
raw_text: normalized,
|
|
6728
|
+
agent_id: record.agent_id,
|
|
6729
|
+
project_name: record.project_name,
|
|
6730
|
+
tool_name: record.tool_name,
|
|
6731
|
+
memory_type: memoryType
|
|
6732
|
+
});
|
|
6733
|
+
return {
|
|
6734
|
+
record: {
|
|
6735
|
+
...record,
|
|
6736
|
+
raw_text: normalized,
|
|
6737
|
+
memory_type: memoryType,
|
|
6738
|
+
vector: skipEmbedding ? null : record.vector
|
|
6739
|
+
},
|
|
6740
|
+
contentHash: hashMemoryContent(normalized),
|
|
6741
|
+
shouldDrop: drop.drop,
|
|
6742
|
+
dropReason: drop.reason,
|
|
6743
|
+
skipEmbedding,
|
|
6744
|
+
hygiene: {
|
|
6745
|
+
dedup: true,
|
|
6746
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
6747
|
+
}
|
|
6748
|
+
};
|
|
6749
|
+
}
|
|
6750
|
+
async function findScopedDuplicate(input) {
|
|
6751
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6752
|
+
const client = getClient2();
|
|
6753
|
+
const args2 = scopedDedupArgs(input);
|
|
6754
|
+
let sql = `SELECT id FROM memories
|
|
6755
|
+
WHERE content_hash = ?
|
|
6756
|
+
AND agent_id = ?
|
|
6757
|
+
AND project_name = ?
|
|
6758
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6759
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
6760
|
+
if (input.excludeId) {
|
|
6761
|
+
sql += " AND id != ?";
|
|
6762
|
+
args2.push(input.excludeId);
|
|
6763
|
+
}
|
|
6764
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
6765
|
+
const result = await client.execute({ sql, args: args2 });
|
|
6766
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
6767
|
+
}
|
|
6768
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
6769
|
+
try {
|
|
6770
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6771
|
+
const client = getClient2();
|
|
6772
|
+
const current = await client.execute({
|
|
6773
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
6774
|
+
importance, timestamp
|
|
6775
|
+
FROM memories
|
|
6776
|
+
WHERE id = ?
|
|
6777
|
+
LIMIT 1`,
|
|
6778
|
+
args: [memoryId]
|
|
6779
|
+
});
|
|
6780
|
+
const row = current.rows[0];
|
|
6781
|
+
if (!row) return;
|
|
6782
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
6783
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
6784
|
+
const agentId = String(row.agent_id);
|
|
6785
|
+
const projectName2 = String(row.project_name);
|
|
6786
|
+
if (contentHash) {
|
|
6787
|
+
await client.execute({
|
|
6788
|
+
sql: `UPDATE memories
|
|
6789
|
+
SET status = 'deleted',
|
|
6790
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6791
|
+
WHERE id != ?
|
|
6792
|
+
AND content_hash = ?
|
|
6793
|
+
AND agent_id = ?
|
|
6794
|
+
AND project_name = ?
|
|
6795
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6796
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
6797
|
+
args: [memoryId, contentHash, agentId, projectName2, memoryType]
|
|
6798
|
+
});
|
|
6799
|
+
}
|
|
6800
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
6801
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
6802
|
+
const old = await client.execute({
|
|
6803
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
6804
|
+
args: [supersedesId]
|
|
6805
|
+
});
|
|
6806
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
6807
|
+
const newImportance = Number(row.importance ?? 0);
|
|
6808
|
+
await client.batch([
|
|
6809
|
+
{
|
|
6810
|
+
sql: `UPDATE memories
|
|
6811
|
+
SET status = 'archived',
|
|
6812
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6813
|
+
WHERE id = ?`,
|
|
6814
|
+
args: [supersedesId]
|
|
6815
|
+
},
|
|
6816
|
+
{
|
|
6817
|
+
sql: `UPDATE memories
|
|
6818
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
6819
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
6820
|
+
WHERE id = ?`,
|
|
6821
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
6822
|
+
}
|
|
6823
|
+
], "write");
|
|
6824
|
+
}
|
|
6825
|
+
} catch (err) {
|
|
6826
|
+
process.stderr.write(
|
|
6827
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
6828
|
+
`
|
|
6829
|
+
);
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
6833
|
+
if (memoryIds.length === 0) return;
|
|
6834
|
+
const run = () => {
|
|
6835
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
6836
|
+
};
|
|
6837
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
6838
|
+
else setTimeout(run, 0);
|
|
6839
|
+
}
|
|
6840
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
6841
|
+
var init_memory_write_governor = __esm({
|
|
6842
|
+
"src/lib/memory-write-governor.ts"() {
|
|
6843
|
+
"use strict";
|
|
6844
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
6845
|
+
"decision",
|
|
6846
|
+
"adr",
|
|
6847
|
+
"behavior",
|
|
6848
|
+
"procedure"
|
|
6849
|
+
]);
|
|
6850
|
+
NOISE_DROP_PATTERNS = [
|
|
6851
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
6852
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
6853
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
6854
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
6855
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
6856
|
+
];
|
|
6857
|
+
SKIP_EMBED_PATTERNS = [
|
|
6858
|
+
/tmux capture-pane\b/i,
|
|
6859
|
+
/docker ps\b/i,
|
|
6860
|
+
/docker images\b/i,
|
|
6861
|
+
/git status\b/i,
|
|
6862
|
+
/grep .*node_modules/i,
|
|
6863
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
6864
|
+
];
|
|
6865
|
+
}
|
|
6866
|
+
});
|
|
6867
|
+
|
|
6666
6868
|
// src/lib/shard-manager.ts
|
|
6667
6869
|
var shard_manager_exports = {};
|
|
6668
6870
|
__export(shard_manager_exports, {
|
|
@@ -6827,7 +7029,8 @@ async function ensureShardSchema(client) {
|
|
|
6827
7029
|
}
|
|
6828
7030
|
for (const idx of [
|
|
6829
7031
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
6830
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
7032
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
7033
|
+
"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"
|
|
6831
7034
|
]) {
|
|
6832
7035
|
try {
|
|
6833
7036
|
await client.execute(idx);
|
|
@@ -7252,7 +7455,6 @@ __export(store_exports, {
|
|
|
7252
7455
|
vectorToBlob: () => vectorToBlob,
|
|
7253
7456
|
writeMemory: () => writeMemory
|
|
7254
7457
|
});
|
|
7255
|
-
import { createHash } from "crypto";
|
|
7256
7458
|
function isBusyError2(err) {
|
|
7257
7459
|
if (err instanceof Error) {
|
|
7258
7460
|
const msg = err.message.toLowerCase();
|
|
@@ -7366,17 +7568,24 @@ async function writeMemory(record) {
|
|
|
7366
7568
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
7367
7569
|
);
|
|
7368
7570
|
}
|
|
7369
|
-
const
|
|
7370
|
-
if (
|
|
7571
|
+
const governed = governMemoryRecord(record);
|
|
7572
|
+
if (governed.shouldDrop) return;
|
|
7573
|
+
record = governed.record;
|
|
7574
|
+
const contentHash = governed.contentHash;
|
|
7575
|
+
const memoryType = record.memory_type ?? "raw";
|
|
7576
|
+
if (_pendingRecords.some(
|
|
7577
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
7578
|
+
)) {
|
|
7371
7579
|
return;
|
|
7372
7580
|
}
|
|
7373
7581
|
try {
|
|
7374
|
-
const
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7582
|
+
const existing = await findScopedDuplicate({
|
|
7583
|
+
contentHash,
|
|
7584
|
+
agentId: record.agent_id,
|
|
7585
|
+
projectName: record.project_name,
|
|
7586
|
+
memoryType
|
|
7378
7587
|
});
|
|
7379
|
-
if (existing
|
|
7588
|
+
if (existing) return;
|
|
7380
7589
|
} catch {
|
|
7381
7590
|
}
|
|
7382
7591
|
const dbRow = {
|
|
@@ -7407,7 +7616,7 @@ async function writeMemory(record) {
|
|
|
7407
7616
|
tier: record.tier ?? classifyTier(record),
|
|
7408
7617
|
supersedes_id: record.supersedes_id ?? null,
|
|
7409
7618
|
draft: record.draft ? 1 : 0,
|
|
7410
|
-
memory_type:
|
|
7619
|
+
memory_type: memoryType,
|
|
7411
7620
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
7412
7621
|
content_hash: contentHash,
|
|
7413
7622
|
intent: record.intent ?? null,
|
|
@@ -7566,6 +7775,7 @@ async function flushBatch() {
|
|
|
7566
7775
|
const globalClient = getClient();
|
|
7567
7776
|
const globalStmts = batch.map(buildStmt);
|
|
7568
7777
|
await globalClient.batch(globalStmts, "write");
|
|
7778
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
7569
7779
|
_pendingRecords.splice(0, batch.length);
|
|
7570
7780
|
try {
|
|
7571
7781
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -7824,6 +8034,7 @@ var init_store = __esm({
|
|
|
7824
8034
|
init_keychain();
|
|
7825
8035
|
init_config();
|
|
7826
8036
|
init_state_bus();
|
|
8037
|
+
init_memory_write_governor();
|
|
7827
8038
|
INIT_MAX_RETRIES = 3;
|
|
7828
8039
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
7829
8040
|
_pendingRecords = [];
|