@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
package/dist/bin/exe-review.js
CHANGED
|
@@ -2302,6 +2302,14 @@ async function ensureSchema() {
|
|
|
2302
2302
|
);
|
|
2303
2303
|
} catch {
|
|
2304
2304
|
}
|
|
2305
|
+
try {
|
|
2306
|
+
await client.execute(
|
|
2307
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2308
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2309
|
+
WHERE content_hash IS NOT NULL`
|
|
2310
|
+
);
|
|
2311
|
+
} catch {
|
|
2312
|
+
}
|
|
2305
2313
|
await client.executeMultiple(`
|
|
2306
2314
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2307
2315
|
id TEXT PRIMARY KEY,
|
|
@@ -3047,6 +3055,197 @@ var init_state_bus = __esm({
|
|
|
3047
3055
|
}
|
|
3048
3056
|
});
|
|
3049
3057
|
|
|
3058
|
+
// src/lib/memory-write-governor.ts
|
|
3059
|
+
import { createHash } from "crypto";
|
|
3060
|
+
function normalizeMemoryText(text) {
|
|
3061
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3062
|
+
}
|
|
3063
|
+
function classifyMemoryType(input) {
|
|
3064
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3065
|
+
const tool = input.tool_name.toLowerCase();
|
|
3066
|
+
const text = input.raw_text.toLowerCase();
|
|
3067
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3068
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3069
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3070
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3071
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3072
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3073
|
+
return "raw";
|
|
3074
|
+
}
|
|
3075
|
+
function shouldDropMemory(text) {
|
|
3076
|
+
const normalized = normalizeMemoryText(text);
|
|
3077
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3078
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3079
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3080
|
+
}
|
|
3081
|
+
return { drop: false };
|
|
3082
|
+
}
|
|
3083
|
+
function shouldSkipEmbedding(input) {
|
|
3084
|
+
const type = classifyMemoryType(input);
|
|
3085
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3086
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3087
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3088
|
+
return false;
|
|
3089
|
+
}
|
|
3090
|
+
function hashMemoryContent(text) {
|
|
3091
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3092
|
+
}
|
|
3093
|
+
function scopedDedupArgs(input) {
|
|
3094
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3095
|
+
}
|
|
3096
|
+
function governMemoryRecord(record) {
|
|
3097
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3098
|
+
const memoryType = classifyMemoryType({
|
|
3099
|
+
raw_text: normalized,
|
|
3100
|
+
agent_id: record.agent_id,
|
|
3101
|
+
project_name: record.project_name,
|
|
3102
|
+
tool_name: record.tool_name,
|
|
3103
|
+
memory_type: record.memory_type
|
|
3104
|
+
});
|
|
3105
|
+
const drop = shouldDropMemory(normalized);
|
|
3106
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3107
|
+
raw_text: normalized,
|
|
3108
|
+
agent_id: record.agent_id,
|
|
3109
|
+
project_name: record.project_name,
|
|
3110
|
+
tool_name: record.tool_name,
|
|
3111
|
+
memory_type: memoryType
|
|
3112
|
+
});
|
|
3113
|
+
return {
|
|
3114
|
+
record: {
|
|
3115
|
+
...record,
|
|
3116
|
+
raw_text: normalized,
|
|
3117
|
+
memory_type: memoryType,
|
|
3118
|
+
vector: skipEmbedding ? null : record.vector
|
|
3119
|
+
},
|
|
3120
|
+
contentHash: hashMemoryContent(normalized),
|
|
3121
|
+
shouldDrop: drop.drop,
|
|
3122
|
+
dropReason: drop.reason,
|
|
3123
|
+
skipEmbedding,
|
|
3124
|
+
hygiene: {
|
|
3125
|
+
dedup: true,
|
|
3126
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3127
|
+
}
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
async function findScopedDuplicate(input) {
|
|
3131
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3132
|
+
const client = getClient2();
|
|
3133
|
+
const args = scopedDedupArgs(input);
|
|
3134
|
+
let sql = `SELECT id FROM memories
|
|
3135
|
+
WHERE content_hash = ?
|
|
3136
|
+
AND agent_id = ?
|
|
3137
|
+
AND project_name = ?
|
|
3138
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3139
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3140
|
+
if (input.excludeId) {
|
|
3141
|
+
sql += " AND id != ?";
|
|
3142
|
+
args.push(input.excludeId);
|
|
3143
|
+
}
|
|
3144
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3145
|
+
const result = await client.execute({ sql, args });
|
|
3146
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3147
|
+
}
|
|
3148
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3149
|
+
try {
|
|
3150
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3151
|
+
const client = getClient2();
|
|
3152
|
+
const current = await client.execute({
|
|
3153
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3154
|
+
importance, timestamp
|
|
3155
|
+
FROM memories
|
|
3156
|
+
WHERE id = ?
|
|
3157
|
+
LIMIT 1`,
|
|
3158
|
+
args: [memoryId]
|
|
3159
|
+
});
|
|
3160
|
+
const row = current.rows[0];
|
|
3161
|
+
if (!row) return;
|
|
3162
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3163
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3164
|
+
const agentId = String(row.agent_id);
|
|
3165
|
+
const projectName = String(row.project_name);
|
|
3166
|
+
if (contentHash) {
|
|
3167
|
+
await client.execute({
|
|
3168
|
+
sql: `UPDATE memories
|
|
3169
|
+
SET status = 'deleted',
|
|
3170
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3171
|
+
WHERE id != ?
|
|
3172
|
+
AND content_hash = ?
|
|
3173
|
+
AND agent_id = ?
|
|
3174
|
+
AND project_name = ?
|
|
3175
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3176
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3177
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3178
|
+
});
|
|
3179
|
+
}
|
|
3180
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3181
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3182
|
+
const old = await client.execute({
|
|
3183
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3184
|
+
args: [supersedesId]
|
|
3185
|
+
});
|
|
3186
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3187
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3188
|
+
await client.batch([
|
|
3189
|
+
{
|
|
3190
|
+
sql: `UPDATE memories
|
|
3191
|
+
SET status = 'archived',
|
|
3192
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3193
|
+
WHERE id = ?`,
|
|
3194
|
+
args: [supersedesId]
|
|
3195
|
+
},
|
|
3196
|
+
{
|
|
3197
|
+
sql: `UPDATE memories
|
|
3198
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3199
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3200
|
+
WHERE id = ?`,
|
|
3201
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3202
|
+
}
|
|
3203
|
+
], "write");
|
|
3204
|
+
}
|
|
3205
|
+
} catch (err) {
|
|
3206
|
+
process.stderr.write(
|
|
3207
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3208
|
+
`
|
|
3209
|
+
);
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3213
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3214
|
+
if (memoryIds.length === 0) return;
|
|
3215
|
+
const run = () => {
|
|
3216
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3217
|
+
};
|
|
3218
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3219
|
+
else setTimeout(run, 0);
|
|
3220
|
+
}
|
|
3221
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3222
|
+
var init_memory_write_governor = __esm({
|
|
3223
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3224
|
+
"use strict";
|
|
3225
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3226
|
+
"decision",
|
|
3227
|
+
"adr",
|
|
3228
|
+
"behavior",
|
|
3229
|
+
"procedure"
|
|
3230
|
+
]);
|
|
3231
|
+
NOISE_DROP_PATTERNS = [
|
|
3232
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3233
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3234
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3235
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3236
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3237
|
+
];
|
|
3238
|
+
SKIP_EMBED_PATTERNS = [
|
|
3239
|
+
/tmux capture-pane\b/i,
|
|
3240
|
+
/docker ps\b/i,
|
|
3241
|
+
/docker images\b/i,
|
|
3242
|
+
/git status\b/i,
|
|
3243
|
+
/grep .*node_modules/i,
|
|
3244
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3245
|
+
];
|
|
3246
|
+
}
|
|
3247
|
+
});
|
|
3248
|
+
|
|
3050
3249
|
// src/lib/shard-manager.ts
|
|
3051
3250
|
var shard_manager_exports = {};
|
|
3052
3251
|
__export(shard_manager_exports, {
|
|
@@ -3211,7 +3410,8 @@ async function ensureShardSchema(client) {
|
|
|
3211
3410
|
}
|
|
3212
3411
|
for (const idx of [
|
|
3213
3412
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3214
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3413
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3414
|
+
"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"
|
|
3215
3415
|
]) {
|
|
3216
3416
|
try {
|
|
3217
3417
|
await client.execute(idx);
|
|
@@ -3405,7 +3605,7 @@ var init_platform_procedures = __esm({
|
|
|
3405
3605
|
title: "Chain of command \u2014 who talks to whom",
|
|
3406
3606
|
domain: "workflow",
|
|
3407
3607
|
priority: "p0",
|
|
3408
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3608
|
+
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."
|
|
3409
3609
|
},
|
|
3410
3610
|
{
|
|
3411
3611
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3636,7 +3836,6 @@ __export(store_exports, {
|
|
|
3636
3836
|
vectorToBlob: () => vectorToBlob,
|
|
3637
3837
|
writeMemory: () => writeMemory
|
|
3638
3838
|
});
|
|
3639
|
-
import { createHash } from "crypto";
|
|
3640
3839
|
function isBusyError2(err) {
|
|
3641
3840
|
if (err instanceof Error) {
|
|
3642
3841
|
const msg = err.message.toLowerCase();
|
|
@@ -3750,17 +3949,24 @@ async function writeMemory(record) {
|
|
|
3750
3949
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3751
3950
|
);
|
|
3752
3951
|
}
|
|
3753
|
-
const
|
|
3754
|
-
if (
|
|
3952
|
+
const governed = governMemoryRecord(record);
|
|
3953
|
+
if (governed.shouldDrop) return;
|
|
3954
|
+
record = governed.record;
|
|
3955
|
+
const contentHash = governed.contentHash;
|
|
3956
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3957
|
+
if (_pendingRecords.some(
|
|
3958
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3959
|
+
)) {
|
|
3755
3960
|
return;
|
|
3756
3961
|
}
|
|
3757
3962
|
try {
|
|
3758
|
-
const
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3963
|
+
const existing = await findScopedDuplicate({
|
|
3964
|
+
contentHash,
|
|
3965
|
+
agentId: record.agent_id,
|
|
3966
|
+
projectName: record.project_name,
|
|
3967
|
+
memoryType
|
|
3762
3968
|
});
|
|
3763
|
-
if (existing
|
|
3969
|
+
if (existing) return;
|
|
3764
3970
|
} catch {
|
|
3765
3971
|
}
|
|
3766
3972
|
const dbRow = {
|
|
@@ -3791,7 +3997,7 @@ async function writeMemory(record) {
|
|
|
3791
3997
|
tier: record.tier ?? classifyTier(record),
|
|
3792
3998
|
supersedes_id: record.supersedes_id ?? null,
|
|
3793
3999
|
draft: record.draft ? 1 : 0,
|
|
3794
|
-
memory_type:
|
|
4000
|
+
memory_type: memoryType,
|
|
3795
4001
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3796
4002
|
content_hash: contentHash,
|
|
3797
4003
|
intent: record.intent ?? null,
|
|
@@ -3950,6 +4156,7 @@ async function flushBatch() {
|
|
|
3950
4156
|
const globalClient = getClient();
|
|
3951
4157
|
const globalStmts = batch.map(buildStmt);
|
|
3952
4158
|
await globalClient.batch(globalStmts, "write");
|
|
4159
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3953
4160
|
_pendingRecords.splice(0, batch.length);
|
|
3954
4161
|
try {
|
|
3955
4162
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4070,7 +4277,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4070
4277
|
sql += ` AND timestamp >= ?`;
|
|
4071
4278
|
args.push(options.since);
|
|
4072
4279
|
}
|
|
4073
|
-
if (options?.
|
|
4280
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4281
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4282
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4283
|
+
args.push(...uniqueTypes);
|
|
4284
|
+
} else if (options?.memoryType) {
|
|
4074
4285
|
sql += ` AND memory_type = ?`;
|
|
4075
4286
|
args.push(options.memoryType);
|
|
4076
4287
|
}
|
|
@@ -4208,6 +4419,7 @@ var init_store = __esm({
|
|
|
4208
4419
|
init_keychain();
|
|
4209
4420
|
init_config();
|
|
4210
4421
|
init_state_bus();
|
|
4422
|
+
init_memory_write_governor();
|
|
4211
4423
|
INIT_MAX_RETRIES = 3;
|
|
4212
4424
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4213
4425
|
_pendingRecords = [];
|
package/dist/bin/exe-search.js
CHANGED
|
@@ -2277,6 +2277,14 @@ async function ensureSchema() {
|
|
|
2277
2277
|
);
|
|
2278
2278
|
} catch {
|
|
2279
2279
|
}
|
|
2280
|
+
try {
|
|
2281
|
+
await client.execute(
|
|
2282
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2283
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2284
|
+
WHERE content_hash IS NOT NULL`
|
|
2285
|
+
);
|
|
2286
|
+
} catch {
|
|
2287
|
+
}
|
|
2280
2288
|
await client.executeMultiple(`
|
|
2281
2289
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2282
2290
|
id TEXT PRIMARY KEY,
|
|
@@ -3022,6 +3030,197 @@ var init_state_bus = __esm({
|
|
|
3022
3030
|
}
|
|
3023
3031
|
});
|
|
3024
3032
|
|
|
3033
|
+
// src/lib/memory-write-governor.ts
|
|
3034
|
+
import { createHash } from "crypto";
|
|
3035
|
+
function normalizeMemoryText(text) {
|
|
3036
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3037
|
+
}
|
|
3038
|
+
function classifyMemoryType(input) {
|
|
3039
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
3040
|
+
const tool = input.tool_name.toLowerCase();
|
|
3041
|
+
const text = input.raw_text.toLowerCase();
|
|
3042
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3043
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3044
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3045
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3046
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3047
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3048
|
+
return "raw";
|
|
3049
|
+
}
|
|
3050
|
+
function shouldDropMemory(text) {
|
|
3051
|
+
const normalized = normalizeMemoryText(text);
|
|
3052
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3053
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3054
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3055
|
+
}
|
|
3056
|
+
return { drop: false };
|
|
3057
|
+
}
|
|
3058
|
+
function shouldSkipEmbedding(input) {
|
|
3059
|
+
const type = classifyMemoryType(input);
|
|
3060
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3061
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
3062
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
3063
|
+
return false;
|
|
3064
|
+
}
|
|
3065
|
+
function hashMemoryContent(text) {
|
|
3066
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3067
|
+
}
|
|
3068
|
+
function scopedDedupArgs(input) {
|
|
3069
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
3070
|
+
}
|
|
3071
|
+
function governMemoryRecord(record) {
|
|
3072
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3073
|
+
const memoryType = classifyMemoryType({
|
|
3074
|
+
raw_text: normalized,
|
|
3075
|
+
agent_id: record.agent_id,
|
|
3076
|
+
project_name: record.project_name,
|
|
3077
|
+
tool_name: record.tool_name,
|
|
3078
|
+
memory_type: record.memory_type
|
|
3079
|
+
});
|
|
3080
|
+
const drop = shouldDropMemory(normalized);
|
|
3081
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3082
|
+
raw_text: normalized,
|
|
3083
|
+
agent_id: record.agent_id,
|
|
3084
|
+
project_name: record.project_name,
|
|
3085
|
+
tool_name: record.tool_name,
|
|
3086
|
+
memory_type: memoryType
|
|
3087
|
+
});
|
|
3088
|
+
return {
|
|
3089
|
+
record: {
|
|
3090
|
+
...record,
|
|
3091
|
+
raw_text: normalized,
|
|
3092
|
+
memory_type: memoryType,
|
|
3093
|
+
vector: skipEmbedding ? null : record.vector
|
|
3094
|
+
},
|
|
3095
|
+
contentHash: hashMemoryContent(normalized),
|
|
3096
|
+
shouldDrop: drop.drop,
|
|
3097
|
+
dropReason: drop.reason,
|
|
3098
|
+
skipEmbedding,
|
|
3099
|
+
hygiene: {
|
|
3100
|
+
dedup: true,
|
|
3101
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3102
|
+
}
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
async function findScopedDuplicate(input) {
|
|
3106
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3107
|
+
const client = getClient2();
|
|
3108
|
+
const args = scopedDedupArgs(input);
|
|
3109
|
+
let sql = `SELECT id FROM memories
|
|
3110
|
+
WHERE content_hash = ?
|
|
3111
|
+
AND agent_id = ?
|
|
3112
|
+
AND project_name = ?
|
|
3113
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3114
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3115
|
+
if (input.excludeId) {
|
|
3116
|
+
sql += " AND id != ?";
|
|
3117
|
+
args.push(input.excludeId);
|
|
3118
|
+
}
|
|
3119
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3120
|
+
const result = await client.execute({ sql, args });
|
|
3121
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3122
|
+
}
|
|
3123
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3124
|
+
try {
|
|
3125
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3126
|
+
const client = getClient2();
|
|
3127
|
+
const current = await client.execute({
|
|
3128
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3129
|
+
importance, timestamp
|
|
3130
|
+
FROM memories
|
|
3131
|
+
WHERE id = ?
|
|
3132
|
+
LIMIT 1`,
|
|
3133
|
+
args: [memoryId]
|
|
3134
|
+
});
|
|
3135
|
+
const row = current.rows[0];
|
|
3136
|
+
if (!row) return;
|
|
3137
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3138
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3139
|
+
const agentId = String(row.agent_id);
|
|
3140
|
+
const projectName = String(row.project_name);
|
|
3141
|
+
if (contentHash) {
|
|
3142
|
+
await client.execute({
|
|
3143
|
+
sql: `UPDATE memories
|
|
3144
|
+
SET status = 'deleted',
|
|
3145
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3146
|
+
WHERE id != ?
|
|
3147
|
+
AND content_hash = ?
|
|
3148
|
+
AND agent_id = ?
|
|
3149
|
+
AND project_name = ?
|
|
3150
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3151
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3152
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3153
|
+
});
|
|
3154
|
+
}
|
|
3155
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3156
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3157
|
+
const old = await client.execute({
|
|
3158
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3159
|
+
args: [supersedesId]
|
|
3160
|
+
});
|
|
3161
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3162
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3163
|
+
await client.batch([
|
|
3164
|
+
{
|
|
3165
|
+
sql: `UPDATE memories
|
|
3166
|
+
SET status = 'archived',
|
|
3167
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3168
|
+
WHERE id = ?`,
|
|
3169
|
+
args: [supersedesId]
|
|
3170
|
+
},
|
|
3171
|
+
{
|
|
3172
|
+
sql: `UPDATE memories
|
|
3173
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3174
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3175
|
+
WHERE id = ?`,
|
|
3176
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3177
|
+
}
|
|
3178
|
+
], "write");
|
|
3179
|
+
}
|
|
3180
|
+
} catch (err) {
|
|
3181
|
+
process.stderr.write(
|
|
3182
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3183
|
+
`
|
|
3184
|
+
);
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3188
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3189
|
+
if (memoryIds.length === 0) return;
|
|
3190
|
+
const run = () => {
|
|
3191
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3192
|
+
};
|
|
3193
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3194
|
+
else setTimeout(run, 0);
|
|
3195
|
+
}
|
|
3196
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3197
|
+
var init_memory_write_governor = __esm({
|
|
3198
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3199
|
+
"use strict";
|
|
3200
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3201
|
+
"decision",
|
|
3202
|
+
"adr",
|
|
3203
|
+
"behavior",
|
|
3204
|
+
"procedure"
|
|
3205
|
+
]);
|
|
3206
|
+
NOISE_DROP_PATTERNS = [
|
|
3207
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3208
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3209
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3210
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3211
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3212
|
+
];
|
|
3213
|
+
SKIP_EMBED_PATTERNS = [
|
|
3214
|
+
/tmux capture-pane\b/i,
|
|
3215
|
+
/docker ps\b/i,
|
|
3216
|
+
/docker images\b/i,
|
|
3217
|
+
/git status\b/i,
|
|
3218
|
+
/grep .*node_modules/i,
|
|
3219
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3220
|
+
];
|
|
3221
|
+
}
|
|
3222
|
+
});
|
|
3223
|
+
|
|
3025
3224
|
// src/lib/shard-manager.ts
|
|
3026
3225
|
var shard_manager_exports = {};
|
|
3027
3226
|
__export(shard_manager_exports, {
|
|
@@ -3186,7 +3385,8 @@ async function ensureShardSchema(client) {
|
|
|
3186
3385
|
}
|
|
3187
3386
|
for (const idx of [
|
|
3188
3387
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3189
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3388
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3389
|
+
"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"
|
|
3190
3390
|
]) {
|
|
3191
3391
|
try {
|
|
3192
3392
|
await client.execute(idx);
|
|
@@ -3380,7 +3580,7 @@ var init_platform_procedures = __esm({
|
|
|
3380
3580
|
title: "Chain of command \u2014 who talks to whom",
|
|
3381
3581
|
domain: "workflow",
|
|
3382
3582
|
priority: "p0",
|
|
3383
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3583
|
+
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."
|
|
3384
3584
|
},
|
|
3385
3585
|
{
|
|
3386
3586
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3611,7 +3811,6 @@ __export(store_exports, {
|
|
|
3611
3811
|
vectorToBlob: () => vectorToBlob,
|
|
3612
3812
|
writeMemory: () => writeMemory
|
|
3613
3813
|
});
|
|
3614
|
-
import { createHash } from "crypto";
|
|
3615
3814
|
function isBusyError2(err) {
|
|
3616
3815
|
if (err instanceof Error) {
|
|
3617
3816
|
const msg = err.message.toLowerCase();
|
|
@@ -3725,17 +3924,24 @@ async function writeMemory(record) {
|
|
|
3725
3924
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3726
3925
|
);
|
|
3727
3926
|
}
|
|
3728
|
-
const
|
|
3729
|
-
if (
|
|
3927
|
+
const governed = governMemoryRecord(record);
|
|
3928
|
+
if (governed.shouldDrop) return;
|
|
3929
|
+
record = governed.record;
|
|
3930
|
+
const contentHash = governed.contentHash;
|
|
3931
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3932
|
+
if (_pendingRecords.some(
|
|
3933
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3934
|
+
)) {
|
|
3730
3935
|
return;
|
|
3731
3936
|
}
|
|
3732
3937
|
try {
|
|
3733
|
-
const
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3938
|
+
const existing = await findScopedDuplicate({
|
|
3939
|
+
contentHash,
|
|
3940
|
+
agentId: record.agent_id,
|
|
3941
|
+
projectName: record.project_name,
|
|
3942
|
+
memoryType
|
|
3737
3943
|
});
|
|
3738
|
-
if (existing
|
|
3944
|
+
if (existing) return;
|
|
3739
3945
|
} catch {
|
|
3740
3946
|
}
|
|
3741
3947
|
const dbRow = {
|
|
@@ -3766,7 +3972,7 @@ async function writeMemory(record) {
|
|
|
3766
3972
|
tier: record.tier ?? classifyTier(record),
|
|
3767
3973
|
supersedes_id: record.supersedes_id ?? null,
|
|
3768
3974
|
draft: record.draft ? 1 : 0,
|
|
3769
|
-
memory_type:
|
|
3975
|
+
memory_type: memoryType,
|
|
3770
3976
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3771
3977
|
content_hash: contentHash,
|
|
3772
3978
|
intent: record.intent ?? null,
|
|
@@ -3925,6 +4131,7 @@ async function flushBatch() {
|
|
|
3925
4131
|
const globalClient = getClient();
|
|
3926
4132
|
const globalStmts = batch.map(buildStmt);
|
|
3927
4133
|
await globalClient.batch(globalStmts, "write");
|
|
4134
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3928
4135
|
_pendingRecords.splice(0, batch.length);
|
|
3929
4136
|
try {
|
|
3930
4137
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4045,7 +4252,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4045
4252
|
sql += ` AND timestamp >= ?`;
|
|
4046
4253
|
args.push(options.since);
|
|
4047
4254
|
}
|
|
4048
|
-
if (options?.
|
|
4255
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4256
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4257
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4258
|
+
args.push(...uniqueTypes);
|
|
4259
|
+
} else if (options?.memoryType) {
|
|
4049
4260
|
sql += ` AND memory_type = ?`;
|
|
4050
4261
|
args.push(options.memoryType);
|
|
4051
4262
|
}
|
|
@@ -4183,6 +4394,7 @@ var init_store = __esm({
|
|
|
4183
4394
|
init_keychain();
|
|
4184
4395
|
init_config();
|
|
4185
4396
|
init_state_bus();
|
|
4397
|
+
init_memory_write_governor();
|
|
4186
4398
|
INIT_MAX_RETRIES = 3;
|
|
4187
4399
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4188
4400
|
_pendingRecords = [];
|
|
@@ -5339,6 +5551,17 @@ init_store();
|
|
|
5339
5551
|
init_store();
|
|
5340
5552
|
init_database();
|
|
5341
5553
|
var RRF_K = 60;
|
|
5554
|
+
function appendMemoryTypeFilter(sql, args, column, options) {
|
|
5555
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
5556
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
5557
|
+
sql += ` AND ${column} IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
5558
|
+
args.push(...uniqueTypes);
|
|
5559
|
+
} else if (options?.memoryType) {
|
|
5560
|
+
sql += ` AND ${column} = ?`;
|
|
5561
|
+
args.push(options.memoryType);
|
|
5562
|
+
}
|
|
5563
|
+
return sql;
|
|
5564
|
+
}
|
|
5342
5565
|
async function hybridSearch(queryText, agentId, options) {
|
|
5343
5566
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
5344
5567
|
const config = await loadConfig2();
|
|
@@ -5567,6 +5790,7 @@ async function estimateCardinality(agentId, options) {
|
|
|
5567
5790
|
sql += ` AND timestamp >= ?`;
|
|
5568
5791
|
args.push(options.since);
|
|
5569
5792
|
}
|
|
5793
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
5570
5794
|
try {
|
|
5571
5795
|
const result = await client.execute({ sql, args });
|
|
5572
5796
|
return Number(result.rows[0]?.cnt) || 0;
|
|
@@ -5685,10 +5909,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
5685
5909
|
sql += ` AND m.timestamp >= ?`;
|
|
5686
5910
|
args.push(options.since);
|
|
5687
5911
|
}
|
|
5688
|
-
|
|
5689
|
-
sql += ` AND m.memory_type = ?`;
|
|
5690
|
-
args.push(options.memoryType);
|
|
5691
|
-
}
|
|
5912
|
+
sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
|
|
5692
5913
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
5693
5914
|
args.push(limit);
|
|
5694
5915
|
const result = await client.execute({ sql, args });
|
|
@@ -5745,9 +5966,16 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
5745
5966
|
AND timestamp >= ? AND timestamp <= ?
|
|
5746
5967
|
AND COALESCE(status, 'active') = 'active'
|
|
5747
5968
|
AND ${options?.includeRaw === false ? "COALESCE(memory_type, 'raw') != 'raw'" : "1 = 1"}
|
|
5969
|
+
${options?.memoryTypes?.length ? `AND memory_type IN (${options.memoryTypes.map(() => "?").join(",")})` : options?.memoryType ? "AND memory_type = ?" : ""}
|
|
5748
5970
|
AND COALESCE(confidence, 0.7) >= 0.3
|
|
5749
5971
|
ORDER BY timestamp DESC LIMIT ?`,
|
|
5750
|
-
args: [
|
|
5972
|
+
args: [
|
|
5973
|
+
agentId,
|
|
5974
|
+
windowStart,
|
|
5975
|
+
killedAt,
|
|
5976
|
+
...options?.memoryTypes?.length ? [...new Set(options.memoryTypes)] : options?.memoryType ? [options.memoryType] : [],
|
|
5977
|
+
boundarySlots
|
|
5978
|
+
]
|
|
5751
5979
|
});
|
|
5752
5980
|
for (const row of boundaryResult.rows) {
|
|
5753
5981
|
sessionBoundaryMemories.push(rowToMemoryRecord(row));
|
|
@@ -5793,10 +6021,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
5793
6021
|
sql += ` AND timestamp >= ?`;
|
|
5794
6022
|
args.push(options.since);
|
|
5795
6023
|
}
|
|
5796
|
-
|
|
5797
|
-
sql += ` AND memory_type = ?`;
|
|
5798
|
-
args.push(options.memoryType);
|
|
5799
|
-
}
|
|
6024
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
5800
6025
|
if (textFilter) {
|
|
5801
6026
|
sql += ` AND raw_text LIKE '%' || ? || '%'`;
|
|
5802
6027
|
args.push(textFilter);
|