@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
|
@@ -2276,6 +2276,14 @@ async function ensureSchema() {
|
|
|
2276
2276
|
);
|
|
2277
2277
|
} catch {
|
|
2278
2278
|
}
|
|
2279
|
+
try {
|
|
2280
|
+
await client.execute(
|
|
2281
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2282
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2283
|
+
WHERE content_hash IS NOT NULL`
|
|
2284
|
+
);
|
|
2285
|
+
} catch {
|
|
2286
|
+
}
|
|
2279
2287
|
await client.executeMultiple(`
|
|
2280
2288
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2281
2289
|
id TEXT PRIMARY KEY,
|
|
@@ -3021,6 +3029,196 @@ var init_state_bus = __esm({
|
|
|
3021
3029
|
}
|
|
3022
3030
|
});
|
|
3023
3031
|
|
|
3032
|
+
// src/lib/memory-write-governor.ts
|
|
3033
|
+
import { createHash } from "crypto";
|
|
3034
|
+
function normalizeMemoryText(text) {
|
|
3035
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3036
|
+
}
|
|
3037
|
+
function classifyMemoryType(input) {
|
|
3038
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3039
|
+
const tool = input.tool_name.toLowerCase();
|
|
3040
|
+
const text = input.raw_text.toLowerCase();
|
|
3041
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3042
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3043
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3044
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3045
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3046
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3047
|
+
return "raw";
|
|
3048
|
+
}
|
|
3049
|
+
function shouldDropMemory(text) {
|
|
3050
|
+
const normalized = normalizeMemoryText(text);
|
|
3051
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3052
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3053
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3054
|
+
}
|
|
3055
|
+
return { drop: false };
|
|
3056
|
+
}
|
|
3057
|
+
function shouldSkipEmbedding(input) {
|
|
3058
|
+
const type = classifyMemoryType(input);
|
|
3059
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3060
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3061
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3062
|
+
return false;
|
|
3063
|
+
}
|
|
3064
|
+
function hashMemoryContent(text) {
|
|
3065
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3066
|
+
}
|
|
3067
|
+
function scopedDedupArgs(input) {
|
|
3068
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3069
|
+
}
|
|
3070
|
+
function governMemoryRecord(record) {
|
|
3071
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3072
|
+
const memoryType = classifyMemoryType({
|
|
3073
|
+
raw_text: normalized,
|
|
3074
|
+
agent_id: record.agent_id,
|
|
3075
|
+
project_name: record.project_name,
|
|
3076
|
+
tool_name: record.tool_name,
|
|
3077
|
+
memory_type: record.memory_type
|
|
3078
|
+
});
|
|
3079
|
+
const drop = shouldDropMemory(normalized);
|
|
3080
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3081
|
+
raw_text: normalized,
|
|
3082
|
+
agent_id: record.agent_id,
|
|
3083
|
+
project_name: record.project_name,
|
|
3084
|
+
tool_name: record.tool_name,
|
|
3085
|
+
memory_type: memoryType
|
|
3086
|
+
});
|
|
3087
|
+
return {
|
|
3088
|
+
record: {
|
|
3089
|
+
...record,
|
|
3090
|
+
raw_text: normalized,
|
|
3091
|
+
memory_type: memoryType,
|
|
3092
|
+
vector: skipEmbedding ? null : record.vector
|
|
3093
|
+
},
|
|
3094
|
+
contentHash: hashMemoryContent(normalized),
|
|
3095
|
+
shouldDrop: drop.drop,
|
|
3096
|
+
dropReason: drop.reason,
|
|
3097
|
+
skipEmbedding,
|
|
3098
|
+
hygiene: {
|
|
3099
|
+
dedup: true,
|
|
3100
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3101
|
+
}
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
async function findScopedDuplicate(input) {
|
|
3105
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3106
|
+
const client = getClient2();
|
|
3107
|
+
const args = scopedDedupArgs(input);
|
|
3108
|
+
let sql = `SELECT id FROM memories
|
|
3109
|
+
WHERE content_hash = ?
|
|
3110
|
+
AND agent_id = ?
|
|
3111
|
+
AND project_name = ?
|
|
3112
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3113
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3114
|
+
if (input.excludeId) {
|
|
3115
|
+
sql += " AND id != ?";
|
|
3116
|
+
args.push(input.excludeId);
|
|
3117
|
+
}
|
|
3118
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3119
|
+
const result = await client.execute({ sql, args });
|
|
3120
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3121
|
+
}
|
|
3122
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3123
|
+
try {
|
|
3124
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3125
|
+
const client = getClient2();
|
|
3126
|
+
const current = await client.execute({
|
|
3127
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3128
|
+
importance, timestamp
|
|
3129
|
+
FROM memories
|
|
3130
|
+
WHERE id = ?
|
|
3131
|
+
LIMIT 1`,
|
|
3132
|
+
args: [memoryId]
|
|
3133
|
+
});
|
|
3134
|
+
const row = current.rows[0];
|
|
3135
|
+
if (!row) return;
|
|
3136
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3137
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3138
|
+
const agentId = String(row.agent_id);
|
|
3139
|
+
const projectName = String(row.project_name);
|
|
3140
|
+
if (contentHash) {
|
|
3141
|
+
await client.execute({
|
|
3142
|
+
sql: `UPDATE memories
|
|
3143
|
+
SET status = 'deleted',
|
|
3144
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3145
|
+
WHERE id != ?
|
|
3146
|
+
AND content_hash = ?
|
|
3147
|
+
AND agent_id = ?
|
|
3148
|
+
AND project_name = ?
|
|
3149
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3150
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3151
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3152
|
+
});
|
|
3153
|
+
}
|
|
3154
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3155
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3156
|
+
const old = await client.execute({
|
|
3157
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3158
|
+
args: [supersedesId]
|
|
3159
|
+
});
|
|
3160
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3161
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3162
|
+
await client.batch([
|
|
3163
|
+
{
|
|
3164
|
+
sql: `UPDATE memories
|
|
3165
|
+
SET status = 'archived',
|
|
3166
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3167
|
+
WHERE id = ?`,
|
|
3168
|
+
args: [supersedesId]
|
|
3169
|
+
},
|
|
3170
|
+
{
|
|
3171
|
+
sql: `UPDATE memories
|
|
3172
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3173
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3174
|
+
WHERE id = ?`,
|
|
3175
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3176
|
+
}
|
|
3177
|
+
], "write");
|
|
3178
|
+
}
|
|
3179
|
+
} catch (err) {
|
|
3180
|
+
process.stderr.write(
|
|
3181
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3182
|
+
`
|
|
3183
|
+
);
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3187
|
+
if (memoryIds.length === 0) return;
|
|
3188
|
+
const run = () => {
|
|
3189
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3190
|
+
};
|
|
3191
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3192
|
+
else setTimeout(run, 0);
|
|
3193
|
+
}
|
|
3194
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3195
|
+
var init_memory_write_governor = __esm({
|
|
3196
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3197
|
+
"use strict";
|
|
3198
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3199
|
+
"decision",
|
|
3200
|
+
"adr",
|
|
3201
|
+
"behavior",
|
|
3202
|
+
"procedure"
|
|
3203
|
+
]);
|
|
3204
|
+
NOISE_DROP_PATTERNS = [
|
|
3205
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3206
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3207
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3208
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3209
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3210
|
+
];
|
|
3211
|
+
SKIP_EMBED_PATTERNS = [
|
|
3212
|
+
/tmux capture-pane\b/i,
|
|
3213
|
+
/docker ps\b/i,
|
|
3214
|
+
/docker images\b/i,
|
|
3215
|
+
/git status\b/i,
|
|
3216
|
+
/grep .*node_modules/i,
|
|
3217
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3218
|
+
];
|
|
3219
|
+
}
|
|
3220
|
+
});
|
|
3221
|
+
|
|
3024
3222
|
// src/lib/shard-manager.ts
|
|
3025
3223
|
var shard_manager_exports = {};
|
|
3026
3224
|
__export(shard_manager_exports, {
|
|
@@ -3185,7 +3383,8 @@ async function ensureShardSchema(client) {
|
|
|
3185
3383
|
}
|
|
3186
3384
|
for (const idx of [
|
|
3187
3385
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3188
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3386
|
+
"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_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
|
|
3189
3388
|
]) {
|
|
3190
3389
|
try {
|
|
3191
3390
|
await client.execute(idx);
|
|
@@ -3610,7 +3809,6 @@ __export(store_exports, {
|
|
|
3610
3809
|
vectorToBlob: () => vectorToBlob,
|
|
3611
3810
|
writeMemory: () => writeMemory
|
|
3612
3811
|
});
|
|
3613
|
-
import { createHash } from "crypto";
|
|
3614
3812
|
function isBusyError2(err) {
|
|
3615
3813
|
if (err instanceof Error) {
|
|
3616
3814
|
const msg = err.message.toLowerCase();
|
|
@@ -3724,17 +3922,24 @@ async function writeMemory(record) {
|
|
|
3724
3922
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3725
3923
|
);
|
|
3726
3924
|
}
|
|
3727
|
-
const
|
|
3728
|
-
if (
|
|
3925
|
+
const governed = governMemoryRecord(record);
|
|
3926
|
+
if (governed.shouldDrop) return;
|
|
3927
|
+
record = governed.record;
|
|
3928
|
+
const contentHash = governed.contentHash;
|
|
3929
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3930
|
+
if (_pendingRecords.some(
|
|
3931
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3932
|
+
)) {
|
|
3729
3933
|
return;
|
|
3730
3934
|
}
|
|
3731
3935
|
try {
|
|
3732
|
-
const
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3936
|
+
const existing = await findScopedDuplicate({
|
|
3937
|
+
contentHash,
|
|
3938
|
+
agentId: record.agent_id,
|
|
3939
|
+
projectName: record.project_name,
|
|
3940
|
+
memoryType
|
|
3736
3941
|
});
|
|
3737
|
-
if (existing
|
|
3942
|
+
if (existing) return;
|
|
3738
3943
|
} catch {
|
|
3739
3944
|
}
|
|
3740
3945
|
const dbRow = {
|
|
@@ -3765,7 +3970,7 @@ async function writeMemory(record) {
|
|
|
3765
3970
|
tier: record.tier ?? classifyTier(record),
|
|
3766
3971
|
supersedes_id: record.supersedes_id ?? null,
|
|
3767
3972
|
draft: record.draft ? 1 : 0,
|
|
3768
|
-
memory_type:
|
|
3973
|
+
memory_type: memoryType,
|
|
3769
3974
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3770
3975
|
content_hash: contentHash,
|
|
3771
3976
|
intent: record.intent ?? null,
|
|
@@ -3924,6 +4129,7 @@ async function flushBatch() {
|
|
|
3924
4129
|
const globalClient = getClient();
|
|
3925
4130
|
const globalStmts = batch.map(buildStmt);
|
|
3926
4131
|
await globalClient.batch(globalStmts, "write");
|
|
4132
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3927
4133
|
_pendingRecords.splice(0, batch.length);
|
|
3928
4134
|
try {
|
|
3929
4135
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4182,6 +4388,7 @@ var init_store = __esm({
|
|
|
4182
4388
|
init_keychain();
|
|
4183
4389
|
init_config();
|
|
4184
4390
|
init_state_bus();
|
|
4391
|
+
init_memory_write_governor();
|
|
4185
4392
|
INIT_MAX_RETRIES = 3;
|
|
4186
4393
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4187
4394
|
_pendingRecords = [];
|
package/dist/lib/schedules.js
CHANGED
|
@@ -2072,6 +2072,14 @@ async function ensureSchema() {
|
|
|
2072
2072
|
);
|
|
2073
2073
|
} catch {
|
|
2074
2074
|
}
|
|
2075
|
+
try {
|
|
2076
|
+
await client.execute(
|
|
2077
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2078
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2079
|
+
WHERE content_hash IS NOT NULL`
|
|
2080
|
+
);
|
|
2081
|
+
} catch {
|
|
2082
|
+
}
|
|
2075
2083
|
await client.executeMultiple(`
|
|
2076
2084
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2077
2085
|
id TEXT PRIMARY KEY,
|
|
@@ -2741,7 +2749,8 @@ async function ensureShardSchema(client) {
|
|
|
2741
2749
|
}
|
|
2742
2750
|
for (const idx of [
|
|
2743
2751
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
2744
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
2752
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
2753
|
+
"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"
|
|
2745
2754
|
]) {
|
|
2746
2755
|
try {
|
|
2747
2756
|
await client.execute(idx);
|
|
@@ -3156,7 +3165,6 @@ import { execSync as execSync3 } from "child_process";
|
|
|
3156
3165
|
// src/lib/store.ts
|
|
3157
3166
|
init_memory();
|
|
3158
3167
|
init_database();
|
|
3159
|
-
import { createHash } from "crypto";
|
|
3160
3168
|
|
|
3161
3169
|
// src/lib/keychain.ts
|
|
3162
3170
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
@@ -3389,6 +3397,9 @@ var StateBus = class {
|
|
|
3389
3397
|
};
|
|
3390
3398
|
var orgBus = new StateBus();
|
|
3391
3399
|
|
|
3400
|
+
// src/lib/memory-write-governor.ts
|
|
3401
|
+
import { createHash } from "crypto";
|
|
3402
|
+
|
|
3392
3403
|
// src/lib/store.ts
|
|
3393
3404
|
var INIT_MAX_RETRIES = 3;
|
|
3394
3405
|
var INIT_RETRY_DELAY_MS = 1e3;
|
package/dist/lib/store.js
CHANGED
|
@@ -2072,6 +2072,14 @@ async function ensureSchema() {
|
|
|
2072
2072
|
);
|
|
2073
2073
|
} catch {
|
|
2074
2074
|
}
|
|
2075
|
+
try {
|
|
2076
|
+
await client.execute(
|
|
2077
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2078
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2079
|
+
WHERE content_hash IS NOT NULL`
|
|
2080
|
+
);
|
|
2081
|
+
} catch {
|
|
2082
|
+
}
|
|
2075
2083
|
await client.executeMultiple(`
|
|
2076
2084
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2077
2085
|
id TEXT PRIMARY KEY,
|
|
@@ -2741,7 +2749,8 @@ async function ensureShardSchema(client) {
|
|
|
2741
2749
|
}
|
|
2742
2750
|
for (const idx of [
|
|
2743
2751
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
2744
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
2752
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
2753
|
+
"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"
|
|
2745
2754
|
]) {
|
|
2746
2755
|
try {
|
|
2747
2756
|
await client.execute(idx);
|
|
@@ -3151,7 +3160,6 @@ ${p.content}`).join("\n\n");
|
|
|
3151
3160
|
// src/lib/store.ts
|
|
3152
3161
|
init_memory();
|
|
3153
3162
|
init_database();
|
|
3154
|
-
import { createHash } from "crypto";
|
|
3155
3163
|
|
|
3156
3164
|
// src/lib/keychain.ts
|
|
3157
3165
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
@@ -3384,6 +3392,190 @@ var StateBus = class {
|
|
|
3384
3392
|
};
|
|
3385
3393
|
var orgBus = new StateBus();
|
|
3386
3394
|
|
|
3395
|
+
// src/lib/memory-write-governor.ts
|
|
3396
|
+
import { createHash } from "crypto";
|
|
3397
|
+
var HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3398
|
+
"decision",
|
|
3399
|
+
"adr",
|
|
3400
|
+
"behavior",
|
|
3401
|
+
"procedure"
|
|
3402
|
+
]);
|
|
3403
|
+
var NOISE_DROP_PATTERNS = [
|
|
3404
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3405
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3406
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3407
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3408
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3409
|
+
];
|
|
3410
|
+
var SKIP_EMBED_PATTERNS = [
|
|
3411
|
+
/tmux capture-pane\b/i,
|
|
3412
|
+
/docker ps\b/i,
|
|
3413
|
+
/docker images\b/i,
|
|
3414
|
+
/git status\b/i,
|
|
3415
|
+
/grep .*node_modules/i,
|
|
3416
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3417
|
+
];
|
|
3418
|
+
function normalizeMemoryText(text) {
|
|
3419
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3420
|
+
}
|
|
3421
|
+
function classifyMemoryType(input) {
|
|
3422
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3423
|
+
const tool = input.tool_name.toLowerCase();
|
|
3424
|
+
const text = input.raw_text.toLowerCase();
|
|
3425
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3426
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3427
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3428
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3429
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3430
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3431
|
+
return "raw";
|
|
3432
|
+
}
|
|
3433
|
+
function shouldDropMemory(text) {
|
|
3434
|
+
const normalized = normalizeMemoryText(text);
|
|
3435
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3436
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3437
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3438
|
+
}
|
|
3439
|
+
return { drop: false };
|
|
3440
|
+
}
|
|
3441
|
+
function shouldSkipEmbedding(input) {
|
|
3442
|
+
const type = classifyMemoryType(input);
|
|
3443
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3444
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3445
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3446
|
+
return false;
|
|
3447
|
+
}
|
|
3448
|
+
function hashMemoryContent(text) {
|
|
3449
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3450
|
+
}
|
|
3451
|
+
function scopedDedupArgs(input) {
|
|
3452
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3453
|
+
}
|
|
3454
|
+
function governMemoryRecord(record) {
|
|
3455
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3456
|
+
const memoryType = classifyMemoryType({
|
|
3457
|
+
raw_text: normalized,
|
|
3458
|
+
agent_id: record.agent_id,
|
|
3459
|
+
project_name: record.project_name,
|
|
3460
|
+
tool_name: record.tool_name,
|
|
3461
|
+
memory_type: record.memory_type
|
|
3462
|
+
});
|
|
3463
|
+
const drop = shouldDropMemory(normalized);
|
|
3464
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3465
|
+
raw_text: normalized,
|
|
3466
|
+
agent_id: record.agent_id,
|
|
3467
|
+
project_name: record.project_name,
|
|
3468
|
+
tool_name: record.tool_name,
|
|
3469
|
+
memory_type: memoryType
|
|
3470
|
+
});
|
|
3471
|
+
return {
|
|
3472
|
+
record: {
|
|
3473
|
+
...record,
|
|
3474
|
+
raw_text: normalized,
|
|
3475
|
+
memory_type: memoryType,
|
|
3476
|
+
vector: skipEmbedding ? null : record.vector
|
|
3477
|
+
},
|
|
3478
|
+
contentHash: hashMemoryContent(normalized),
|
|
3479
|
+
shouldDrop: drop.drop,
|
|
3480
|
+
dropReason: drop.reason,
|
|
3481
|
+
skipEmbedding,
|
|
3482
|
+
hygiene: {
|
|
3483
|
+
dedup: true,
|
|
3484
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3485
|
+
}
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3488
|
+
async function findScopedDuplicate(input) {
|
|
3489
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3490
|
+
const client = getClient2();
|
|
3491
|
+
const args = scopedDedupArgs(input);
|
|
3492
|
+
let sql = `SELECT id FROM memories
|
|
3493
|
+
WHERE content_hash = ?
|
|
3494
|
+
AND agent_id = ?
|
|
3495
|
+
AND project_name = ?
|
|
3496
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3497
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3498
|
+
if (input.excludeId) {
|
|
3499
|
+
sql += " AND id != ?";
|
|
3500
|
+
args.push(input.excludeId);
|
|
3501
|
+
}
|
|
3502
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3503
|
+
const result = await client.execute({ sql, args });
|
|
3504
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3505
|
+
}
|
|
3506
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3507
|
+
try {
|
|
3508
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3509
|
+
const client = getClient2();
|
|
3510
|
+
const current = await client.execute({
|
|
3511
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3512
|
+
importance, timestamp
|
|
3513
|
+
FROM memories
|
|
3514
|
+
WHERE id = ?
|
|
3515
|
+
LIMIT 1`,
|
|
3516
|
+
args: [memoryId]
|
|
3517
|
+
});
|
|
3518
|
+
const row = current.rows[0];
|
|
3519
|
+
if (!row) return;
|
|
3520
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3521
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3522
|
+
const agentId = String(row.agent_id);
|
|
3523
|
+
const projectName = String(row.project_name);
|
|
3524
|
+
if (contentHash) {
|
|
3525
|
+
await client.execute({
|
|
3526
|
+
sql: `UPDATE memories
|
|
3527
|
+
SET status = 'deleted',
|
|
3528
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3529
|
+
WHERE id != ?
|
|
3530
|
+
AND content_hash = ?
|
|
3531
|
+
AND agent_id = ?
|
|
3532
|
+
AND project_name = ?
|
|
3533
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3534
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3535
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3536
|
+
});
|
|
3537
|
+
}
|
|
3538
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3539
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3540
|
+
const old = await client.execute({
|
|
3541
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3542
|
+
args: [supersedesId]
|
|
3543
|
+
});
|
|
3544
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3545
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3546
|
+
await client.batch([
|
|
3547
|
+
{
|
|
3548
|
+
sql: `UPDATE memories
|
|
3549
|
+
SET status = 'archived',
|
|
3550
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3551
|
+
WHERE id = ?`,
|
|
3552
|
+
args: [supersedesId]
|
|
3553
|
+
},
|
|
3554
|
+
{
|
|
3555
|
+
sql: `UPDATE memories
|
|
3556
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3557
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3558
|
+
WHERE id = ?`,
|
|
3559
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3560
|
+
}
|
|
3561
|
+
], "write");
|
|
3562
|
+
}
|
|
3563
|
+
} catch (err) {
|
|
3564
|
+
process.stderr.write(
|
|
3565
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3566
|
+
`
|
|
3567
|
+
);
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3571
|
+
if (memoryIds.length === 0) return;
|
|
3572
|
+
const run = () => {
|
|
3573
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3574
|
+
};
|
|
3575
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3576
|
+
else setTimeout(run, 0);
|
|
3577
|
+
}
|
|
3578
|
+
|
|
3387
3579
|
// src/lib/store.ts
|
|
3388
3580
|
var INIT_MAX_RETRIES = 3;
|
|
3389
3581
|
var INIT_RETRY_DELAY_MS = 1e3;
|
|
@@ -3506,17 +3698,24 @@ async function writeMemory(record) {
|
|
|
3506
3698
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3507
3699
|
);
|
|
3508
3700
|
}
|
|
3509
|
-
const
|
|
3510
|
-
if (
|
|
3701
|
+
const governed = governMemoryRecord(record);
|
|
3702
|
+
if (governed.shouldDrop) return;
|
|
3703
|
+
record = governed.record;
|
|
3704
|
+
const contentHash = governed.contentHash;
|
|
3705
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3706
|
+
if (_pendingRecords.some(
|
|
3707
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3708
|
+
)) {
|
|
3511
3709
|
return;
|
|
3512
3710
|
}
|
|
3513
3711
|
try {
|
|
3514
|
-
const
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3712
|
+
const existing = await findScopedDuplicate({
|
|
3713
|
+
contentHash,
|
|
3714
|
+
agentId: record.agent_id,
|
|
3715
|
+
projectName: record.project_name,
|
|
3716
|
+
memoryType
|
|
3518
3717
|
});
|
|
3519
|
-
if (existing
|
|
3718
|
+
if (existing) return;
|
|
3520
3719
|
} catch {
|
|
3521
3720
|
}
|
|
3522
3721
|
const dbRow = {
|
|
@@ -3547,7 +3746,7 @@ async function writeMemory(record) {
|
|
|
3547
3746
|
tier: record.tier ?? classifyTier(record),
|
|
3548
3747
|
supersedes_id: record.supersedes_id ?? null,
|
|
3549
3748
|
draft: record.draft ? 1 : 0,
|
|
3550
|
-
memory_type:
|
|
3749
|
+
memory_type: memoryType,
|
|
3551
3750
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3552
3751
|
content_hash: contentHash,
|
|
3553
3752
|
intent: record.intent ?? null,
|
|
@@ -3706,6 +3905,7 @@ async function flushBatch() {
|
|
|
3706
3905
|
const globalClient = getClient();
|
|
3707
3906
|
const globalStmts = batch.map(buildStmt);
|
|
3708
3907
|
await globalClient.batch(globalStmts, "write");
|
|
3908
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3709
3909
|
_pendingRecords.splice(0, batch.length);
|
|
3710
3910
|
try {
|
|
3711
3911
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
package/dist/lib/tasks.js
CHANGED
|
@@ -2389,7 +2389,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2389
2389
|
}
|
|
2390
2390
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
2391
2391
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
2392
|
-
|
|
2392
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
2393
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
2394
|
+
ccModel += "[1m]";
|
|
2395
|
+
}
|
|
2396
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
2393
2397
|
}
|
|
2394
2398
|
}
|
|
2395
2399
|
let spawnCommand;
|
package/dist/lib/tmux-routing.js
CHANGED
|
@@ -4415,7 +4415,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4415
4415
|
}
|
|
4416
4416
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
4417
4417
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
4418
|
-
|
|
4418
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
4419
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
4420
|
+
ccModel += "[1m]";
|
|
4421
|
+
}
|
|
4422
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
4419
4423
|
}
|
|
4420
4424
|
}
|
|
4421
4425
|
let spawnCommand;
|