@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
|
@@ -2279,6 +2279,14 @@ async function ensureSchema() {
|
|
|
2279
2279
|
);
|
|
2280
2280
|
} catch {
|
|
2281
2281
|
}
|
|
2282
|
+
try {
|
|
2283
|
+
await client.execute(
|
|
2284
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2285
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2286
|
+
WHERE content_hash IS NOT NULL`
|
|
2287
|
+
);
|
|
2288
|
+
} catch {
|
|
2289
|
+
}
|
|
2282
2290
|
await client.executeMultiple(`
|
|
2283
2291
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2284
2292
|
id TEXT PRIMARY KEY,
|
|
@@ -3024,6 +3032,197 @@ var init_state_bus = __esm({
|
|
|
3024
3032
|
}
|
|
3025
3033
|
});
|
|
3026
3034
|
|
|
3035
|
+
// src/lib/memory-write-governor.ts
|
|
3036
|
+
import { createHash } from "crypto";
|
|
3037
|
+
function normalizeMemoryText(text) {
|
|
3038
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3039
|
+
}
|
|
3040
|
+
function classifyMemoryType(input2) {
|
|
3041
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3042
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3043
|
+
const text = input2.raw_text.toLowerCase();
|
|
3044
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3045
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3046
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3047
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3048
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3049
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3050
|
+
return "raw";
|
|
3051
|
+
}
|
|
3052
|
+
function shouldDropMemory(text) {
|
|
3053
|
+
const normalized = normalizeMemoryText(text);
|
|
3054
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3055
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3056
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3057
|
+
}
|
|
3058
|
+
return { drop: false };
|
|
3059
|
+
}
|
|
3060
|
+
function shouldSkipEmbedding(input2) {
|
|
3061
|
+
const type = classifyMemoryType(input2);
|
|
3062
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3063
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3064
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3065
|
+
return false;
|
|
3066
|
+
}
|
|
3067
|
+
function hashMemoryContent(text) {
|
|
3068
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3069
|
+
}
|
|
3070
|
+
function scopedDedupArgs(input2) {
|
|
3071
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3072
|
+
}
|
|
3073
|
+
function governMemoryRecord(record) {
|
|
3074
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3075
|
+
const memoryType = classifyMemoryType({
|
|
3076
|
+
raw_text: normalized,
|
|
3077
|
+
agent_id: record.agent_id,
|
|
3078
|
+
project_name: record.project_name,
|
|
3079
|
+
tool_name: record.tool_name,
|
|
3080
|
+
memory_type: record.memory_type
|
|
3081
|
+
});
|
|
3082
|
+
const drop = shouldDropMemory(normalized);
|
|
3083
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3084
|
+
raw_text: normalized,
|
|
3085
|
+
agent_id: record.agent_id,
|
|
3086
|
+
project_name: record.project_name,
|
|
3087
|
+
tool_name: record.tool_name,
|
|
3088
|
+
memory_type: memoryType
|
|
3089
|
+
});
|
|
3090
|
+
return {
|
|
3091
|
+
record: {
|
|
3092
|
+
...record,
|
|
3093
|
+
raw_text: normalized,
|
|
3094
|
+
memory_type: memoryType,
|
|
3095
|
+
vector: skipEmbedding ? null : record.vector
|
|
3096
|
+
},
|
|
3097
|
+
contentHash: hashMemoryContent(normalized),
|
|
3098
|
+
shouldDrop: drop.drop,
|
|
3099
|
+
dropReason: drop.reason,
|
|
3100
|
+
skipEmbedding,
|
|
3101
|
+
hygiene: {
|
|
3102
|
+
dedup: true,
|
|
3103
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3104
|
+
}
|
|
3105
|
+
};
|
|
3106
|
+
}
|
|
3107
|
+
async function findScopedDuplicate(input2) {
|
|
3108
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3109
|
+
const client = getClient2();
|
|
3110
|
+
const args = scopedDedupArgs(input2);
|
|
3111
|
+
let sql = `SELECT id FROM memories
|
|
3112
|
+
WHERE content_hash = ?
|
|
3113
|
+
AND agent_id = ?
|
|
3114
|
+
AND project_name = ?
|
|
3115
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3116
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3117
|
+
if (input2.excludeId) {
|
|
3118
|
+
sql += " AND id != ?";
|
|
3119
|
+
args.push(input2.excludeId);
|
|
3120
|
+
}
|
|
3121
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3122
|
+
const result = await client.execute({ sql, args });
|
|
3123
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3124
|
+
}
|
|
3125
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3126
|
+
try {
|
|
3127
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3128
|
+
const client = getClient2();
|
|
3129
|
+
const current = await client.execute({
|
|
3130
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3131
|
+
importance, timestamp
|
|
3132
|
+
FROM memories
|
|
3133
|
+
WHERE id = ?
|
|
3134
|
+
LIMIT 1`,
|
|
3135
|
+
args: [memoryId]
|
|
3136
|
+
});
|
|
3137
|
+
const row = current.rows[0];
|
|
3138
|
+
if (!row) return;
|
|
3139
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3140
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3141
|
+
const agentId = String(row.agent_id);
|
|
3142
|
+
const projectName = String(row.project_name);
|
|
3143
|
+
if (contentHash) {
|
|
3144
|
+
await client.execute({
|
|
3145
|
+
sql: `UPDATE memories
|
|
3146
|
+
SET status = 'deleted',
|
|
3147
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3148
|
+
WHERE id != ?
|
|
3149
|
+
AND content_hash = ?
|
|
3150
|
+
AND agent_id = ?
|
|
3151
|
+
AND project_name = ?
|
|
3152
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3153
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3154
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3155
|
+
});
|
|
3156
|
+
}
|
|
3157
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3158
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3159
|
+
const old = await client.execute({
|
|
3160
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3161
|
+
args: [supersedesId]
|
|
3162
|
+
});
|
|
3163
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3164
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3165
|
+
await client.batch([
|
|
3166
|
+
{
|
|
3167
|
+
sql: `UPDATE memories
|
|
3168
|
+
SET status = 'archived',
|
|
3169
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3170
|
+
WHERE id = ?`,
|
|
3171
|
+
args: [supersedesId]
|
|
3172
|
+
},
|
|
3173
|
+
{
|
|
3174
|
+
sql: `UPDATE memories
|
|
3175
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3176
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3177
|
+
WHERE id = ?`,
|
|
3178
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3179
|
+
}
|
|
3180
|
+
], "write");
|
|
3181
|
+
}
|
|
3182
|
+
} catch (err) {
|
|
3183
|
+
process.stderr.write(
|
|
3184
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3185
|
+
`
|
|
3186
|
+
);
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3190
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3191
|
+
if (memoryIds.length === 0) return;
|
|
3192
|
+
const run = () => {
|
|
3193
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3194
|
+
};
|
|
3195
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3196
|
+
else setTimeout(run, 0);
|
|
3197
|
+
}
|
|
3198
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3199
|
+
var init_memory_write_governor = __esm({
|
|
3200
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3201
|
+
"use strict";
|
|
3202
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3203
|
+
"decision",
|
|
3204
|
+
"adr",
|
|
3205
|
+
"behavior",
|
|
3206
|
+
"procedure"
|
|
3207
|
+
]);
|
|
3208
|
+
NOISE_DROP_PATTERNS = [
|
|
3209
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3210
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3211
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3212
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3213
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3214
|
+
];
|
|
3215
|
+
SKIP_EMBED_PATTERNS = [
|
|
3216
|
+
/tmux capture-pane\b/i,
|
|
3217
|
+
/docker ps\b/i,
|
|
3218
|
+
/docker images\b/i,
|
|
3219
|
+
/git status\b/i,
|
|
3220
|
+
/grep .*node_modules/i,
|
|
3221
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3222
|
+
];
|
|
3223
|
+
}
|
|
3224
|
+
});
|
|
3225
|
+
|
|
3027
3226
|
// src/lib/shard-manager.ts
|
|
3028
3227
|
var shard_manager_exports = {};
|
|
3029
3228
|
__export(shard_manager_exports, {
|
|
@@ -3188,7 +3387,8 @@ async function ensureShardSchema(client) {
|
|
|
3188
3387
|
}
|
|
3189
3388
|
for (const idx of [
|
|
3190
3389
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3191
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3390
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3391
|
+
"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"
|
|
3192
3392
|
]) {
|
|
3193
3393
|
try {
|
|
3194
3394
|
await client.execute(idx);
|
|
@@ -3382,7 +3582,7 @@ var init_platform_procedures = __esm({
|
|
|
3382
3582
|
title: "Chain of command \u2014 who talks to whom",
|
|
3383
3583
|
domain: "workflow",
|
|
3384
3584
|
priority: "p0",
|
|
3385
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3585
|
+
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."
|
|
3386
3586
|
},
|
|
3387
3587
|
{
|
|
3388
3588
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3613,7 +3813,6 @@ __export(store_exports, {
|
|
|
3613
3813
|
vectorToBlob: () => vectorToBlob,
|
|
3614
3814
|
writeMemory: () => writeMemory
|
|
3615
3815
|
});
|
|
3616
|
-
import { createHash } from "crypto";
|
|
3617
3816
|
function isBusyError2(err) {
|
|
3618
3817
|
if (err instanceof Error) {
|
|
3619
3818
|
const msg = err.message.toLowerCase();
|
|
@@ -3727,17 +3926,24 @@ async function writeMemory(record) {
|
|
|
3727
3926
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3728
3927
|
);
|
|
3729
3928
|
}
|
|
3730
|
-
const
|
|
3731
|
-
if (
|
|
3929
|
+
const governed = governMemoryRecord(record);
|
|
3930
|
+
if (governed.shouldDrop) return;
|
|
3931
|
+
record = governed.record;
|
|
3932
|
+
const contentHash = governed.contentHash;
|
|
3933
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3934
|
+
if (_pendingRecords.some(
|
|
3935
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3936
|
+
)) {
|
|
3732
3937
|
return;
|
|
3733
3938
|
}
|
|
3734
3939
|
try {
|
|
3735
|
-
const
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3940
|
+
const existing = await findScopedDuplicate({
|
|
3941
|
+
contentHash,
|
|
3942
|
+
agentId: record.agent_id,
|
|
3943
|
+
projectName: record.project_name,
|
|
3944
|
+
memoryType
|
|
3739
3945
|
});
|
|
3740
|
-
if (existing
|
|
3946
|
+
if (existing) return;
|
|
3741
3947
|
} catch {
|
|
3742
3948
|
}
|
|
3743
3949
|
const dbRow = {
|
|
@@ -3768,7 +3974,7 @@ async function writeMemory(record) {
|
|
|
3768
3974
|
tier: record.tier ?? classifyTier(record),
|
|
3769
3975
|
supersedes_id: record.supersedes_id ?? null,
|
|
3770
3976
|
draft: record.draft ? 1 : 0,
|
|
3771
|
-
memory_type:
|
|
3977
|
+
memory_type: memoryType,
|
|
3772
3978
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3773
3979
|
content_hash: contentHash,
|
|
3774
3980
|
intent: record.intent ?? null,
|
|
@@ -3927,6 +4133,7 @@ async function flushBatch() {
|
|
|
3927
4133
|
const globalClient = getClient();
|
|
3928
4134
|
const globalStmts = batch.map(buildStmt);
|
|
3929
4135
|
await globalClient.batch(globalStmts, "write");
|
|
4136
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3930
4137
|
_pendingRecords.splice(0, batch.length);
|
|
3931
4138
|
try {
|
|
3932
4139
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4047,7 +4254,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4047
4254
|
sql += ` AND timestamp >= ?`;
|
|
4048
4255
|
args.push(options.since);
|
|
4049
4256
|
}
|
|
4050
|
-
if (options?.
|
|
4257
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4258
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4259
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4260
|
+
args.push(...uniqueTypes);
|
|
4261
|
+
} else if (options?.memoryType) {
|
|
4051
4262
|
sql += ` AND memory_type = ?`;
|
|
4052
4263
|
args.push(options.memoryType);
|
|
4053
4264
|
}
|
|
@@ -4185,6 +4396,7 @@ var init_store = __esm({
|
|
|
4185
4396
|
init_keychain();
|
|
4186
4397
|
init_config();
|
|
4187
4398
|
init_state_bus();
|
|
4399
|
+
init_memory_write_governor();
|
|
4188
4400
|
INIT_MAX_RETRIES = 3;
|
|
4189
4401
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4190
4402
|
_pendingRecords = [];
|
|
@@ -5448,6 +5660,17 @@ init_store();
|
|
|
5448
5660
|
init_store();
|
|
5449
5661
|
init_database();
|
|
5450
5662
|
var RRF_K = 60;
|
|
5663
|
+
function appendMemoryTypeFilter(sql, args, column, options) {
|
|
5664
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
5665
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
5666
|
+
sql += ` AND ${column} IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
5667
|
+
args.push(...uniqueTypes);
|
|
5668
|
+
} else if (options?.memoryType) {
|
|
5669
|
+
sql += ` AND ${column} = ?`;
|
|
5670
|
+
args.push(options.memoryType);
|
|
5671
|
+
}
|
|
5672
|
+
return sql;
|
|
5673
|
+
}
|
|
5451
5674
|
async function hybridSearch(queryText, agentId, options) {
|
|
5452
5675
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
5453
5676
|
const config = await loadConfig2();
|
|
@@ -5676,6 +5899,7 @@ async function estimateCardinality(agentId, options) {
|
|
|
5676
5899
|
sql += ` AND timestamp >= ?`;
|
|
5677
5900
|
args.push(options.since);
|
|
5678
5901
|
}
|
|
5902
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
5679
5903
|
try {
|
|
5680
5904
|
const result = await client.execute({ sql, args });
|
|
5681
5905
|
return Number(result.rows[0]?.cnt) || 0;
|
|
@@ -5794,10 +6018,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
5794
6018
|
sql += ` AND m.timestamp >= ?`;
|
|
5795
6019
|
args.push(options.since);
|
|
5796
6020
|
}
|
|
5797
|
-
|
|
5798
|
-
sql += ` AND m.memory_type = ?`;
|
|
5799
|
-
args.push(options.memoryType);
|
|
5800
|
-
}
|
|
6021
|
+
sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
|
|
5801
6022
|
sql += ` ORDER BY rank LIMIT ?`;
|
|
5802
6023
|
args.push(limit);
|
|
5803
6024
|
const result = await client.execute({ sql, args });
|
|
@@ -5854,9 +6075,16 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
5854
6075
|
AND timestamp >= ? AND timestamp <= ?
|
|
5855
6076
|
AND COALESCE(status, 'active') = 'active'
|
|
5856
6077
|
AND ${options?.includeRaw === false ? "COALESCE(memory_type, 'raw') != 'raw'" : "1 = 1"}
|
|
6078
|
+
${options?.memoryTypes?.length ? `AND memory_type IN (${options.memoryTypes.map(() => "?").join(",")})` : options?.memoryType ? "AND memory_type = ?" : ""}
|
|
5857
6079
|
AND COALESCE(confidence, 0.7) >= 0.3
|
|
5858
6080
|
ORDER BY timestamp DESC LIMIT ?`,
|
|
5859
|
-
args: [
|
|
6081
|
+
args: [
|
|
6082
|
+
agentId,
|
|
6083
|
+
windowStart,
|
|
6084
|
+
killedAt,
|
|
6085
|
+
...options?.memoryTypes?.length ? [...new Set(options.memoryTypes)] : options?.memoryType ? [options.memoryType] : [],
|
|
6086
|
+
boundarySlots
|
|
6087
|
+
]
|
|
5860
6088
|
});
|
|
5861
6089
|
for (const row of boundaryResult.rows) {
|
|
5862
6090
|
sessionBoundaryMemories.push(rowToMemoryRecord(row));
|
|
@@ -5902,10 +6130,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
|
|
|
5902
6130
|
sql += ` AND timestamp >= ?`;
|
|
5903
6131
|
args.push(options.since);
|
|
5904
6132
|
}
|
|
5905
|
-
|
|
5906
|
-
sql += ` AND memory_type = ?`;
|
|
5907
|
-
args.push(options.memoryType);
|
|
5908
|
-
}
|
|
6133
|
+
sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
|
|
5909
6134
|
if (textFilter) {
|
|
5910
6135
|
sql += ` AND raw_text LIKE '%' || ? || '%'`;
|
|
5911
6136
|
args.push(textFilter);
|
package/dist/hooks/ingest.js
CHANGED
|
@@ -2455,6 +2455,14 @@ async function ensureSchema() {
|
|
|
2455
2455
|
);
|
|
2456
2456
|
} catch {
|
|
2457
2457
|
}
|
|
2458
|
+
try {
|
|
2459
|
+
await client.execute(
|
|
2460
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2461
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2462
|
+
WHERE content_hash IS NOT NULL`
|
|
2463
|
+
);
|
|
2464
|
+
} catch {
|
|
2465
|
+
}
|
|
2458
2466
|
await client.executeMultiple(`
|
|
2459
2467
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2460
2468
|
id TEXT PRIMARY KEY,
|
|
@@ -3200,6 +3208,197 @@ var init_state_bus = __esm({
|
|
|
3200
3208
|
}
|
|
3201
3209
|
});
|
|
3202
3210
|
|
|
3211
|
+
// src/lib/memory-write-governor.ts
|
|
3212
|
+
import { createHash } from "crypto";
|
|
3213
|
+
function normalizeMemoryText(text) {
|
|
3214
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3215
|
+
}
|
|
3216
|
+
function classifyMemoryType(input2) {
|
|
3217
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3218
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3219
|
+
const text = input2.raw_text.toLowerCase();
|
|
3220
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3221
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3222
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3223
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3224
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3225
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3226
|
+
return "raw";
|
|
3227
|
+
}
|
|
3228
|
+
function shouldDropMemory(text) {
|
|
3229
|
+
const normalized = normalizeMemoryText(text);
|
|
3230
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3231
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3232
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3233
|
+
}
|
|
3234
|
+
return { drop: false };
|
|
3235
|
+
}
|
|
3236
|
+
function shouldSkipEmbedding(input2) {
|
|
3237
|
+
const type = classifyMemoryType(input2);
|
|
3238
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3239
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3240
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3241
|
+
return false;
|
|
3242
|
+
}
|
|
3243
|
+
function hashMemoryContent(text) {
|
|
3244
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3245
|
+
}
|
|
3246
|
+
function scopedDedupArgs(input2) {
|
|
3247
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3248
|
+
}
|
|
3249
|
+
function governMemoryRecord(record) {
|
|
3250
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3251
|
+
const memoryType = classifyMemoryType({
|
|
3252
|
+
raw_text: normalized,
|
|
3253
|
+
agent_id: record.agent_id,
|
|
3254
|
+
project_name: record.project_name,
|
|
3255
|
+
tool_name: record.tool_name,
|
|
3256
|
+
memory_type: record.memory_type
|
|
3257
|
+
});
|
|
3258
|
+
const drop = shouldDropMemory(normalized);
|
|
3259
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3260
|
+
raw_text: normalized,
|
|
3261
|
+
agent_id: record.agent_id,
|
|
3262
|
+
project_name: record.project_name,
|
|
3263
|
+
tool_name: record.tool_name,
|
|
3264
|
+
memory_type: memoryType
|
|
3265
|
+
});
|
|
3266
|
+
return {
|
|
3267
|
+
record: {
|
|
3268
|
+
...record,
|
|
3269
|
+
raw_text: normalized,
|
|
3270
|
+
memory_type: memoryType,
|
|
3271
|
+
vector: skipEmbedding ? null : record.vector
|
|
3272
|
+
},
|
|
3273
|
+
contentHash: hashMemoryContent(normalized),
|
|
3274
|
+
shouldDrop: drop.drop,
|
|
3275
|
+
dropReason: drop.reason,
|
|
3276
|
+
skipEmbedding,
|
|
3277
|
+
hygiene: {
|
|
3278
|
+
dedup: true,
|
|
3279
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3280
|
+
}
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
async function findScopedDuplicate(input2) {
|
|
3284
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3285
|
+
const client = getClient2();
|
|
3286
|
+
const args = scopedDedupArgs(input2);
|
|
3287
|
+
let sql = `SELECT id FROM memories
|
|
3288
|
+
WHERE content_hash = ?
|
|
3289
|
+
AND agent_id = ?
|
|
3290
|
+
AND project_name = ?
|
|
3291
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3292
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3293
|
+
if (input2.excludeId) {
|
|
3294
|
+
sql += " AND id != ?";
|
|
3295
|
+
args.push(input2.excludeId);
|
|
3296
|
+
}
|
|
3297
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3298
|
+
const result = await client.execute({ sql, args });
|
|
3299
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3300
|
+
}
|
|
3301
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3302
|
+
try {
|
|
3303
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3304
|
+
const client = getClient2();
|
|
3305
|
+
const current = await client.execute({
|
|
3306
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3307
|
+
importance, timestamp
|
|
3308
|
+
FROM memories
|
|
3309
|
+
WHERE id = ?
|
|
3310
|
+
LIMIT 1`,
|
|
3311
|
+
args: [memoryId]
|
|
3312
|
+
});
|
|
3313
|
+
const row = current.rows[0];
|
|
3314
|
+
if (!row) return;
|
|
3315
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3316
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3317
|
+
const agentId = String(row.agent_id);
|
|
3318
|
+
const projectName = String(row.project_name);
|
|
3319
|
+
if (contentHash) {
|
|
3320
|
+
await client.execute({
|
|
3321
|
+
sql: `UPDATE memories
|
|
3322
|
+
SET status = 'deleted',
|
|
3323
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3324
|
+
WHERE id != ?
|
|
3325
|
+
AND content_hash = ?
|
|
3326
|
+
AND agent_id = ?
|
|
3327
|
+
AND project_name = ?
|
|
3328
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3329
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3330
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3331
|
+
});
|
|
3332
|
+
}
|
|
3333
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3334
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3335
|
+
const old = await client.execute({
|
|
3336
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3337
|
+
args: [supersedesId]
|
|
3338
|
+
});
|
|
3339
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3340
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3341
|
+
await client.batch([
|
|
3342
|
+
{
|
|
3343
|
+
sql: `UPDATE memories
|
|
3344
|
+
SET status = 'archived',
|
|
3345
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3346
|
+
WHERE id = ?`,
|
|
3347
|
+
args: [supersedesId]
|
|
3348
|
+
},
|
|
3349
|
+
{
|
|
3350
|
+
sql: `UPDATE memories
|
|
3351
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3352
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3353
|
+
WHERE id = ?`,
|
|
3354
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3355
|
+
}
|
|
3356
|
+
], "write");
|
|
3357
|
+
}
|
|
3358
|
+
} catch (err) {
|
|
3359
|
+
process.stderr.write(
|
|
3360
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3361
|
+
`
|
|
3362
|
+
);
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3366
|
+
if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
|
|
3367
|
+
if (memoryIds.length === 0) return;
|
|
3368
|
+
const run = () => {
|
|
3369
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3370
|
+
};
|
|
3371
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3372
|
+
else setTimeout(run, 0);
|
|
3373
|
+
}
|
|
3374
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3375
|
+
var init_memory_write_governor = __esm({
|
|
3376
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3377
|
+
"use strict";
|
|
3378
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3379
|
+
"decision",
|
|
3380
|
+
"adr",
|
|
3381
|
+
"behavior",
|
|
3382
|
+
"procedure"
|
|
3383
|
+
]);
|
|
3384
|
+
NOISE_DROP_PATTERNS = [
|
|
3385
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3386
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3387
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3388
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3389
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3390
|
+
];
|
|
3391
|
+
SKIP_EMBED_PATTERNS = [
|
|
3392
|
+
/tmux capture-pane\b/i,
|
|
3393
|
+
/docker ps\b/i,
|
|
3394
|
+
/docker images\b/i,
|
|
3395
|
+
/git status\b/i,
|
|
3396
|
+
/grep .*node_modules/i,
|
|
3397
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3398
|
+
];
|
|
3399
|
+
}
|
|
3400
|
+
});
|
|
3401
|
+
|
|
3203
3402
|
// src/lib/shard-manager.ts
|
|
3204
3403
|
var shard_manager_exports = {};
|
|
3205
3404
|
__export(shard_manager_exports, {
|
|
@@ -3364,7 +3563,8 @@ async function ensureShardSchema(client) {
|
|
|
3364
3563
|
}
|
|
3365
3564
|
for (const idx of [
|
|
3366
3565
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3367
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3566
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3567
|
+
"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"
|
|
3368
3568
|
]) {
|
|
3369
3569
|
try {
|
|
3370
3570
|
await client.execute(idx);
|
|
@@ -3558,7 +3758,7 @@ var init_platform_procedures = __esm({
|
|
|
3558
3758
|
title: "Chain of command \u2014 who talks to whom",
|
|
3559
3759
|
domain: "workflow",
|
|
3560
3760
|
priority: "p0",
|
|
3561
|
-
content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the
|
|
3761
|
+
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."
|
|
3562
3762
|
},
|
|
3563
3763
|
{
|
|
3564
3764
|
title: "Single dispatch path \u2014 create_task only",
|
|
@@ -3789,7 +3989,6 @@ __export(store_exports, {
|
|
|
3789
3989
|
vectorToBlob: () => vectorToBlob,
|
|
3790
3990
|
writeMemory: () => writeMemory
|
|
3791
3991
|
});
|
|
3792
|
-
import { createHash } from "crypto";
|
|
3793
3992
|
function isBusyError2(err) {
|
|
3794
3993
|
if (err instanceof Error) {
|
|
3795
3994
|
const msg = err.message.toLowerCase();
|
|
@@ -3903,17 +4102,24 @@ async function writeMemory(record) {
|
|
|
3903
4102
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3904
4103
|
);
|
|
3905
4104
|
}
|
|
3906
|
-
const
|
|
3907
|
-
if (
|
|
4105
|
+
const governed = governMemoryRecord(record);
|
|
4106
|
+
if (governed.shouldDrop) return;
|
|
4107
|
+
record = governed.record;
|
|
4108
|
+
const contentHash = governed.contentHash;
|
|
4109
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4110
|
+
if (_pendingRecords.some(
|
|
4111
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4112
|
+
)) {
|
|
3908
4113
|
return;
|
|
3909
4114
|
}
|
|
3910
4115
|
try {
|
|
3911
|
-
const
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
4116
|
+
const existing = await findScopedDuplicate({
|
|
4117
|
+
contentHash,
|
|
4118
|
+
agentId: record.agent_id,
|
|
4119
|
+
projectName: record.project_name,
|
|
4120
|
+
memoryType
|
|
3915
4121
|
});
|
|
3916
|
-
if (existing
|
|
4122
|
+
if (existing) return;
|
|
3917
4123
|
} catch {
|
|
3918
4124
|
}
|
|
3919
4125
|
const dbRow = {
|
|
@@ -3944,7 +4150,7 @@ async function writeMemory(record) {
|
|
|
3944
4150
|
tier: record.tier ?? classifyTier(record),
|
|
3945
4151
|
supersedes_id: record.supersedes_id ?? null,
|
|
3946
4152
|
draft: record.draft ? 1 : 0,
|
|
3947
|
-
memory_type:
|
|
4153
|
+
memory_type: memoryType,
|
|
3948
4154
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3949
4155
|
content_hash: contentHash,
|
|
3950
4156
|
intent: record.intent ?? null,
|
|
@@ -4103,6 +4309,7 @@ async function flushBatch() {
|
|
|
4103
4309
|
const globalClient = getClient();
|
|
4104
4310
|
const globalStmts = batch.map(buildStmt);
|
|
4105
4311
|
await globalClient.batch(globalStmts, "write");
|
|
4312
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4106
4313
|
_pendingRecords.splice(0, batch.length);
|
|
4107
4314
|
try {
|
|
4108
4315
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4223,7 +4430,11 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
4223
4430
|
sql += ` AND timestamp >= ?`;
|
|
4224
4431
|
args.push(options.since);
|
|
4225
4432
|
}
|
|
4226
|
-
if (options?.
|
|
4433
|
+
if (options?.memoryTypes && options.memoryTypes.length > 0) {
|
|
4434
|
+
const uniqueTypes = [...new Set(options.memoryTypes)];
|
|
4435
|
+
sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
|
|
4436
|
+
args.push(...uniqueTypes);
|
|
4437
|
+
} else if (options?.memoryType) {
|
|
4227
4438
|
sql += ` AND memory_type = ?`;
|
|
4228
4439
|
args.push(options.memoryType);
|
|
4229
4440
|
}
|
|
@@ -4361,6 +4572,7 @@ var init_store = __esm({
|
|
|
4361
4572
|
init_keychain();
|
|
4362
4573
|
init_config();
|
|
4363
4574
|
init_state_bus();
|
|
4575
|
+
init_memory_write_governor();
|
|
4364
4576
|
INIT_MAX_RETRIES = 3;
|
|
4365
4577
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4366
4578
|
_pendingRecords = [];
|