@askexenow/exe-os 0.9.34 → 0.9.36
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 +212 -11
- package/dist/bin/backfill-responses.js +212 -11
- package/dist/bin/backfill-vectors.js +14 -3
- package/dist/bin/cleanup-stale-review-tasks.js +224 -12
- package/dist/bin/cli.js +265 -23
- package/dist/bin/exe-agent.js +1 -1
- package/dist/bin/exe-assign.js +217 -12
- package/dist/bin/exe-boot.js +25 -4
- package/dist/bin/exe-call.js +7 -5
- package/dist/bin/exe-dispatch.js +229 -13
- package/dist/bin/exe-doctor.js +14 -3
- package/dist/bin/exe-export-behaviors.js +301 -14
- package/dist/bin/exe-forget.js +245 -21
- package/dist/bin/exe-gateway.js +229 -13
- package/dist/bin/exe-heartbeat.js +224 -12
- package/dist/bin/exe-kill.js +224 -12
- package/dist/bin/exe-launch-agent.js +177 -9
- package/dist/bin/exe-link.js +8 -0
- package/dist/bin/exe-new-employee.js +26 -2
- package/dist/bin/exe-pending-messages.js +224 -12
- package/dist/bin/exe-pending-notifications.js +224 -12
- package/dist/bin/exe-pending-reviews.js +224 -12
- package/dist/bin/exe-rename.js +9 -1
- package/dist/bin/exe-review.js +224 -12
- package/dist/bin/exe-search.js +246 -21
- package/dist/bin/exe-session-cleanup.js +229 -13
- package/dist/bin/exe-start-codex.js +161 -5
- package/dist/bin/exe-start-opencode.js +172 -5
- package/dist/bin/exe-status.js +224 -12
- package/dist/bin/exe-team.js +224 -12
- package/dist/bin/git-sweep.js +229 -13
- package/dist/bin/graph-backfill.js +94 -3
- package/dist/bin/graph-export.js +224 -12
- package/dist/bin/install.js +25 -1
- package/dist/bin/intercom-check.js +229 -13
- package/dist/bin/scan-tasks.js +229 -13
- package/dist/bin/setup.js +15 -5
- package/dist/bin/shard-migrate.js +94 -3
- package/dist/gateway/index.js +229 -13
- package/dist/hooks/bug-report-worker.js +229 -13
- package/dist/hooks/codex-stop-task-finalizer.js +224 -12
- package/dist/hooks/commit-complete.js +229 -13
- package/dist/hooks/error-recall.js +246 -21
- package/dist/hooks/ingest.js +224 -12
- package/dist/hooks/instructions-loaded.js +224 -12
- package/dist/hooks/notification.js +224 -12
- package/dist/hooks/post-compact.js +224 -12
- package/dist/hooks/post-tool-combined.js +246 -21
- package/dist/hooks/pre-compact.js +229 -13
- package/dist/hooks/pre-tool-use.js +234 -18
- package/dist/hooks/prompt-submit.js +346 -23
- package/dist/hooks/session-end.js +229 -13
- package/dist/hooks/session-start.js +418 -42
- package/dist/hooks/stop.js +224 -12
- package/dist/hooks/subagent-stop.js +224 -12
- package/dist/hooks/summary-worker.js +15 -2
- package/dist/index.js +229 -13
- 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/employee-templates.js +7 -5
- package/dist/lib/exe-daemon.js +1776 -1433
- package/dist/lib/hybrid-search.js +246 -21
- package/dist/lib/schedules.js +14 -3
- package/dist/lib/store.js +217 -12
- package/dist/lib/tasks.js +5 -1
- package/dist/lib/tmux-routing.js +5 -1
- package/dist/mcp/server.js +331 -37
- package/dist/mcp/tools/create-task.js +5 -1
- package/dist/mcp/tools/update-task.js +5 -1
- package/dist/runtime/index.js +229 -13
- package/dist/tui/App.js +229 -13
- package/package.json +1 -1
- package/src/commands/exe/save.md +48 -0
|
@@ -2318,6 +2318,14 @@ async function ensureSchema() {
|
|
|
2318
2318
|
);
|
|
2319
2319
|
} catch {
|
|
2320
2320
|
}
|
|
2321
|
+
try {
|
|
2322
|
+
await client.execute(
|
|
2323
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2324
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2325
|
+
WHERE content_hash IS NOT NULL`
|
|
2326
|
+
);
|
|
2327
|
+
} catch {
|
|
2328
|
+
}
|
|
2321
2329
|
await client.executeMultiple(`
|
|
2322
2330
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2323
2331
|
id TEXT PRIMARY KEY,
|
|
@@ -3063,6 +3071,197 @@ var init_state_bus = __esm({
|
|
|
3063
3071
|
}
|
|
3064
3072
|
});
|
|
3065
3073
|
|
|
3074
|
+
// src/lib/memory-write-governor.ts
|
|
3075
|
+
import { createHash } from "crypto";
|
|
3076
|
+
function normalizeMemoryText(text) {
|
|
3077
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3078
|
+
}
|
|
3079
|
+
function classifyMemoryType(input) {
|
|
3080
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3081
|
+
const tool = input.tool_name.toLowerCase();
|
|
3082
|
+
const text = input.raw_text.toLowerCase();
|
|
3083
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3084
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3085
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3086
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3087
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3088
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3089
|
+
return "raw";
|
|
3090
|
+
}
|
|
3091
|
+
function shouldDropMemory(text) {
|
|
3092
|
+
const normalized = normalizeMemoryText(text);
|
|
3093
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3094
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3095
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3096
|
+
}
|
|
3097
|
+
return { drop: false };
|
|
3098
|
+
}
|
|
3099
|
+
function shouldSkipEmbedding(input) {
|
|
3100
|
+
const type = classifyMemoryType(input);
|
|
3101
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3102
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3103
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3104
|
+
return false;
|
|
3105
|
+
}
|
|
3106
|
+
function hashMemoryContent(text) {
|
|
3107
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3108
|
+
}
|
|
3109
|
+
function scopedDedupArgs(input) {
|
|
3110
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3111
|
+
}
|
|
3112
|
+
function governMemoryRecord(record) {
|
|
3113
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3114
|
+
const memoryType = classifyMemoryType({
|
|
3115
|
+
raw_text: normalized,
|
|
3116
|
+
agent_id: record.agent_id,
|
|
3117
|
+
project_name: record.project_name,
|
|
3118
|
+
tool_name: record.tool_name,
|
|
3119
|
+
memory_type: record.memory_type
|
|
3120
|
+
});
|
|
3121
|
+
const drop = shouldDropMemory(normalized);
|
|
3122
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3123
|
+
raw_text: normalized,
|
|
3124
|
+
agent_id: record.agent_id,
|
|
3125
|
+
project_name: record.project_name,
|
|
3126
|
+
tool_name: record.tool_name,
|
|
3127
|
+
memory_type: memoryType
|
|
3128
|
+
});
|
|
3129
|
+
return {
|
|
3130
|
+
record: {
|
|
3131
|
+
...record,
|
|
3132
|
+
raw_text: normalized,
|
|
3133
|
+
memory_type: memoryType,
|
|
3134
|
+
vector: skipEmbedding ? null : record.vector
|
|
3135
|
+
},
|
|
3136
|
+
contentHash: hashMemoryContent(normalized),
|
|
3137
|
+
shouldDrop: drop.drop,
|
|
3138
|
+
dropReason: drop.reason,
|
|
3139
|
+
skipEmbedding,
|
|
3140
|
+
hygiene: {
|
|
3141
|
+
dedup: true,
|
|
3142
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3143
|
+
}
|
|
3144
|
+
};
|
|
3145
|
+
}
|
|
3146
|
+
async function findScopedDuplicate(input) {
|
|
3147
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3148
|
+
const client = getClient2();
|
|
3149
|
+
const args = scopedDedupArgs(input);
|
|
3150
|
+
let sql = `SELECT id FROM memories
|
|
3151
|
+
WHERE content_hash = ?
|
|
3152
|
+
AND agent_id = ?
|
|
3153
|
+
AND project_name = ?
|
|
3154
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3155
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3156
|
+
if (input.excludeId) {
|
|
3157
|
+
sql += " AND id != ?";
|
|
3158
|
+
args.push(input.excludeId);
|
|
3159
|
+
}
|
|
3160
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3161
|
+
const result = await client.execute({ sql, args });
|
|
3162
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3163
|
+
}
|
|
3164
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3165
|
+
try {
|
|
3166
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3167
|
+
const client = getClient2();
|
|
3168
|
+
const current = await client.execute({
|
|
3169
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3170
|
+
importance, timestamp
|
|
3171
|
+
FROM memories
|
|
3172
|
+
WHERE id = ?
|
|
3173
|
+
LIMIT 1`,
|
|
3174
|
+
args: [memoryId]
|
|
3175
|
+
});
|
|
3176
|
+
const row = current.rows[0];
|
|
3177
|
+
if (!row) return;
|
|
3178
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3179
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3180
|
+
const agentId = String(row.agent_id);
|
|
3181
|
+
const projectName = String(row.project_name);
|
|
3182
|
+
if (contentHash) {
|
|
3183
|
+
await client.execute({
|
|
3184
|
+
sql: `UPDATE memories
|
|
3185
|
+
SET status = 'deleted',
|
|
3186
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3187
|
+
WHERE id != ?
|
|
3188
|
+
AND content_hash = ?
|
|
3189
|
+
AND agent_id = ?
|
|
3190
|
+
AND project_name = ?
|
|
3191
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3192
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3193
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3197
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3198
|
+
const old = await client.execute({
|
|
3199
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3200
|
+
args: [supersedesId]
|
|
3201
|
+
});
|
|
3202
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3203
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3204
|
+
await client.batch([
|
|
3205
|
+
{
|
|
3206
|
+
sql: `UPDATE memories
|
|
3207
|
+
SET status = 'archived',
|
|
3208
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3209
|
+
WHERE id = ?`,
|
|
3210
|
+
args: [supersedesId]
|
|
3211
|
+
},
|
|
3212
|
+
{
|
|
3213
|
+
sql: `UPDATE memories
|
|
3214
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3215
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3216
|
+
WHERE id = ?`,
|
|
3217
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3218
|
+
}
|
|
3219
|
+
], "write");
|
|
3220
|
+
}
|
|
3221
|
+
} catch (err) {
|
|
3222
|
+
process.stderr.write(
|
|
3223
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3224
|
+
`
|
|
3225
|
+
);
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3229
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3230
|
+
if (memoryIds.length === 0) return;
|
|
3231
|
+
const run = () => {
|
|
3232
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3233
|
+
};
|
|
3234
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3235
|
+
else setTimeout(run, 0);
|
|
3236
|
+
}
|
|
3237
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3238
|
+
var init_memory_write_governor = __esm({
|
|
3239
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3240
|
+
"use strict";
|
|
3241
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3242
|
+
"decision",
|
|
3243
|
+
"adr",
|
|
3244
|
+
"behavior",
|
|
3245
|
+
"procedure"
|
|
3246
|
+
]);
|
|
3247
|
+
NOISE_DROP_PATTERNS = [
|
|
3248
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3249
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3250
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3251
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3252
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3253
|
+
];
|
|
3254
|
+
SKIP_EMBED_PATTERNS = [
|
|
3255
|
+
/tmux capture-pane\b/i,
|
|
3256
|
+
/docker ps\b/i,
|
|
3257
|
+
/docker images\b/i,
|
|
3258
|
+
/git status\b/i,
|
|
3259
|
+
/grep .*node_modules/i,
|
|
3260
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3261
|
+
];
|
|
3262
|
+
}
|
|
3263
|
+
});
|
|
3264
|
+
|
|
3066
3265
|
// src/lib/shard-manager.ts
|
|
3067
3266
|
var shard_manager_exports = {};
|
|
3068
3267
|
__export(shard_manager_exports, {
|
|
@@ -3227,7 +3426,8 @@ async function ensureShardSchema(client) {
|
|
|
3227
3426
|
}
|
|
3228
3427
|
for (const idx of [
|
|
3229
3428
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3230
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3429
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3430
|
+
"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"
|
|
3231
3431
|
]) {
|
|
3232
3432
|
try {
|
|
3233
3433
|
await client.execute(idx);
|
|
@@ -3421,7 +3621,7 @@ var init_platform_procedures = __esm({
|
|
|
3421
3621
|
title: "Chain of command \u2014 who talks to whom",
|
|
3422
3622
|
domain: "workflow",
|
|
3423
3623
|
priority: "p0",
|
|
3424
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3624
|
+
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
3425
3625
|
},
|
|
3426
3626
|
{
|
|
3427
3627
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3652,7 +3852,6 @@ __export(store_exports, {
|
|
|
3652
3852
|
vectorToBlob: () => vectorToBlob,
|
|
3653
3853
|
writeMemory: () => writeMemory
|
|
3654
3854
|
});
|
|
3655
|
-
import { createHash } from "crypto";
|
|
3656
3855
|
function isBusyError2(err) {
|
|
3657
3856
|
if (err instanceof Error) {
|
|
3658
3857
|
const msg = err.message.toLowerCase();
|
|
@@ -3766,17 +3965,24 @@ async function writeMemory(record) {
|
|
|
3766
3965
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3767
3966
|
);
|
|
3768
3967
|
}
|
|
3769
|
-
const
|
|
3770
|
-
if (
|
|
3968
|
+
const governed = governMemoryRecord(record);
|
|
3969
|
+
if (governed.shouldDrop) return;
|
|
3970
|
+
record = governed.record;
|
|
3971
|
+
const contentHash = governed.contentHash;
|
|
3972
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3973
|
+
if (_pendingRecords.some(
|
|
3974
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3975
|
+
)) {
|
|
3771
3976
|
return;
|
|
3772
3977
|
}
|
|
3773
3978
|
try {
|
|
3774
|
-
const
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3979
|
+
const existing = await findScopedDuplicate({
|
|
3980
|
+
contentHash,
|
|
3981
|
+
agentId: record.agent_id,
|
|
3982
|
+
projectName: record.project_name,
|
|
3983
|
+
memoryType
|
|
3778
3984
|
});
|
|
3779
|
-
if (existing
|
|
3985
|
+
if (existing) return;
|
|
3780
3986
|
} catch {
|
|
3781
3987
|
}
|
|
3782
3988
|
const dbRow = {
|
|
@@ -3807,7 +4013,7 @@ async function writeMemory(record) {
|
|
|
3807
4013
|
tier: record.tier ?? classifyTier(record),
|
|
3808
4014
|
supersedes_id: record.supersedes_id ?? null,
|
|
3809
4015
|
draft: record.draft ? 1 : 0,
|
|
3810
|
-
memory_type:
|
|
4016
|
+
memory_type: memoryType,
|
|
3811
4017
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3812
4018
|
content_hash: contentHash,
|
|
3813
4019
|
intent: record.intent ?? null,
|
|
@@ -3966,6 +4172,7 @@ async function flushBatch() {
|
|
|
3966
4172
|
const globalClient = getClient();
|
|
3967
4173
|
const globalStmts = batch.map(buildStmt);
|
|
3968
4174
|
await globalClient.batch(globalStmts, "write");
|
|
4175
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3969
4176
|
_pendingRecords.splice(0, batch.length);
|
|
3970
4177
|
try {
|
|
3971
4178
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4086,7 +4293,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4086
4293
|
sql += ` AND timestamp >= ?`;
|
|
4087
4294
|
args.push(options.since);
|
|
4088
4295
|
}
|
|
4089
|
-
if (options?.
|
|
4296
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4297
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4298
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4299
|
+
args.push(...uniqueTypes);
|
|
4300
|
+
} else if (options?.memoryType) {
|
|
4090
4301
|
sql += ` AND memory_type = ?`;
|
|
4091
4302
|
args.push(options.memoryType);
|
|
4092
4303
|
}
|
|
@@ -4224,6 +4435,7 @@ var init_store = __esm({
|
|
|
4224
4435
|
init_keychain();
|
|
4225
4436
|
init_config();
|
|
4226
4437
|
init_state_bus();
|
|
4438
|
+
init_memory_write_governor();
|
|
4227
4439
|
INIT_MAX_RETRIES = 3;
|
|
4228
4440
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4229
4441
|
_pendingRecords = [];
|
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,197 @@ 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 (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3200
|
+
if (memoryIds.length === 0) return;
|
|
3201
|
+
const run = () => {
|
|
3202
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3203
|
+
};
|
|
3204
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3205
|
+
else setTimeout(run, 0);
|
|
3206
|
+
}
|
|
3207
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3208
|
+
var init_memory_write_governor = __esm({
|
|
3209
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3210
|
+
"use strict";
|
|
3211
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3212
|
+
"decision",
|
|
3213
|
+
"adr",
|
|
3214
|
+
"behavior",
|
|
3215
|
+
"procedure"
|
|
3216
|
+
]);
|
|
3217
|
+
NOISE_DROP_PATTERNS = [
|
|
3218
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3219
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3220
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3221
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3222
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3223
|
+
];
|
|
3224
|
+
SKIP_EMBED_PATTERNS = [
|
|
3225
|
+
/tmux capture-pane\b/i,
|
|
3226
|
+
/docker ps\b/i,
|
|
3227
|
+
/docker images\b/i,
|
|
3228
|
+
/git status\b/i,
|
|
3229
|
+
/grep .*node_modules/i,
|
|
3230
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3231
|
+
];
|
|
3232
|
+
}
|
|
3233
|
+
});
|
|
3234
|
+
|
|
3036
3235
|
// src/lib/shard-manager.ts
|
|
3037
3236
|
var shard_manager_exports = {};
|
|
3038
3237
|
__export(shard_manager_exports, {
|
|
@@ -3197,7 +3396,8 @@ async function ensureShardSchema(client) {
|
|
|
3197
3396
|
}
|
|
3198
3397
|
for (const idx of [
|
|
3199
3398
|
"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"
|
|
3399
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3400
|
+
"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
3401
|
]) {
|
|
3202
3402
|
try {
|
|
3203
3403
|
await client.execute(idx);
|
|
@@ -3391,7 +3591,7 @@ var init_platform_procedures = __esm({
|
|
|
3391
3591
|
title: "Chain of command \u2014 who talks to whom",
|
|
3392
3592
|
domain: "workflow",
|
|
3393
3593
|
priority: "p0",
|
|
3394
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3594
|
+
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
3395
3595
|
},
|
|
3396
3596
|
{
|
|
3397
3597
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3622,7 +3822,6 @@ __export(store_exports, {
|
|
|
3622
3822
|
vectorToBlob: () => vectorToBlob,
|
|
3623
3823
|
writeMemory: () => writeMemory
|
|
3624
3824
|
});
|
|
3625
|
-
import { createHash } from "crypto";
|
|
3626
3825
|
function isBusyError2(err) {
|
|
3627
3826
|
if (err instanceof Error) {
|
|
3628
3827
|
const msg = err.message.toLowerCase();
|
|
@@ -3736,17 +3935,24 @@ async function writeMemory(record) {
|
|
|
3736
3935
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3737
3936
|
);
|
|
3738
3937
|
}
|
|
3739
|
-
const
|
|
3740
|
-
if (
|
|
3938
|
+
const governed = governMemoryRecord(record);
|
|
3939
|
+
if (governed.shouldDrop) return;
|
|
3940
|
+
record = governed.record;
|
|
3941
|
+
const contentHash = governed.contentHash;
|
|
3942
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3943
|
+
if (_pendingRecords.some(
|
|
3944
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3945
|
+
)) {
|
|
3741
3946
|
return;
|
|
3742
3947
|
}
|
|
3743
3948
|
try {
|
|
3744
|
-
const
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3949
|
+
const existing = await findScopedDuplicate({
|
|
3950
|
+
contentHash,
|
|
3951
|
+
agentId: record.agent_id,
|
|
3952
|
+
projectName: record.project_name,
|
|
3953
|
+
memoryType
|
|
3748
3954
|
});
|
|
3749
|
-
if (existing
|
|
3955
|
+
if (existing) return;
|
|
3750
3956
|
} catch {
|
|
3751
3957
|
}
|
|
3752
3958
|
const dbRow = {
|
|
@@ -3777,7 +3983,7 @@ async function writeMemory(record) {
|
|
|
3777
3983
|
tier: record.tier ?? classifyTier(record),
|
|
3778
3984
|
supersedes_id: record.supersedes_id ?? null,
|
|
3779
3985
|
draft: record.draft ? 1 : 0,
|
|
3780
|
-
memory_type:
|
|
3986
|
+
memory_type: memoryType,
|
|
3781
3987
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3782
3988
|
content_hash: contentHash,
|
|
3783
3989
|
intent: record.intent ?? null,
|
|
@@ -3936,6 +4142,7 @@ async function flushBatch() {
|
|
|
3936
4142
|
const globalClient = getClient();
|
|
3937
4143
|
const globalStmts = batch.map(buildStmt);
|
|
3938
4144
|
await globalClient.batch(globalStmts, "write");
|
|
4145
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3939
4146
|
_pendingRecords.splice(0, batch.length);
|
|
3940
4147
|
try {
|
|
3941
4148
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4056,7 +4263,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4056
4263
|
sql += ` AND timestamp >= ?`;
|
|
4057
4264
|
args.push(options.since);
|
|
4058
4265
|
}
|
|
4059
|
-
if (options?.
|
|
4266
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4267
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4268
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4269
|
+
args.push(...uniqueTypes);
|
|
4270
|
+
} else if (options?.memoryType) {
|
|
4060
4271
|
sql += ` AND memory_type = ?`;
|
|
4061
4272
|
args.push(options.memoryType);
|
|
4062
4273
|
}
|
|
@@ -4194,6 +4405,7 @@ var init_store = __esm({
|
|
|
4194
4405
|
init_keychain();
|
|
4195
4406
|
init_config();
|
|
4196
4407
|
init_state_bus();
|
|
4408
|
+
init_memory_write_governor();
|
|
4197
4409
|
INIT_MAX_RETRIES = 3;
|
|
4198
4410
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4199
4411
|
_pendingRecords = [];
|