@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-kill.js
CHANGED
|
@@ -2288,6 +2288,14 @@ async function ensureSchema() {
|
|
|
2288
2288
|
);
|
|
2289
2289
|
} catch {
|
|
2290
2290
|
}
|
|
2291
|
+
try {
|
|
2292
|
+
await client.execute(
|
|
2293
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2294
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2295
|
+
WHERE content_hash IS NOT NULL`
|
|
2296
|
+
);
|
|
2297
|
+
} catch {
|
|
2298
|
+
}
|
|
2291
2299
|
await client.executeMultiple(`
|
|
2292
2300
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2293
2301
|
id TEXT PRIMARY KEY,
|
|
@@ -3033,6 +3041,196 @@ var init_state_bus = __esm({
|
|
|
3033
3041
|
}
|
|
3034
3042
|
});
|
|
3035
3043
|
|
|
3044
|
+
// src/lib/memory-write-governor.ts
|
|
3045
|
+
import { createHash } from "crypto";
|
|
3046
|
+
function normalizeMemoryText(text) {
|
|
3047
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3048
|
+
}
|
|
3049
|
+
function classifyMemoryType(input) {
|
|
3050
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3051
|
+
const tool = input.tool_name.toLowerCase();
|
|
3052
|
+
const text = input.raw_text.toLowerCase();
|
|
3053
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3054
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3055
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3056
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3057
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3058
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3059
|
+
return "raw";
|
|
3060
|
+
}
|
|
3061
|
+
function shouldDropMemory(text) {
|
|
3062
|
+
const normalized = normalizeMemoryText(text);
|
|
3063
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3064
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3065
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3066
|
+
}
|
|
3067
|
+
return { drop: false };
|
|
3068
|
+
}
|
|
3069
|
+
function shouldSkipEmbedding(input) {
|
|
3070
|
+
const type = classifyMemoryType(input);
|
|
3071
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3072
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3073
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3074
|
+
return false;
|
|
3075
|
+
}
|
|
3076
|
+
function hashMemoryContent(text) {
|
|
3077
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3078
|
+
}
|
|
3079
|
+
function scopedDedupArgs(input) {
|
|
3080
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3081
|
+
}
|
|
3082
|
+
function governMemoryRecord(record) {
|
|
3083
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3084
|
+
const memoryType = classifyMemoryType({
|
|
3085
|
+
raw_text: normalized,
|
|
3086
|
+
agent_id: record.agent_id,
|
|
3087
|
+
project_name: record.project_name,
|
|
3088
|
+
tool_name: record.tool_name,
|
|
3089
|
+
memory_type: record.memory_type
|
|
3090
|
+
});
|
|
3091
|
+
const drop = shouldDropMemory(normalized);
|
|
3092
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3093
|
+
raw_text: normalized,
|
|
3094
|
+
agent_id: record.agent_id,
|
|
3095
|
+
project_name: record.project_name,
|
|
3096
|
+
tool_name: record.tool_name,
|
|
3097
|
+
memory_type: memoryType
|
|
3098
|
+
});
|
|
3099
|
+
return {
|
|
3100
|
+
record: {
|
|
3101
|
+
...record,
|
|
3102
|
+
raw_text: normalized,
|
|
3103
|
+
memory_type: memoryType,
|
|
3104
|
+
vector: skipEmbedding ? null : record.vector
|
|
3105
|
+
},
|
|
3106
|
+
contentHash: hashMemoryContent(normalized),
|
|
3107
|
+
shouldDrop: drop.drop,
|
|
3108
|
+
dropReason: drop.reason,
|
|
3109
|
+
skipEmbedding,
|
|
3110
|
+
hygiene: {
|
|
3111
|
+
dedup: true,
|
|
3112
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3113
|
+
}
|
|
3114
|
+
};
|
|
3115
|
+
}
|
|
3116
|
+
async function findScopedDuplicate(input) {
|
|
3117
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3118
|
+
const client = getClient2();
|
|
3119
|
+
const args = scopedDedupArgs(input);
|
|
3120
|
+
let sql = `SELECT id FROM memories
|
|
3121
|
+
WHERE content_hash = ?
|
|
3122
|
+
AND agent_id = ?
|
|
3123
|
+
AND project_name = ?
|
|
3124
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3125
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3126
|
+
if (input.excludeId) {
|
|
3127
|
+
sql += " AND id != ?";
|
|
3128
|
+
args.push(input.excludeId);
|
|
3129
|
+
}
|
|
3130
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3131
|
+
const result = await client.execute({ sql, args });
|
|
3132
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3133
|
+
}
|
|
3134
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3135
|
+
try {
|
|
3136
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3137
|
+
const client = getClient2();
|
|
3138
|
+
const current = await client.execute({
|
|
3139
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3140
|
+
importance, timestamp
|
|
3141
|
+
FROM memories
|
|
3142
|
+
WHERE id = ?
|
|
3143
|
+
LIMIT 1`,
|
|
3144
|
+
args: [memoryId]
|
|
3145
|
+
});
|
|
3146
|
+
const row = current.rows[0];
|
|
3147
|
+
if (!row) return;
|
|
3148
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3149
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3150
|
+
const agentId = String(row.agent_id);
|
|
3151
|
+
const projectName = String(row.project_name);
|
|
3152
|
+
if (contentHash) {
|
|
3153
|
+
await client.execute({
|
|
3154
|
+
sql: `UPDATE memories
|
|
3155
|
+
SET status = 'deleted',
|
|
3156
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3157
|
+
WHERE id != ?
|
|
3158
|
+
AND content_hash = ?
|
|
3159
|
+
AND agent_id = ?
|
|
3160
|
+
AND project_name = ?
|
|
3161
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3162
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3163
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3164
|
+
});
|
|
3165
|
+
}
|
|
3166
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3167
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3168
|
+
const old = await client.execute({
|
|
3169
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3170
|
+
args: [supersedesId]
|
|
3171
|
+
});
|
|
3172
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3173
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3174
|
+
await client.batch([
|
|
3175
|
+
{
|
|
3176
|
+
sql: `UPDATE memories
|
|
3177
|
+
SET status = 'archived',
|
|
3178
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3179
|
+
WHERE id = ?`,
|
|
3180
|
+
args: [supersedesId]
|
|
3181
|
+
},
|
|
3182
|
+
{
|
|
3183
|
+
sql: `UPDATE memories
|
|
3184
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3185
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3186
|
+
WHERE id = ?`,
|
|
3187
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3188
|
+
}
|
|
3189
|
+
], "write");
|
|
3190
|
+
}
|
|
3191
|
+
} catch (err) {
|
|
3192
|
+
process.stderr.write(
|
|
3193
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3194
|
+
`
|
|
3195
|
+
);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3199
|
+
if (memoryIds.length === 0) return;
|
|
3200
|
+
const run = () => {
|
|
3201
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3202
|
+
};
|
|
3203
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3204
|
+
else setTimeout(run, 0);
|
|
3205
|
+
}
|
|
3206
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3207
|
+
var init_memory_write_governor = __esm({
|
|
3208
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3209
|
+
"use strict";
|
|
3210
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3211
|
+
"decision",
|
|
3212
|
+
"adr",
|
|
3213
|
+
"behavior",
|
|
3214
|
+
"procedure"
|
|
3215
|
+
]);
|
|
3216
|
+
NOISE_DROP_PATTERNS = [
|
|
3217
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3218
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3219
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3220
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3221
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3222
|
+
];
|
|
3223
|
+
SKIP_EMBED_PATTERNS = [
|
|
3224
|
+
/tmux capture-pane\b/i,
|
|
3225
|
+
/docker ps\b/i,
|
|
3226
|
+
/docker images\b/i,
|
|
3227
|
+
/git status\b/i,
|
|
3228
|
+
/grep .*node_modules/i,
|
|
3229
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3230
|
+
];
|
|
3231
|
+
}
|
|
3232
|
+
});
|
|
3233
|
+
|
|
3036
3234
|
// src/lib/shard-manager.ts
|
|
3037
3235
|
var shard_manager_exports = {};
|
|
3038
3236
|
__export(shard_manager_exports, {
|
|
@@ -3197,7 +3395,8 @@ async function ensureShardSchema(client) {
|
|
|
3197
3395
|
}
|
|
3198
3396
|
for (const idx of [
|
|
3199
3397
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3200
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3398
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3399
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
|
|
3201
3400
|
]) {
|
|
3202
3401
|
try {
|
|
3203
3402
|
await client.execute(idx);
|
|
@@ -3622,7 +3821,6 @@ __export(store_exports, {
|
|
|
3622
3821
|
vectorToBlob: () => vectorToBlob,
|
|
3623
3822
|
writeMemory: () => writeMemory
|
|
3624
3823
|
});
|
|
3625
|
-
import { createHash } from "crypto";
|
|
3626
3824
|
function isBusyError2(err) {
|
|
3627
3825
|
if (err instanceof Error) {
|
|
3628
3826
|
const msg = err.message.toLowerCase();
|
|
@@ -3736,17 +3934,24 @@ async function writeMemory(record) {
|
|
|
3736
3934
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3737
3935
|
);
|
|
3738
3936
|
}
|
|
3739
|
-
const
|
|
3740
|
-
if (
|
|
3937
|
+
const governed = governMemoryRecord(record);
|
|
3938
|
+
if (governed.shouldDrop) return;
|
|
3939
|
+
record = governed.record;
|
|
3940
|
+
const contentHash = governed.contentHash;
|
|
3941
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3942
|
+
if (_pendingRecords.some(
|
|
3943
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3944
|
+
)) {
|
|
3741
3945
|
return;
|
|
3742
3946
|
}
|
|
3743
3947
|
try {
|
|
3744
|
-
const
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3948
|
+
const existing = await findScopedDuplicate({
|
|
3949
|
+
contentHash,
|
|
3950
|
+
agentId: record.agent_id,
|
|
3951
|
+
projectName: record.project_name,
|
|
3952
|
+
memoryType
|
|
3748
3953
|
});
|
|
3749
|
-
if (existing
|
|
3954
|
+
if (existing) return;
|
|
3750
3955
|
} catch {
|
|
3751
3956
|
}
|
|
3752
3957
|
const dbRow = {
|
|
@@ -3777,7 +3982,7 @@ async function writeMemory(record) {
|
|
|
3777
3982
|
tier: record.tier ?? classifyTier(record),
|
|
3778
3983
|
supersedes_id: record.supersedes_id ?? null,
|
|
3779
3984
|
draft: record.draft ? 1 : 0,
|
|
3780
|
-
memory_type:
|
|
3985
|
+
memory_type: memoryType,
|
|
3781
3986
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3782
3987
|
content_hash: contentHash,
|
|
3783
3988
|
intent: record.intent ?? null,
|
|
@@ -3936,6 +4141,7 @@ async function flushBatch() {
|
|
|
3936
4141
|
const globalClient = getClient();
|
|
3937
4142
|
const globalStmts = batch.map(buildStmt);
|
|
3938
4143
|
await globalClient.batch(globalStmts, "write");
|
|
4144
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3939
4145
|
_pendingRecords.splice(0, batch.length);
|
|
3940
4146
|
try {
|
|
3941
4147
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4194,6 +4400,7 @@ var init_store = __esm({
|
|
|
4194
4400
|
init_keychain();
|
|
4195
4401
|
init_config();
|
|
4196
4402
|
init_state_bus();
|
|
4403
|
+
init_memory_write_governor();
|
|
4197
4404
|
INIT_MAX_RETRIES = 3;
|
|
4198
4405
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4199
4406
|
_pendingRecords = [];
|
|
@@ -2407,6 +2407,14 @@ async function ensureSchema() {
|
|
|
2407
2407
|
);
|
|
2408
2408
|
} catch {
|
|
2409
2409
|
}
|
|
2410
|
+
try {
|
|
2411
|
+
await client.execute(
|
|
2412
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2413
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2414
|
+
WHERE content_hash IS NOT NULL`
|
|
2415
|
+
);
|
|
2416
|
+
} catch {
|
|
2417
|
+
}
|
|
2410
2418
|
await client.executeMultiple(`
|
|
2411
2419
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2412
2420
|
id TEXT PRIMARY KEY,
|
|
@@ -3076,7 +3084,8 @@ async function ensureShardSchema(client) {
|
|
|
3076
3084
|
}
|
|
3077
3085
|
for (const idx of [
|
|
3078
3086
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3079
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3087
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3088
|
+
"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"
|
|
3080
3089
|
]) {
|
|
3081
3090
|
try {
|
|
3082
3091
|
await client.execute(idx);
|
|
@@ -4368,7 +4377,6 @@ import { spawnSync } from "child_process";
|
|
|
4368
4377
|
// src/lib/store.ts
|
|
4369
4378
|
init_memory();
|
|
4370
4379
|
init_database();
|
|
4371
|
-
import { createHash } from "crypto";
|
|
4372
4380
|
|
|
4373
4381
|
// src/lib/keychain.ts
|
|
4374
4382
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
@@ -4601,6 +4609,87 @@ var StateBus = class {
|
|
|
4601
4609
|
};
|
|
4602
4610
|
var orgBus = new StateBus();
|
|
4603
4611
|
|
|
4612
|
+
// src/lib/memory-write-governor.ts
|
|
4613
|
+
import { createHash } from "crypto";
|
|
4614
|
+
var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
4615
|
+
"decision",
|
|
4616
|
+
"adr",
|
|
4617
|
+
"behavior",
|
|
4618
|
+
"procedure"
|
|
4619
|
+
]);
|
|
4620
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
4621
|
+
try {
|
|
4622
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4623
|
+
const client = getClient2();
|
|
4624
|
+
const current = await client.execute({
|
|
4625
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
4626
|
+
importance, timestamp
|
|
4627
|
+
FROM memories
|
|
4628
|
+
WHERE id = ?
|
|
4629
|
+
LIMIT 1`,
|
|
4630
|
+
args: [memoryId]
|
|
4631
|
+
});
|
|
4632
|
+
const row = current.rows[0];
|
|
4633
|
+
if (!row) return;
|
|
4634
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
4635
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
4636
|
+
const agentId = String(row.agent_id);
|
|
4637
|
+
const projectName = String(row.project_name);
|
|
4638
|
+
if (contentHash) {
|
|
4639
|
+
await client.execute({
|
|
4640
|
+
sql: `UPDATE memories
|
|
4641
|
+
SET status = 'deleted',
|
|
4642
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4643
|
+
WHERE id != ?
|
|
4644
|
+
AND content_hash = ?
|
|
4645
|
+
AND agent_id = ?
|
|
4646
|
+
AND project_name = ?
|
|
4647
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
4648
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
4649
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
4650
|
+
});
|
|
4651
|
+
}
|
|
4652
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
4653
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
4654
|
+
const old = await client.execute({
|
|
4655
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
4656
|
+
args: [supersedesId]
|
|
4657
|
+
});
|
|
4658
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
4659
|
+
const newImportance = Number(row.importance ?? 0);
|
|
4660
|
+
await client.batch([
|
|
4661
|
+
{
|
|
4662
|
+
sql: `UPDATE memories
|
|
4663
|
+
SET status = 'archived',
|
|
4664
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4665
|
+
WHERE id = ?`,
|
|
4666
|
+
args: [supersedesId]
|
|
4667
|
+
},
|
|
4668
|
+
{
|
|
4669
|
+
sql: `UPDATE memories
|
|
4670
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
4671
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
4672
|
+
WHERE id = ?`,
|
|
4673
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
4674
|
+
}
|
|
4675
|
+
], "write");
|
|
4676
|
+
}
|
|
4677
|
+
} catch (err) {
|
|
4678
|
+
process.stderr.write(
|
|
4679
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
4680
|
+
`
|
|
4681
|
+
);
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
4685
|
+
if (memoryIds.length === 0) return;
|
|
4686
|
+
const run = () => {
|
|
4687
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
4688
|
+
};
|
|
4689
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
4690
|
+
else setTimeout(run, 0);
|
|
4691
|
+
}
|
|
4692
|
+
|
|
4604
4693
|
// src/lib/store.ts
|
|
4605
4694
|
var INIT_MAX_RETRIES = 3;
|
|
4606
4695
|
var INIT_RETRY_DELAY_MS = 1e3;
|
|
@@ -4800,6 +4889,7 @@ async function flushBatch() {
|
|
|
4800
4889
|
const globalClient = getClient();
|
|
4801
4890
|
const globalStmts = batch.map(buildStmt);
|
|
4802
4891
|
await globalClient.batch(globalStmts, "write");
|
|
4892
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4803
4893
|
_pendingRecords.splice(0, batch.length);
|
|
4804
4894
|
try {
|
|
4805
4895
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
package/dist/bin/exe-link.js
CHANGED
|
@@ -2542,6 +2542,14 @@ async function ensureSchema() {
|
|
|
2542
2542
|
);
|
|
2543
2543
|
} catch {
|
|
2544
2544
|
}
|
|
2545
|
+
try {
|
|
2546
|
+
await client.execute(
|
|
2547
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2548
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2549
|
+
WHERE content_hash IS NOT NULL`
|
|
2550
|
+
);
|
|
2551
|
+
} catch {
|
|
2552
|
+
}
|
|
2545
2553
|
await client.executeMultiple(`
|
|
2546
2554
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2547
2555
|
id TEXT PRIMARY KEY,
|