@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
|
@@ -2387,6 +2387,14 @@ async function ensureSchema() {
|
|
|
2387
2387
|
);
|
|
2388
2388
|
} catch {
|
|
2389
2389
|
}
|
|
2390
|
+
try {
|
|
2391
|
+
await client.execute(
|
|
2392
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2393
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2394
|
+
WHERE content_hash IS NOT NULL`
|
|
2395
|
+
);
|
|
2396
|
+
} catch {
|
|
2397
|
+
}
|
|
2390
2398
|
await client.executeMultiple(`
|
|
2391
2399
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2392
2400
|
id TEXT PRIMARY KEY,
|
|
@@ -3132,6 +3140,196 @@ var init_state_bus = __esm({
|
|
|
3132
3140
|
}
|
|
3133
3141
|
});
|
|
3134
3142
|
|
|
3143
|
+
// src/lib/memory-write-governor.ts
|
|
3144
|
+
import { createHash } from "crypto";
|
|
3145
|
+
function normalizeMemoryText(text) {
|
|
3146
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3147
|
+
}
|
|
3148
|
+
function classifyMemoryType(input) {
|
|
3149
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3150
|
+
const tool = input.tool_name.toLowerCase();
|
|
3151
|
+
const text = input.raw_text.toLowerCase();
|
|
3152
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3153
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3154
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3155
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3156
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3157
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3158
|
+
return "raw";
|
|
3159
|
+
}
|
|
3160
|
+
function shouldDropMemory(text) {
|
|
3161
|
+
const normalized = normalizeMemoryText(text);
|
|
3162
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3163
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3164
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3165
|
+
}
|
|
3166
|
+
return { drop: false };
|
|
3167
|
+
}
|
|
3168
|
+
function shouldSkipEmbedding(input) {
|
|
3169
|
+
const type = classifyMemoryType(input);
|
|
3170
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3171
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3172
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3173
|
+
return false;
|
|
3174
|
+
}
|
|
3175
|
+
function hashMemoryContent(text) {
|
|
3176
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3177
|
+
}
|
|
3178
|
+
function scopedDedupArgs(input) {
|
|
3179
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3180
|
+
}
|
|
3181
|
+
function governMemoryRecord(record) {
|
|
3182
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3183
|
+
const memoryType = classifyMemoryType({
|
|
3184
|
+
raw_text: normalized,
|
|
3185
|
+
agent_id: record.agent_id,
|
|
3186
|
+
project_name: record.project_name,
|
|
3187
|
+
tool_name: record.tool_name,
|
|
3188
|
+
memory_type: record.memory_type
|
|
3189
|
+
});
|
|
3190
|
+
const drop = shouldDropMemory(normalized);
|
|
3191
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3192
|
+
raw_text: normalized,
|
|
3193
|
+
agent_id: record.agent_id,
|
|
3194
|
+
project_name: record.project_name,
|
|
3195
|
+
tool_name: record.tool_name,
|
|
3196
|
+
memory_type: memoryType
|
|
3197
|
+
});
|
|
3198
|
+
return {
|
|
3199
|
+
record: {
|
|
3200
|
+
...record,
|
|
3201
|
+
raw_text: normalized,
|
|
3202
|
+
memory_type: memoryType,
|
|
3203
|
+
vector: skipEmbedding ? null : record.vector
|
|
3204
|
+
},
|
|
3205
|
+
contentHash: hashMemoryContent(normalized),
|
|
3206
|
+
shouldDrop: drop.drop,
|
|
3207
|
+
dropReason: drop.reason,
|
|
3208
|
+
skipEmbedding,
|
|
3209
|
+
hygiene: {
|
|
3210
|
+
dedup: true,
|
|
3211
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3212
|
+
}
|
|
3213
|
+
};
|
|
3214
|
+
}
|
|
3215
|
+
async function findScopedDuplicate(input) {
|
|
3216
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3217
|
+
const client = getClient2();
|
|
3218
|
+
const args = scopedDedupArgs(input);
|
|
3219
|
+
let sql = `SELECT id FROM memories
|
|
3220
|
+
WHERE content_hash = ?
|
|
3221
|
+
AND agent_id = ?
|
|
3222
|
+
AND project_name = ?
|
|
3223
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3224
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3225
|
+
if (input.excludeId) {
|
|
3226
|
+
sql += " AND id != ?";
|
|
3227
|
+
args.push(input.excludeId);
|
|
3228
|
+
}
|
|
3229
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3230
|
+
const result = await client.execute({ sql, args });
|
|
3231
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3232
|
+
}
|
|
3233
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3234
|
+
try {
|
|
3235
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3236
|
+
const client = getClient2();
|
|
3237
|
+
const current = await client.execute({
|
|
3238
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3239
|
+
importance, timestamp
|
|
3240
|
+
FROM memories
|
|
3241
|
+
WHERE id = ?
|
|
3242
|
+
LIMIT 1`,
|
|
3243
|
+
args: [memoryId]
|
|
3244
|
+
});
|
|
3245
|
+
const row = current.rows[0];
|
|
3246
|
+
if (!row) return;
|
|
3247
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3248
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3249
|
+
const agentId = String(row.agent_id);
|
|
3250
|
+
const projectName = String(row.project_name);
|
|
3251
|
+
if (contentHash) {
|
|
3252
|
+
await client.execute({
|
|
3253
|
+
sql: `UPDATE memories
|
|
3254
|
+
SET status = 'deleted',
|
|
3255
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3256
|
+
WHERE id != ?
|
|
3257
|
+
AND content_hash = ?
|
|
3258
|
+
AND agent_id = ?
|
|
3259
|
+
AND project_name = ?
|
|
3260
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3261
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3262
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3263
|
+
});
|
|
3264
|
+
}
|
|
3265
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3266
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3267
|
+
const old = await client.execute({
|
|
3268
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3269
|
+
args: [supersedesId]
|
|
3270
|
+
});
|
|
3271
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3272
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3273
|
+
await client.batch([
|
|
3274
|
+
{
|
|
3275
|
+
sql: `UPDATE memories
|
|
3276
|
+
SET status = 'archived',
|
|
3277
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3278
|
+
WHERE id = ?`,
|
|
3279
|
+
args: [supersedesId]
|
|
3280
|
+
},
|
|
3281
|
+
{
|
|
3282
|
+
sql: `UPDATE memories
|
|
3283
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3284
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3285
|
+
WHERE id = ?`,
|
|
3286
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3287
|
+
}
|
|
3288
|
+
], "write");
|
|
3289
|
+
}
|
|
3290
|
+
} catch (err) {
|
|
3291
|
+
process.stderr.write(
|
|
3292
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3293
|
+
`
|
|
3294
|
+
);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3298
|
+
if (memoryIds.length === 0) return;
|
|
3299
|
+
const run = () => {
|
|
3300
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3301
|
+
};
|
|
3302
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3303
|
+
else setTimeout(run, 0);
|
|
3304
|
+
}
|
|
3305
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3306
|
+
var init_memory_write_governor = __esm({
|
|
3307
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3308
|
+
"use strict";
|
|
3309
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3310
|
+
"decision",
|
|
3311
|
+
"adr",
|
|
3312
|
+
"behavior",
|
|
3313
|
+
"procedure"
|
|
3314
|
+
]);
|
|
3315
|
+
NOISE_DROP_PATTERNS = [
|
|
3316
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3317
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3318
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3319
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3320
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3321
|
+
];
|
|
3322
|
+
SKIP_EMBED_PATTERNS = [
|
|
3323
|
+
/tmux capture-pane\b/i,
|
|
3324
|
+
/docker ps\b/i,
|
|
3325
|
+
/docker images\b/i,
|
|
3326
|
+
/git status\b/i,
|
|
3327
|
+
/grep .*node_modules/i,
|
|
3328
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3329
|
+
];
|
|
3330
|
+
}
|
|
3331
|
+
});
|
|
3332
|
+
|
|
3135
3333
|
// src/lib/shard-manager.ts
|
|
3136
3334
|
var shard_manager_exports = {};
|
|
3137
3335
|
__export(shard_manager_exports, {
|
|
@@ -3296,7 +3494,8 @@ async function ensureShardSchema(client) {
|
|
|
3296
3494
|
}
|
|
3297
3495
|
for (const idx of [
|
|
3298
3496
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3299
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3497
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3498
|
+
"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"
|
|
3300
3499
|
]) {
|
|
3301
3500
|
try {
|
|
3302
3501
|
await client.execute(idx);
|
|
@@ -3721,7 +3920,6 @@ __export(store_exports, {
|
|
|
3721
3920
|
vectorToBlob: () => vectorToBlob,
|
|
3722
3921
|
writeMemory: () => writeMemory
|
|
3723
3922
|
});
|
|
3724
|
-
import { createHash } from "crypto";
|
|
3725
3923
|
function isBusyError2(err) {
|
|
3726
3924
|
if (err instanceof Error) {
|
|
3727
3925
|
const msg = err.message.toLowerCase();
|
|
@@ -3835,17 +4033,24 @@ async function writeMemory(record) {
|
|
|
3835
4033
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3836
4034
|
);
|
|
3837
4035
|
}
|
|
3838
|
-
const
|
|
3839
|
-
if (
|
|
4036
|
+
const governed = governMemoryRecord(record);
|
|
4037
|
+
if (governed.shouldDrop) return;
|
|
4038
|
+
record = governed.record;
|
|
4039
|
+
const contentHash = governed.contentHash;
|
|
4040
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4041
|
+
if (_pendingRecords.some(
|
|
4042
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4043
|
+
)) {
|
|
3840
4044
|
return;
|
|
3841
4045
|
}
|
|
3842
4046
|
try {
|
|
3843
|
-
const
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
4047
|
+
const existing = await findScopedDuplicate({
|
|
4048
|
+
contentHash,
|
|
4049
|
+
agentId: record.agent_id,
|
|
4050
|
+
projectName: record.project_name,
|
|
4051
|
+
memoryType
|
|
3847
4052
|
});
|
|
3848
|
-
if (existing
|
|
4053
|
+
if (existing) return;
|
|
3849
4054
|
} catch {
|
|
3850
4055
|
}
|
|
3851
4056
|
const dbRow = {
|
|
@@ -3876,7 +4081,7 @@ async function writeMemory(record) {
|
|
|
3876
4081
|
tier: record.tier ?? classifyTier(record),
|
|
3877
4082
|
supersedes_id: record.supersedes_id ?? null,
|
|
3878
4083
|
draft: record.draft ? 1 : 0,
|
|
3879
|
-
memory_type:
|
|
4084
|
+
memory_type: memoryType,
|
|
3880
4085
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3881
4086
|
content_hash: contentHash,
|
|
3882
4087
|
intent: record.intent ?? null,
|
|
@@ -4035,6 +4240,7 @@ async function flushBatch() {
|
|
|
4035
4240
|
const globalClient = getClient();
|
|
4036
4241
|
const globalStmts = batch.map(buildStmt);
|
|
4037
4242
|
await globalClient.batch(globalStmts, "write");
|
|
4243
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4038
4244
|
_pendingRecords.splice(0, batch.length);
|
|
4039
4245
|
try {
|
|
4040
4246
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4293,6 +4499,7 @@ var init_store = __esm({
|
|
|
4293
4499
|
init_keychain();
|
|
4294
4500
|
init_config();
|
|
4295
4501
|
init_state_bus();
|
|
4502
|
+
init_memory_write_governor();
|
|
4296
4503
|
INIT_MAX_RETRIES = 3;
|
|
4297
4504
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4298
4505
|
_pendingRecords = [];
|
|
@@ -7451,7 +7658,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7451
7658
|
}
|
|
7452
7659
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
7453
7660
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
7454
|
-
|
|
7661
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
7662
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
7663
|
+
ccModel += "[1m]";
|
|
7664
|
+
}
|
|
7665
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
7455
7666
|
}
|
|
7456
7667
|
}
|
|
7457
7668
|
let spawnCommand;
|
package/dist/bin/scan-tasks.js
CHANGED
|
@@ -2879,6 +2879,14 @@ async function ensureSchema() {
|
|
|
2879
2879
|
);
|
|
2880
2880
|
} catch {
|
|
2881
2881
|
}
|
|
2882
|
+
try {
|
|
2883
|
+
await client.execute(
|
|
2884
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2885
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2886
|
+
WHERE content_hash IS NOT NULL`
|
|
2887
|
+
);
|
|
2888
|
+
} catch {
|
|
2889
|
+
}
|
|
2882
2890
|
await client.executeMultiple(`
|
|
2883
2891
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2884
2892
|
id TEXT PRIMARY KEY,
|
|
@@ -6387,7 +6395,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6387
6395
|
}
|
|
6388
6396
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6389
6397
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
6390
|
-
|
|
6398
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
6399
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
6400
|
+
ccModel += "[1m]";
|
|
6401
|
+
}
|
|
6402
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
6391
6403
|
}
|
|
6392
6404
|
}
|
|
6393
6405
|
let spawnCommand;
|
|
@@ -6734,6 +6746,196 @@ var init_keychain = __esm({
|
|
|
6734
6746
|
}
|
|
6735
6747
|
});
|
|
6736
6748
|
|
|
6749
|
+
// src/lib/memory-write-governor.ts
|
|
6750
|
+
import { createHash } from "crypto";
|
|
6751
|
+
function normalizeMemoryText(text) {
|
|
6752
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
6753
|
+
}
|
|
6754
|
+
function classifyMemoryType(input) {
|
|
6755
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
6756
|
+
const tool = input.tool_name.toLowerCase();
|
|
6757
|
+
const text = input.raw_text.toLowerCase();
|
|
6758
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
6759
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
6760
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
6761
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
6762
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
6763
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
6764
|
+
return "raw";
|
|
6765
|
+
}
|
|
6766
|
+
function shouldDropMemory(text) {
|
|
6767
|
+
const normalized = normalizeMemoryText(text);
|
|
6768
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
6769
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
6770
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
6771
|
+
}
|
|
6772
|
+
return { drop: false };
|
|
6773
|
+
}
|
|
6774
|
+
function shouldSkipEmbedding(input) {
|
|
6775
|
+
const type = classifyMemoryType(input);
|
|
6776
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
6777
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
6778
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
6779
|
+
return false;
|
|
6780
|
+
}
|
|
6781
|
+
function hashMemoryContent(text) {
|
|
6782
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
6783
|
+
}
|
|
6784
|
+
function scopedDedupArgs(input) {
|
|
6785
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
6786
|
+
}
|
|
6787
|
+
function governMemoryRecord(record) {
|
|
6788
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
6789
|
+
const memoryType = classifyMemoryType({
|
|
6790
|
+
raw_text: normalized,
|
|
6791
|
+
agent_id: record.agent_id,
|
|
6792
|
+
project_name: record.project_name,
|
|
6793
|
+
tool_name: record.tool_name,
|
|
6794
|
+
memory_type: record.memory_type
|
|
6795
|
+
});
|
|
6796
|
+
const drop = shouldDropMemory(normalized);
|
|
6797
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
6798
|
+
raw_text: normalized,
|
|
6799
|
+
agent_id: record.agent_id,
|
|
6800
|
+
project_name: record.project_name,
|
|
6801
|
+
tool_name: record.tool_name,
|
|
6802
|
+
memory_type: memoryType
|
|
6803
|
+
});
|
|
6804
|
+
return {
|
|
6805
|
+
record: {
|
|
6806
|
+
...record,
|
|
6807
|
+
raw_text: normalized,
|
|
6808
|
+
memory_type: memoryType,
|
|
6809
|
+
vector: skipEmbedding ? null : record.vector
|
|
6810
|
+
},
|
|
6811
|
+
contentHash: hashMemoryContent(normalized),
|
|
6812
|
+
shouldDrop: drop.drop,
|
|
6813
|
+
dropReason: drop.reason,
|
|
6814
|
+
skipEmbedding,
|
|
6815
|
+
hygiene: {
|
|
6816
|
+
dedup: true,
|
|
6817
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
6818
|
+
}
|
|
6819
|
+
};
|
|
6820
|
+
}
|
|
6821
|
+
async function findScopedDuplicate(input) {
|
|
6822
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6823
|
+
const client = getClient2();
|
|
6824
|
+
const args = scopedDedupArgs(input);
|
|
6825
|
+
let sql = `SELECT id FROM memories
|
|
6826
|
+
WHERE content_hash = ?
|
|
6827
|
+
AND agent_id = ?
|
|
6828
|
+
AND project_name = ?
|
|
6829
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6830
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
6831
|
+
if (input.excludeId) {
|
|
6832
|
+
sql += " AND id != ?";
|
|
6833
|
+
args.push(input.excludeId);
|
|
6834
|
+
}
|
|
6835
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
6836
|
+
const result = await client.execute({ sql, args });
|
|
6837
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
6838
|
+
}
|
|
6839
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
6840
|
+
try {
|
|
6841
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6842
|
+
const client = getClient2();
|
|
6843
|
+
const current = await client.execute({
|
|
6844
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
6845
|
+
importance, timestamp
|
|
6846
|
+
FROM memories
|
|
6847
|
+
WHERE id = ?
|
|
6848
|
+
LIMIT 1`,
|
|
6849
|
+
args: [memoryId]
|
|
6850
|
+
});
|
|
6851
|
+
const row = current.rows[0];
|
|
6852
|
+
if (!row) return;
|
|
6853
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
6854
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
6855
|
+
const agentId = String(row.agent_id);
|
|
6856
|
+
const projectName = String(row.project_name);
|
|
6857
|
+
if (contentHash) {
|
|
6858
|
+
await client.execute({
|
|
6859
|
+
sql: `UPDATE memories
|
|
6860
|
+
SET status = 'deleted',
|
|
6861
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6862
|
+
WHERE id != ?
|
|
6863
|
+
AND content_hash = ?
|
|
6864
|
+
AND agent_id = ?
|
|
6865
|
+
AND project_name = ?
|
|
6866
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6867
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
6868
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
6869
|
+
});
|
|
6870
|
+
}
|
|
6871
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
6872
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
6873
|
+
const old = await client.execute({
|
|
6874
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
6875
|
+
args: [supersedesId]
|
|
6876
|
+
});
|
|
6877
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
6878
|
+
const newImportance = Number(row.importance ?? 0);
|
|
6879
|
+
await client.batch([
|
|
6880
|
+
{
|
|
6881
|
+
sql: `UPDATE memories
|
|
6882
|
+
SET status = 'archived',
|
|
6883
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6884
|
+
WHERE id = ?`,
|
|
6885
|
+
args: [supersedesId]
|
|
6886
|
+
},
|
|
6887
|
+
{
|
|
6888
|
+
sql: `UPDATE memories
|
|
6889
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
6890
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
6891
|
+
WHERE id = ?`,
|
|
6892
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
6893
|
+
}
|
|
6894
|
+
], "write");
|
|
6895
|
+
}
|
|
6896
|
+
} catch (err) {
|
|
6897
|
+
process.stderr.write(
|
|
6898
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
6899
|
+
`
|
|
6900
|
+
);
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
6904
|
+
if (memoryIds.length === 0) return;
|
|
6905
|
+
const run = () => {
|
|
6906
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
6907
|
+
};
|
|
6908
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
6909
|
+
else setTimeout(run, 0);
|
|
6910
|
+
}
|
|
6911
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
6912
|
+
var init_memory_write_governor = __esm({
|
|
6913
|
+
"src/lib/memory-write-governor.ts"() {
|
|
6914
|
+
"use strict";
|
|
6915
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
6916
|
+
"decision",
|
|
6917
|
+
"adr",
|
|
6918
|
+
"behavior",
|
|
6919
|
+
"procedure"
|
|
6920
|
+
]);
|
|
6921
|
+
NOISE_DROP_PATTERNS = [
|
|
6922
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
6923
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
6924
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
6925
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
6926
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
6927
|
+
];
|
|
6928
|
+
SKIP_EMBED_PATTERNS = [
|
|
6929
|
+
/tmux capture-pane\b/i,
|
|
6930
|
+
/docker ps\b/i,
|
|
6931
|
+
/docker images\b/i,
|
|
6932
|
+
/git status\b/i,
|
|
6933
|
+
/grep .*node_modules/i,
|
|
6934
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
6935
|
+
];
|
|
6936
|
+
}
|
|
6937
|
+
});
|
|
6938
|
+
|
|
6737
6939
|
// src/lib/shard-manager.ts
|
|
6738
6940
|
var shard_manager_exports = {};
|
|
6739
6941
|
__export(shard_manager_exports, {
|
|
@@ -6898,7 +7100,8 @@ async function ensureShardSchema(client) {
|
|
|
6898
7100
|
}
|
|
6899
7101
|
for (const idx of [
|
|
6900
7102
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
6901
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
7103
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
7104
|
+
"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"
|
|
6902
7105
|
]) {
|
|
6903
7106
|
try {
|
|
6904
7107
|
await client.execute(idx);
|
|
@@ -7323,7 +7526,6 @@ __export(store_exports, {
|
|
|
7323
7526
|
vectorToBlob: () => vectorToBlob,
|
|
7324
7527
|
writeMemory: () => writeMemory
|
|
7325
7528
|
});
|
|
7326
|
-
import { createHash } from "crypto";
|
|
7327
7529
|
function isBusyError2(err) {
|
|
7328
7530
|
if (err instanceof Error) {
|
|
7329
7531
|
const msg = err.message.toLowerCase();
|
|
@@ -7437,17 +7639,24 @@ async function writeMemory(record) {
|
|
|
7437
7639
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
7438
7640
|
);
|
|
7439
7641
|
}
|
|
7440
|
-
const
|
|
7441
|
-
if (
|
|
7642
|
+
const governed = governMemoryRecord(record);
|
|
7643
|
+
if (governed.shouldDrop) return;
|
|
7644
|
+
record = governed.record;
|
|
7645
|
+
const contentHash = governed.contentHash;
|
|
7646
|
+
const memoryType = record.memory_type ?? "raw";
|
|
7647
|
+
if (_pendingRecords.some(
|
|
7648
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
7649
|
+
)) {
|
|
7442
7650
|
return;
|
|
7443
7651
|
}
|
|
7444
7652
|
try {
|
|
7445
|
-
const
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7653
|
+
const existing = await findScopedDuplicate({
|
|
7654
|
+
contentHash,
|
|
7655
|
+
agentId: record.agent_id,
|
|
7656
|
+
projectName: record.project_name,
|
|
7657
|
+
memoryType
|
|
7449
7658
|
});
|
|
7450
|
-
if (existing
|
|
7659
|
+
if (existing) return;
|
|
7451
7660
|
} catch {
|
|
7452
7661
|
}
|
|
7453
7662
|
const dbRow = {
|
|
@@ -7478,7 +7687,7 @@ async function writeMemory(record) {
|
|
|
7478
7687
|
tier: record.tier ?? classifyTier(record),
|
|
7479
7688
|
supersedes_id: record.supersedes_id ?? null,
|
|
7480
7689
|
draft: record.draft ? 1 : 0,
|
|
7481
|
-
memory_type:
|
|
7690
|
+
memory_type: memoryType,
|
|
7482
7691
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
7483
7692
|
content_hash: contentHash,
|
|
7484
7693
|
intent: record.intent ?? null,
|
|
@@ -7637,6 +7846,7 @@ async function flushBatch() {
|
|
|
7637
7846
|
const globalClient = getClient();
|
|
7638
7847
|
const globalStmts = batch.map(buildStmt);
|
|
7639
7848
|
await globalClient.batch(globalStmts, "write");
|
|
7849
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
7640
7850
|
_pendingRecords.splice(0, batch.length);
|
|
7641
7851
|
try {
|
|
7642
7852
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -7895,6 +8105,7 @@ var init_store = __esm({
|
|
|
7895
8105
|
init_keychain();
|
|
7896
8106
|
init_config();
|
|
7897
8107
|
init_state_bus();
|
|
8108
|
+
init_memory_write_governor();
|
|
7898
8109
|
INIT_MAX_RETRIES = 3;
|
|
7899
8110
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
7900
8111
|
_pendingRecords = [];
|
package/dist/bin/setup.js
CHANGED
|
@@ -3504,6 +3504,14 @@ async function ensureSchema() {
|
|
|
3504
3504
|
);
|
|
3505
3505
|
} catch {
|
|
3506
3506
|
}
|
|
3507
|
+
try {
|
|
3508
|
+
await client.execute(
|
|
3509
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
3510
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
3511
|
+
WHERE content_hash IS NOT NULL`
|
|
3512
|
+
);
|
|
3513
|
+
} catch {
|
|
3514
|
+
}
|
|
3507
3515
|
await client.executeMultiple(`
|
|
3508
3516
|
CREATE TABLE IF NOT EXISTS entities (
|
|
3509
3517
|
id TEXT PRIMARY KEY,
|