@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
|
@@ -2290,6 +2290,14 @@ async function ensureSchema() {
|
|
|
2290
2290
|
);
|
|
2291
2291
|
} catch {
|
|
2292
2292
|
}
|
|
2293
|
+
try {
|
|
2294
|
+
await client.execute(
|
|
2295
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2296
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2297
|
+
WHERE content_hash IS NOT NULL`
|
|
2298
|
+
);
|
|
2299
|
+
} catch {
|
|
2300
|
+
}
|
|
2293
2301
|
await client.executeMultiple(`
|
|
2294
2302
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2295
2303
|
id TEXT PRIMARY KEY,
|
|
@@ -3035,6 +3043,197 @@ var init_state_bus = __esm({
|
|
|
3035
3043
|
}
|
|
3036
3044
|
});
|
|
3037
3045
|
|
|
3046
|
+
// src/lib/memory-write-governor.ts
|
|
3047
|
+
import { createHash as createHash2 } from "crypto";
|
|
3048
|
+
function normalizeMemoryText(text) {
|
|
3049
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3050
|
+
}
|
|
3051
|
+
function classifyMemoryType(input2) {
|
|
3052
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3053
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3054
|
+
const text = input2.raw_text.toLowerCase();
|
|
3055
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3056
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3057
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3058
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3059
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3060
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3061
|
+
return "raw";
|
|
3062
|
+
}
|
|
3063
|
+
function shouldDropMemory(text) {
|
|
3064
|
+
const normalized = normalizeMemoryText(text);
|
|
3065
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3066
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3067
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3068
|
+
}
|
|
3069
|
+
return { drop: false };
|
|
3070
|
+
}
|
|
3071
|
+
function shouldSkipEmbedding(input2) {
|
|
3072
|
+
const type = classifyMemoryType(input2);
|
|
3073
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3074
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3075
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3076
|
+
return false;
|
|
3077
|
+
}
|
|
3078
|
+
function hashMemoryContent(text) {
|
|
3079
|
+
return createHash2("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3080
|
+
}
|
|
3081
|
+
function scopedDedupArgs(input2) {
|
|
3082
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3083
|
+
}
|
|
3084
|
+
function governMemoryRecord(record) {
|
|
3085
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3086
|
+
const memoryType = classifyMemoryType({
|
|
3087
|
+
raw_text: normalized,
|
|
3088
|
+
agent_id: record.agent_id,
|
|
3089
|
+
project_name: record.project_name,
|
|
3090
|
+
tool_name: record.tool_name,
|
|
3091
|
+
memory_type: record.memory_type
|
|
3092
|
+
});
|
|
3093
|
+
const drop = shouldDropMemory(normalized);
|
|
3094
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3095
|
+
raw_text: normalized,
|
|
3096
|
+
agent_id: record.agent_id,
|
|
3097
|
+
project_name: record.project_name,
|
|
3098
|
+
tool_name: record.tool_name,
|
|
3099
|
+
memory_type: memoryType
|
|
3100
|
+
});
|
|
3101
|
+
return {
|
|
3102
|
+
record: {
|
|
3103
|
+
...record,
|
|
3104
|
+
raw_text: normalized,
|
|
3105
|
+
memory_type: memoryType,
|
|
3106
|
+
vector: skipEmbedding ? null : record.vector
|
|
3107
|
+
},
|
|
3108
|
+
contentHash: hashMemoryContent(normalized),
|
|
3109
|
+
shouldDrop: drop.drop,
|
|
3110
|
+
dropReason: drop.reason,
|
|
3111
|
+
skipEmbedding,
|
|
3112
|
+
hygiene: {
|
|
3113
|
+
dedup: true,
|
|
3114
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3115
|
+
}
|
|
3116
|
+
};
|
|
3117
|
+
}
|
|
3118
|
+
async function findScopedDuplicate(input2) {
|
|
3119
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3120
|
+
const client = getClient2();
|
|
3121
|
+
const args = scopedDedupArgs(input2);
|
|
3122
|
+
let sql = `SELECT id FROM memories
|
|
3123
|
+
WHERE content_hash = ?
|
|
3124
|
+
AND agent_id = ?
|
|
3125
|
+
AND project_name = ?
|
|
3126
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3127
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3128
|
+
if (input2.excludeId) {
|
|
3129
|
+
sql += " AND id != ?";
|
|
3130
|
+
args.push(input2.excludeId);
|
|
3131
|
+
}
|
|
3132
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3133
|
+
const result = await client.execute({ sql, args });
|
|
3134
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3135
|
+
}
|
|
3136
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3137
|
+
try {
|
|
3138
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3139
|
+
const client = getClient2();
|
|
3140
|
+
const current = await client.execute({
|
|
3141
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3142
|
+
importance, timestamp
|
|
3143
|
+
FROM memories
|
|
3144
|
+
WHERE id = ?
|
|
3145
|
+
LIMIT 1`,
|
|
3146
|
+
args: [memoryId]
|
|
3147
|
+
});
|
|
3148
|
+
const row = current.rows[0];
|
|
3149
|
+
if (!row) return;
|
|
3150
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3151
|
+
const contentHash2 = row.content_hash ? String(row.content_hash) : null;
|
|
3152
|
+
const agentId = String(row.agent_id);
|
|
3153
|
+
const projectName = String(row.project_name);
|
|
3154
|
+
if (contentHash2) {
|
|
3155
|
+
await client.execute({
|
|
3156
|
+
sql: `UPDATE memories
|
|
3157
|
+
SET status = 'deleted',
|
|
3158
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3159
|
+
WHERE id != ?
|
|
3160
|
+
AND content_hash = ?
|
|
3161
|
+
AND agent_id = ?
|
|
3162
|
+
AND project_name = ?
|
|
3163
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3164
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3165
|
+
args: [memoryId, contentHash2, agentId, projectName, memoryType]
|
|
3166
|
+
});
|
|
3167
|
+
}
|
|
3168
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3169
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3170
|
+
const old = await client.execute({
|
|
3171
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3172
|
+
args: [supersedesId]
|
|
3173
|
+
});
|
|
3174
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3175
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3176
|
+
await client.batch([
|
|
3177
|
+
{
|
|
3178
|
+
sql: `UPDATE memories
|
|
3179
|
+
SET status = 'archived',
|
|
3180
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3181
|
+
WHERE id = ?`,
|
|
3182
|
+
args: [supersedesId]
|
|
3183
|
+
},
|
|
3184
|
+
{
|
|
3185
|
+
sql: `UPDATE memories
|
|
3186
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3187
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3188
|
+
WHERE id = ?`,
|
|
3189
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3190
|
+
}
|
|
3191
|
+
], "write");
|
|
3192
|
+
}
|
|
3193
|
+
} catch (err) {
|
|
3194
|
+
process.stderr.write(
|
|
3195
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3196
|
+
`
|
|
3197
|
+
);
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3201
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3202
|
+
if (memoryIds.length === 0) return;
|
|
3203
|
+
const run = () => {
|
|
3204
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3205
|
+
};
|
|
3206
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3207
|
+
else setTimeout(run, 0);
|
|
3208
|
+
}
|
|
3209
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3210
|
+
var init_memory_write_governor = __esm({
|
|
3211
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3212
|
+
"use strict";
|
|
3213
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3214
|
+
"decision",
|
|
3215
|
+
"adr",
|
|
3216
|
+
"behavior",
|
|
3217
|
+
"procedure"
|
|
3218
|
+
]);
|
|
3219
|
+
NOISE_DROP_PATTERNS = [
|
|
3220
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3221
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3222
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3223
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3224
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3225
|
+
];
|
|
3226
|
+
SKIP_EMBED_PATTERNS = [
|
|
3227
|
+
/tmux capture-pane\b/i,
|
|
3228
|
+
/docker ps\b/i,
|
|
3229
|
+
/docker images\b/i,
|
|
3230
|
+
/git status\b/i,
|
|
3231
|
+
/grep .*node_modules/i,
|
|
3232
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3233
|
+
];
|
|
3234
|
+
}
|
|
3235
|
+
});
|
|
3236
|
+
|
|
3038
3237
|
// src/lib/shard-manager.ts
|
|
3039
3238
|
var shard_manager_exports = {};
|
|
3040
3239
|
__export(shard_manager_exports, {
|
|
@@ -3199,7 +3398,8 @@ async function ensureShardSchema(client) {
|
|
|
3199
3398
|
}
|
|
3200
3399
|
for (const idx of [
|
|
3201
3400
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3202
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3401
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3402
|
+
"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"
|
|
3203
3403
|
]) {
|
|
3204
3404
|
try {
|
|
3205
3405
|
await client.execute(idx);
|
|
@@ -3393,7 +3593,7 @@ var init_platform_procedures = __esm({
|
|
|
3393
3593
|
title: "Chain of command \u2014 who talks to whom",
|
|
3394
3594
|
domain: "workflow",
|
|
3395
3595
|
priority: "p0",
|
|
3396
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3596
|
+
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."
|
|
3397
3597
|
},
|
|
3398
3598
|
{
|
|
3399
3599
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3624,7 +3824,6 @@ __export(store_exports, {
|
|
|
3624
3824
|
vectorToBlob: () => vectorToBlob,
|
|
3625
3825
|
writeMemory: () => writeMemory
|
|
3626
3826
|
});
|
|
3627
|
-
import { createHash as createHash2 } from "crypto";
|
|
3628
3827
|
function isBusyError2(err) {
|
|
3629
3828
|
if (err instanceof Error) {
|
|
3630
3829
|
const msg = err.message.toLowerCase();
|
|
@@ -3738,17 +3937,24 @@ async function writeMemory(record) {
|
|
|
3738
3937
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3739
3938
|
);
|
|
3740
3939
|
}
|
|
3741
|
-
const
|
|
3742
|
-
if (
|
|
3940
|
+
const governed = governMemoryRecord(record);
|
|
3941
|
+
if (governed.shouldDrop) return;
|
|
3942
|
+
record = governed.record;
|
|
3943
|
+
const contentHash2 = governed.contentHash;
|
|
3944
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3945
|
+
if (_pendingRecords.some(
|
|
3946
|
+
(r) => r.content_hash === contentHash2 && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3947
|
+
)) {
|
|
3743
3948
|
return;
|
|
3744
3949
|
}
|
|
3745
3950
|
try {
|
|
3746
|
-
const
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3951
|
+
const existing = await findScopedDuplicate({
|
|
3952
|
+
contentHash: contentHash2,
|
|
3953
|
+
agentId: record.agent_id,
|
|
3954
|
+
projectName: record.project_name,
|
|
3955
|
+
memoryType
|
|
3750
3956
|
});
|
|
3751
|
-
if (existing
|
|
3957
|
+
if (existing) return;
|
|
3752
3958
|
} catch {
|
|
3753
3959
|
}
|
|
3754
3960
|
const dbRow = {
|
|
@@ -3779,7 +3985,7 @@ async function writeMemory(record) {
|
|
|
3779
3985
|
tier: record.tier ?? classifyTier(record),
|
|
3780
3986
|
supersedes_id: record.supersedes_id ?? null,
|
|
3781
3987
|
draft: record.draft ? 1 : 0,
|
|
3782
|
-
memory_type:
|
|
3988
|
+
memory_type: memoryType,
|
|
3783
3989
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3784
3990
|
content_hash: contentHash2,
|
|
3785
3991
|
intent: record.intent ?? null,
|
|
@@ -3938,6 +4144,7 @@ async function flushBatch() {
|
|
|
3938
4144
|
const globalClient = getClient();
|
|
3939
4145
|
const globalStmts = batch.map(buildStmt);
|
|
3940
4146
|
await globalClient.batch(globalStmts, "write");
|
|
4147
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3941
4148
|
_pendingRecords.splice(0, batch.length);
|
|
3942
4149
|
try {
|
|
3943
4150
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4058,7 +4265,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4058
4265
|
sql += ` AND timestamp >= ?`;
|
|
4059
4266
|
args.push(options.since);
|
|
4060
4267
|
}
|
|
4061
|
-
if (options?.
|
|
4268
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4269
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4270
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4271
|
+
args.push(...uniqueTypes);
|
|
4272
|
+
} else if (options?.memoryType) {
|
|
4062
4273
|
sql += ` AND memory_type = ?`;
|
|
4063
4274
|
args.push(options.memoryType);
|
|
4064
4275
|
}
|
|
@@ -4196,6 +4407,7 @@ var init_store = __esm({
|
|
|
4196
4407
|
init_keychain();
|
|
4197
4408
|
init_config();
|
|
4198
4409
|
init_state_bus();
|
|
4410
|
+
init_memory_write_governor();
|
|
4199
4411
|
INIT_MAX_RETRIES = 3;
|
|
4200
4412
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4201
4413
|
_pendingRecords = [];
|
|
@@ -2290,6 +2290,14 @@ async function ensureSchema() {
|
|
|
2290
2290
|
);
|
|
2291
2291
|
} catch {
|
|
2292
2292
|
}
|
|
2293
|
+
try {
|
|
2294
|
+
await client.execute(
|
|
2295
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2296
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2297
|
+
WHERE content_hash IS NOT NULL`
|
|
2298
|
+
);
|
|
2299
|
+
} catch {
|
|
2300
|
+
}
|
|
2293
2301
|
await client.executeMultiple(`
|
|
2294
2302
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2295
2303
|
id TEXT PRIMARY KEY,
|
|
@@ -3035,6 +3043,197 @@ var init_state_bus = __esm({
|
|
|
3035
3043
|
}
|
|
3036
3044
|
});
|
|
3037
3045
|
|
|
3046
|
+
// src/lib/memory-write-governor.ts
|
|
3047
|
+
import { createHash } from "crypto";
|
|
3048
|
+
function normalizeMemoryText(text) {
|
|
3049
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3050
|
+
}
|
|
3051
|
+
function classifyMemoryType(input2) {
|
|
3052
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3053
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3054
|
+
const text = input2.raw_text.toLowerCase();
|
|
3055
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3056
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3057
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3058
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3059
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3060
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3061
|
+
return "raw";
|
|
3062
|
+
}
|
|
3063
|
+
function shouldDropMemory(text) {
|
|
3064
|
+
const normalized = normalizeMemoryText(text);
|
|
3065
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3066
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3067
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3068
|
+
}
|
|
3069
|
+
return { drop: false };
|
|
3070
|
+
}
|
|
3071
|
+
function shouldSkipEmbedding(input2) {
|
|
3072
|
+
const type = classifyMemoryType(input2);
|
|
3073
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3074
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3075
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3076
|
+
return false;
|
|
3077
|
+
}
|
|
3078
|
+
function hashMemoryContent(text) {
|
|
3079
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3080
|
+
}
|
|
3081
|
+
function scopedDedupArgs(input2) {
|
|
3082
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3083
|
+
}
|
|
3084
|
+
function governMemoryRecord(record) {
|
|
3085
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3086
|
+
const memoryType = classifyMemoryType({
|
|
3087
|
+
raw_text: normalized,
|
|
3088
|
+
agent_id: record.agent_id,
|
|
3089
|
+
project_name: record.project_name,
|
|
3090
|
+
tool_name: record.tool_name,
|
|
3091
|
+
memory_type: record.memory_type
|
|
3092
|
+
});
|
|
3093
|
+
const drop = shouldDropMemory(normalized);
|
|
3094
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3095
|
+
raw_text: normalized,
|
|
3096
|
+
agent_id: record.agent_id,
|
|
3097
|
+
project_name: record.project_name,
|
|
3098
|
+
tool_name: record.tool_name,
|
|
3099
|
+
memory_type: memoryType
|
|
3100
|
+
});
|
|
3101
|
+
return {
|
|
3102
|
+
record: {
|
|
3103
|
+
...record,
|
|
3104
|
+
raw_text: normalized,
|
|
3105
|
+
memory_type: memoryType,
|
|
3106
|
+
vector: skipEmbedding ? null : record.vector
|
|
3107
|
+
},
|
|
3108
|
+
contentHash: hashMemoryContent(normalized),
|
|
3109
|
+
shouldDrop: drop.drop,
|
|
3110
|
+
dropReason: drop.reason,
|
|
3111
|
+
skipEmbedding,
|
|
3112
|
+
hygiene: {
|
|
3113
|
+
dedup: true,
|
|
3114
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3115
|
+
}
|
|
3116
|
+
};
|
|
3117
|
+
}
|
|
3118
|
+
async function findScopedDuplicate(input2) {
|
|
3119
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3120
|
+
const client = getClient2();
|
|
3121
|
+
const args = scopedDedupArgs(input2);
|
|
3122
|
+
let sql = `SELECT id FROM memories
|
|
3123
|
+
WHERE content_hash = ?
|
|
3124
|
+
AND agent_id = ?
|
|
3125
|
+
AND project_name = ?
|
|
3126
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3127
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3128
|
+
if (input2.excludeId) {
|
|
3129
|
+
sql += " AND id != ?";
|
|
3130
|
+
args.push(input2.excludeId);
|
|
3131
|
+
}
|
|
3132
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3133
|
+
const result = await client.execute({ sql, args });
|
|
3134
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3135
|
+
}
|
|
3136
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3137
|
+
try {
|
|
3138
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3139
|
+
const client = getClient2();
|
|
3140
|
+
const current = await client.execute({
|
|
3141
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3142
|
+
importance, timestamp
|
|
3143
|
+
FROM memories
|
|
3144
|
+
WHERE id = ?
|
|
3145
|
+
LIMIT 1`,
|
|
3146
|
+
args: [memoryId]
|
|
3147
|
+
});
|
|
3148
|
+
const row = current.rows[0];
|
|
3149
|
+
if (!row) return;
|
|
3150
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3151
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3152
|
+
const agentId = String(row.agent_id);
|
|
3153
|
+
const projectName = String(row.project_name);
|
|
3154
|
+
if (contentHash) {
|
|
3155
|
+
await client.execute({
|
|
3156
|
+
sql: `UPDATE memories
|
|
3157
|
+
SET status = 'deleted',
|
|
3158
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3159
|
+
WHERE id != ?
|
|
3160
|
+
AND content_hash = ?
|
|
3161
|
+
AND agent_id = ?
|
|
3162
|
+
AND project_name = ?
|
|
3163
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3164
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3165
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3166
|
+
});
|
|
3167
|
+
}
|
|
3168
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3169
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3170
|
+
const old = await client.execute({
|
|
3171
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3172
|
+
args: [supersedesId]
|
|
3173
|
+
});
|
|
3174
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3175
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3176
|
+
await client.batch([
|
|
3177
|
+
{
|
|
3178
|
+
sql: `UPDATE memories
|
|
3179
|
+
SET status = 'archived',
|
|
3180
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3181
|
+
WHERE id = ?`,
|
|
3182
|
+
args: [supersedesId]
|
|
3183
|
+
},
|
|
3184
|
+
{
|
|
3185
|
+
sql: `UPDATE memories
|
|
3186
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3187
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3188
|
+
WHERE id = ?`,
|
|
3189
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3190
|
+
}
|
|
3191
|
+
], "write");
|
|
3192
|
+
}
|
|
3193
|
+
} catch (err) {
|
|
3194
|
+
process.stderr.write(
|
|
3195
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3196
|
+
`
|
|
3197
|
+
);
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3201
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3202
|
+
if (memoryIds.length === 0) return;
|
|
3203
|
+
const run = () => {
|
|
3204
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3205
|
+
};
|
|
3206
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3207
|
+
else setTimeout(run, 0);
|
|
3208
|
+
}
|
|
3209
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3210
|
+
var init_memory_write_governor = __esm({
|
|
3211
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3212
|
+
"use strict";
|
|
3213
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3214
|
+
"decision",
|
|
3215
|
+
"adr",
|
|
3216
|
+
"behavior",
|
|
3217
|
+
"procedure"
|
|
3218
|
+
]);
|
|
3219
|
+
NOISE_DROP_PATTERNS = [
|
|
3220
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3221
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3222
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3223
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3224
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3225
|
+
];
|
|
3226
|
+
SKIP_EMBED_PATTERNS = [
|
|
3227
|
+
/tmux capture-pane\b/i,
|
|
3228
|
+
/docker ps\b/i,
|
|
3229
|
+
/docker images\b/i,
|
|
3230
|
+
/git status\b/i,
|
|
3231
|
+
/grep .*node_modules/i,
|
|
3232
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3233
|
+
];
|
|
3234
|
+
}
|
|
3235
|
+
});
|
|
3236
|
+
|
|
3038
3237
|
// src/lib/shard-manager.ts
|
|
3039
3238
|
var shard_manager_exports = {};
|
|
3040
3239
|
__export(shard_manager_exports, {
|
|
@@ -3199,7 +3398,8 @@ async function ensureShardSchema(client) {
|
|
|
3199
3398
|
}
|
|
3200
3399
|
for (const idx of [
|
|
3201
3400
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3202
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3401
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3402
|
+
"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"
|
|
3203
3403
|
]) {
|
|
3204
3404
|
try {
|
|
3205
3405
|
await client.execute(idx);
|
|
@@ -3393,7 +3593,7 @@ var init_platform_procedures = __esm({
|
|
|
3393
3593
|
title: "Chain of command \u2014 who talks to whom",
|
|
3394
3594
|
domain: "workflow",
|
|
3395
3595
|
priority: "p0",
|
|
3396
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3596
|
+
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."
|
|
3397
3597
|
},
|
|
3398
3598
|
{
|
|
3399
3599
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3624,7 +3824,6 @@ __export(store_exports, {
|
|
|
3624
3824
|
vectorToBlob: () => vectorToBlob,
|
|
3625
3825
|
writeMemory: () => writeMemory
|
|
3626
3826
|
});
|
|
3627
|
-
import { createHash } from "crypto";
|
|
3628
3827
|
function isBusyError2(err) {
|
|
3629
3828
|
if (err instanceof Error) {
|
|
3630
3829
|
const msg = err.message.toLowerCase();
|
|
@@ -3738,17 +3937,24 @@ async function writeMemory(record) {
|
|
|
3738
3937
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3739
3938
|
);
|
|
3740
3939
|
}
|
|
3741
|
-
const
|
|
3742
|
-
if (
|
|
3940
|
+
const governed = governMemoryRecord(record);
|
|
3941
|
+
if (governed.shouldDrop) return;
|
|
3942
|
+
record = governed.record;
|
|
3943
|
+
const contentHash = governed.contentHash;
|
|
3944
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3945
|
+
if (_pendingRecords.some(
|
|
3946
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3947
|
+
)) {
|
|
3743
3948
|
return;
|
|
3744
3949
|
}
|
|
3745
3950
|
try {
|
|
3746
|
-
const
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3951
|
+
const existing = await findScopedDuplicate({
|
|
3952
|
+
contentHash,
|
|
3953
|
+
agentId: record.agent_id,
|
|
3954
|
+
projectName: record.project_name,
|
|
3955
|
+
memoryType
|
|
3750
3956
|
});
|
|
3751
|
-
if (existing
|
|
3957
|
+
if (existing) return;
|
|
3752
3958
|
} catch {
|
|
3753
3959
|
}
|
|
3754
3960
|
const dbRow = {
|
|
@@ -3779,7 +3985,7 @@ async function writeMemory(record) {
|
|
|
3779
3985
|
tier: record.tier ?? classifyTier(record),
|
|
3780
3986
|
supersedes_id: record.supersedes_id ?? null,
|
|
3781
3987
|
draft: record.draft ? 1 : 0,
|
|
3782
|
-
memory_type:
|
|
3988
|
+
memory_type: memoryType,
|
|
3783
3989
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3784
3990
|
content_hash: contentHash,
|
|
3785
3991
|
intent: record.intent ?? null,
|
|
@@ -3938,6 +4144,7 @@ async function flushBatch() {
|
|
|
3938
4144
|
const globalClient = getClient();
|
|
3939
4145
|
const globalStmts = batch.map(buildStmt);
|
|
3940
4146
|
await globalClient.batch(globalStmts, "write");
|
|
4147
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3941
4148
|
_pendingRecords.splice(0, batch.length);
|
|
3942
4149
|
try {
|
|
3943
4150
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4058,7 +4265,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4058
4265
|
sql += ` AND timestamp >= ?`;
|
|
4059
4266
|
args.push(options.since);
|
|
4060
4267
|
}
|
|
4061
|
-
if (options?.
|
|
4268
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4269
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4270
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4271
|
+
args.push(...uniqueTypes);
|
|
4272
|
+
} else if (options?.memoryType) {
|
|
4062
4273
|
sql += ` AND memory_type = ?`;
|
|
4063
4274
|
args.push(options.memoryType);
|
|
4064
4275
|
}
|
|
@@ -4196,6 +4407,7 @@ var init_store = __esm({
|
|
|
4196
4407
|
init_keychain();
|
|
4197
4408
|
init_config();
|
|
4198
4409
|
init_state_bus();
|
|
4410
|
+
init_memory_write_governor();
|
|
4199
4411
|
INIT_MAX_RETRIES = 3;
|
|
4200
4412
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4201
4413
|
_pendingRecords = [];
|