@askexenow/exe-os 0.9.34 → 0.9.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/backfill-conversations.js +210 -10
- package/dist/bin/backfill-responses.js +210 -10
- package/dist/bin/backfill-vectors.js +13 -2
- package/dist/bin/cleanup-stale-review-tasks.js +217 -10
- package/dist/bin/cli.js +222 -11
- package/dist/bin/exe-assign.js +210 -10
- package/dist/bin/exe-boot.js +24 -3
- package/dist/bin/exe-dispatch.js +222 -11
- package/dist/bin/exe-doctor.js +13 -2
- package/dist/bin/exe-export-behaviors.js +217 -10
- package/dist/bin/exe-forget.js +217 -10
- package/dist/bin/exe-gateway.js +222 -11
- package/dist/bin/exe-heartbeat.js +217 -10
- package/dist/bin/exe-kill.js +217 -10
- package/dist/bin/exe-launch-agent.js +92 -2
- package/dist/bin/exe-link.js +8 -0
- package/dist/bin/exe-pending-messages.js +217 -10
- package/dist/bin/exe-pending-notifications.js +217 -10
- package/dist/bin/exe-pending-reviews.js +217 -10
- package/dist/bin/exe-rename.js +8 -0
- package/dist/bin/exe-review.js +217 -10
- package/dist/bin/exe-search.js +217 -10
- package/dist/bin/exe-session-cleanup.js +222 -11
- package/dist/bin/exe-start-codex.js +92 -2
- package/dist/bin/exe-start-opencode.js +92 -2
- package/dist/bin/exe-status.js +217 -10
- package/dist/bin/exe-team.js +217 -10
- package/dist/bin/git-sweep.js +222 -11
- package/dist/bin/graph-backfill.js +92 -2
- package/dist/bin/graph-export.js +217 -10
- package/dist/bin/intercom-check.js +222 -11
- package/dist/bin/scan-tasks.js +222 -11
- package/dist/bin/setup.js +8 -0
- package/dist/bin/shard-migrate.js +92 -2
- package/dist/gateway/index.js +222 -11
- package/dist/hooks/bug-report-worker.js +222 -11
- package/dist/hooks/codex-stop-task-finalizer.js +217 -10
- package/dist/hooks/commit-complete.js +222 -11
- package/dist/hooks/error-recall.js +217 -10
- package/dist/hooks/ingest.js +217 -10
- package/dist/hooks/instructions-loaded.js +217 -10
- package/dist/hooks/notification.js +217 -10
- package/dist/hooks/post-compact.js +217 -10
- package/dist/hooks/post-tool-combined.js +217 -10
- package/dist/hooks/pre-compact.js +222 -11
- package/dist/hooks/pre-tool-use.js +217 -10
- package/dist/hooks/prompt-submit.js +222 -11
- package/dist/hooks/session-end.js +222 -11
- package/dist/hooks/session-start.js +217 -10
- package/dist/hooks/stop.js +217 -10
- package/dist/hooks/subagent-stop.js +217 -10
- package/dist/hooks/summary-worker.js +14 -1
- package/dist/index.js +222 -11
- package/dist/lib/cloud-sync.js +8 -0
- package/dist/lib/consolidation.js +3 -1
- package/dist/lib/database.js +8 -0
- package/dist/lib/db.js +8 -0
- package/dist/lib/device-registry.js +8 -0
- package/dist/lib/exe-daemon.js +1667 -1413
- package/dist/lib/hybrid-search.js +217 -10
- package/dist/lib/schedules.js +13 -2
- package/dist/lib/store.js +210 -10
- package/dist/lib/tasks.js +5 -1
- package/dist/lib/tmux-routing.js +5 -1
- package/dist/mcp/server.js +222 -11
- package/dist/mcp/tools/create-task.js +5 -1
- package/dist/mcp/tools/update-task.js +5 -1
- package/dist/runtime/index.js +222 -11
- package/dist/tui/App.js +222 -11
- package/package.json +1 -1
|
@@ -2869,6 +2869,14 @@ async function ensureSchema() {
|
|
|
2869
2869
|
);
|
|
2870
2870
|
} catch {
|
|
2871
2871
|
}
|
|
2872
|
+
try {
|
|
2873
|
+
await client.execute(
|
|
2874
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2875
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2876
|
+
WHERE content_hash IS NOT NULL`
|
|
2877
|
+
);
|
|
2878
|
+
} catch {
|
|
2879
|
+
}
|
|
2872
2880
|
await client.executeMultiple(`
|
|
2873
2881
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2874
2882
|
id TEXT PRIMARY KEY,
|
|
@@ -6574,7 +6582,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6574
6582
|
}
|
|
6575
6583
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6576
6584
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
6577
|
-
|
|
6585
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
6586
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
6587
|
+
ccModel += "[1m]";
|
|
6588
|
+
}
|
|
6589
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
6578
6590
|
}
|
|
6579
6591
|
}
|
|
6580
6592
|
let spawnCommand;
|
|
@@ -6921,6 +6933,196 @@ var init_keychain = __esm({
|
|
|
6921
6933
|
}
|
|
6922
6934
|
});
|
|
6923
6935
|
|
|
6936
|
+
// src/lib/memory-write-governor.ts
|
|
6937
|
+
import { createHash } from "crypto";
|
|
6938
|
+
function normalizeMemoryText(text) {
|
|
6939
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
6940
|
+
}
|
|
6941
|
+
function classifyMemoryType(input2) {
|
|
6942
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
6943
|
+
const tool = input2.tool_name.toLowerCase();
|
|
6944
|
+
const text = input2.raw_text.toLowerCase();
|
|
6945
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
6946
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
6947
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
6948
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
6949
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
6950
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
6951
|
+
return "raw";
|
|
6952
|
+
}
|
|
6953
|
+
function shouldDropMemory(text) {
|
|
6954
|
+
const normalized = normalizeMemoryText(text);
|
|
6955
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
6956
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
6957
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
6958
|
+
}
|
|
6959
|
+
return { drop: false };
|
|
6960
|
+
}
|
|
6961
|
+
function shouldSkipEmbedding(input2) {
|
|
6962
|
+
const type = classifyMemoryType(input2);
|
|
6963
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
6964
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
6965
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
6966
|
+
return false;
|
|
6967
|
+
}
|
|
6968
|
+
function hashMemoryContent(text) {
|
|
6969
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
6970
|
+
}
|
|
6971
|
+
function scopedDedupArgs(input2) {
|
|
6972
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
6973
|
+
}
|
|
6974
|
+
function governMemoryRecord(record) {
|
|
6975
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
6976
|
+
const memoryType = classifyMemoryType({
|
|
6977
|
+
raw_text: normalized,
|
|
6978
|
+
agent_id: record.agent_id,
|
|
6979
|
+
project_name: record.project_name,
|
|
6980
|
+
tool_name: record.tool_name,
|
|
6981
|
+
memory_type: record.memory_type
|
|
6982
|
+
});
|
|
6983
|
+
const drop = shouldDropMemory(normalized);
|
|
6984
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
6985
|
+
raw_text: normalized,
|
|
6986
|
+
agent_id: record.agent_id,
|
|
6987
|
+
project_name: record.project_name,
|
|
6988
|
+
tool_name: record.tool_name,
|
|
6989
|
+
memory_type: memoryType
|
|
6990
|
+
});
|
|
6991
|
+
return {
|
|
6992
|
+
record: {
|
|
6993
|
+
...record,
|
|
6994
|
+
raw_text: normalized,
|
|
6995
|
+
memory_type: memoryType,
|
|
6996
|
+
vector: skipEmbedding ? null : record.vector
|
|
6997
|
+
},
|
|
6998
|
+
contentHash: hashMemoryContent(normalized),
|
|
6999
|
+
shouldDrop: drop.drop,
|
|
7000
|
+
dropReason: drop.reason,
|
|
7001
|
+
skipEmbedding,
|
|
7002
|
+
hygiene: {
|
|
7003
|
+
dedup: true,
|
|
7004
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
7005
|
+
}
|
|
7006
|
+
};
|
|
7007
|
+
}
|
|
7008
|
+
async function findScopedDuplicate(input2) {
|
|
7009
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
7010
|
+
const client = getClient2();
|
|
7011
|
+
const args = scopedDedupArgs(input2);
|
|
7012
|
+
let sql = `SELECT id FROM memories
|
|
7013
|
+
WHERE content_hash = ?
|
|
7014
|
+
AND agent_id = ?
|
|
7015
|
+
AND project_name = ?
|
|
7016
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
7017
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
7018
|
+
if (input2.excludeId) {
|
|
7019
|
+
sql += " AND id != ?";
|
|
7020
|
+
args.push(input2.excludeId);
|
|
7021
|
+
}
|
|
7022
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
7023
|
+
const result = await client.execute({ sql, args });
|
|
7024
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
7025
|
+
}
|
|
7026
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
7027
|
+
try {
|
|
7028
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
7029
|
+
const client = getClient2();
|
|
7030
|
+
const current = await client.execute({
|
|
7031
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
7032
|
+
importance, timestamp
|
|
7033
|
+
FROM memories
|
|
7034
|
+
WHERE id = ?
|
|
7035
|
+
LIMIT 1`,
|
|
7036
|
+
args: [memoryId]
|
|
7037
|
+
});
|
|
7038
|
+
const row = current.rows[0];
|
|
7039
|
+
if (!row) return;
|
|
7040
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
7041
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
7042
|
+
const agentId = String(row.agent_id);
|
|
7043
|
+
const projectName = String(row.project_name);
|
|
7044
|
+
if (contentHash) {
|
|
7045
|
+
await client.execute({
|
|
7046
|
+
sql: `UPDATE memories
|
|
7047
|
+
SET status = 'deleted',
|
|
7048
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
7049
|
+
WHERE id != ?
|
|
7050
|
+
AND content_hash = ?
|
|
7051
|
+
AND agent_id = ?
|
|
7052
|
+
AND project_name = ?
|
|
7053
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
7054
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
7055
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
7056
|
+
});
|
|
7057
|
+
}
|
|
7058
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
7059
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
7060
|
+
const old = await client.execute({
|
|
7061
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
7062
|
+
args: [supersedesId]
|
|
7063
|
+
});
|
|
7064
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
7065
|
+
const newImportance = Number(row.importance ?? 0);
|
|
7066
|
+
await client.batch([
|
|
7067
|
+
{
|
|
7068
|
+
sql: `UPDATE memories
|
|
7069
|
+
SET status = 'archived',
|
|
7070
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
7071
|
+
WHERE id = ?`,
|
|
7072
|
+
args: [supersedesId]
|
|
7073
|
+
},
|
|
7074
|
+
{
|
|
7075
|
+
sql: `UPDATE memories
|
|
7076
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
7077
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
7078
|
+
WHERE id = ?`,
|
|
7079
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
7080
|
+
}
|
|
7081
|
+
], "write");
|
|
7082
|
+
}
|
|
7083
|
+
} catch (err) {
|
|
7084
|
+
process.stderr.write(
|
|
7085
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
7086
|
+
`
|
|
7087
|
+
);
|
|
7088
|
+
}
|
|
7089
|
+
}
|
|
7090
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
7091
|
+
if (memoryIds.length === 0) return;
|
|
7092
|
+
const run = () => {
|
|
7093
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
7094
|
+
};
|
|
7095
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
7096
|
+
else setTimeout(run, 0);
|
|
7097
|
+
}
|
|
7098
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
7099
|
+
var init_memory_write_governor = __esm({
|
|
7100
|
+
"src/lib/memory-write-governor.ts"() {
|
|
7101
|
+
"use strict";
|
|
7102
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
7103
|
+
"decision",
|
|
7104
|
+
"adr",
|
|
7105
|
+
"behavior",
|
|
7106
|
+
"procedure"
|
|
7107
|
+
]);
|
|
7108
|
+
NOISE_DROP_PATTERNS = [
|
|
7109
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
7110
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
7111
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
7112
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
7113
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
7114
|
+
];
|
|
7115
|
+
SKIP_EMBED_PATTERNS = [
|
|
7116
|
+
/tmux capture-pane\b/i,
|
|
7117
|
+
/docker ps\b/i,
|
|
7118
|
+
/docker images\b/i,
|
|
7119
|
+
/git status\b/i,
|
|
7120
|
+
/grep .*node_modules/i,
|
|
7121
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
7122
|
+
];
|
|
7123
|
+
}
|
|
7124
|
+
});
|
|
7125
|
+
|
|
6924
7126
|
// src/lib/shard-manager.ts
|
|
6925
7127
|
var shard_manager_exports = {};
|
|
6926
7128
|
__export(shard_manager_exports, {
|
|
@@ -7085,7 +7287,8 @@ async function ensureShardSchema(client) {
|
|
|
7085
7287
|
}
|
|
7086
7288
|
for (const idx of [
|
|
7087
7289
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
7088
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
7290
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
7291
|
+
"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"
|
|
7089
7292
|
]) {
|
|
7090
7293
|
try {
|
|
7091
7294
|
await client.execute(idx);
|
|
@@ -7510,7 +7713,6 @@ __export(store_exports, {
|
|
|
7510
7713
|
vectorToBlob: () => vectorToBlob,
|
|
7511
7714
|
writeMemory: () => writeMemory
|
|
7512
7715
|
});
|
|
7513
|
-
import { createHash } from "crypto";
|
|
7514
7716
|
function isBusyError2(err) {
|
|
7515
7717
|
if (err instanceof Error) {
|
|
7516
7718
|
const msg = err.message.toLowerCase();
|
|
@@ -7624,17 +7826,24 @@ async function writeMemory(record) {
|
|
|
7624
7826
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
7625
7827
|
);
|
|
7626
7828
|
}
|
|
7627
|
-
const
|
|
7628
|
-
if (
|
|
7829
|
+
const governed = governMemoryRecord(record);
|
|
7830
|
+
if (governed.shouldDrop) return;
|
|
7831
|
+
record = governed.record;
|
|
7832
|
+
const contentHash = governed.contentHash;
|
|
7833
|
+
const memoryType = record.memory_type ?? "raw";
|
|
7834
|
+
if (_pendingRecords.some(
|
|
7835
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
7836
|
+
)) {
|
|
7629
7837
|
return;
|
|
7630
7838
|
}
|
|
7631
7839
|
try {
|
|
7632
|
-
const
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7840
|
+
const existing = await findScopedDuplicate({
|
|
7841
|
+
contentHash,
|
|
7842
|
+
agentId: record.agent_id,
|
|
7843
|
+
projectName: record.project_name,
|
|
7844
|
+
memoryType
|
|
7636
7845
|
});
|
|
7637
|
-
if (existing
|
|
7846
|
+
if (existing) return;
|
|
7638
7847
|
} catch {
|
|
7639
7848
|
}
|
|
7640
7849
|
const dbRow = {
|
|
@@ -7665,7 +7874,7 @@ async function writeMemory(record) {
|
|
|
7665
7874
|
tier: record.tier ?? classifyTier(record),
|
|
7666
7875
|
supersedes_id: record.supersedes_id ?? null,
|
|
7667
7876
|
draft: record.draft ? 1 : 0,
|
|
7668
|
-
memory_type:
|
|
7877
|
+
memory_type: memoryType,
|
|
7669
7878
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
7670
7879
|
content_hash: contentHash,
|
|
7671
7880
|
intent: record.intent ?? null,
|
|
@@ -7824,6 +8033,7 @@ async function flushBatch() {
|
|
|
7824
8033
|
const globalClient = getClient();
|
|
7825
8034
|
const globalStmts = batch.map(buildStmt);
|
|
7826
8035
|
await globalClient.batch(globalStmts, "write");
|
|
8036
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
7827
8037
|
_pendingRecords.splice(0, batch.length);
|
|
7828
8038
|
try {
|
|
7829
8039
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -8082,6 +8292,7 @@ var init_store = __esm({
|
|
|
8082
8292
|
init_keychain();
|
|
8083
8293
|
init_config();
|
|
8084
8294
|
init_state_bus();
|
|
8295
|
+
init_memory_write_governor();
|
|
8085
8296
|
INIT_MAX_RETRIES = 3;
|
|
8086
8297
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
8087
8298
|
_pendingRecords = [];
|
|
@@ -2687,6 +2687,14 @@ async function ensureSchema() {
|
|
|
2687
2687
|
);
|
|
2688
2688
|
} catch {
|
|
2689
2689
|
}
|
|
2690
|
+
try {
|
|
2691
|
+
await client.execute(
|
|
2692
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2693
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2694
|
+
WHERE content_hash IS NOT NULL`
|
|
2695
|
+
);
|
|
2696
|
+
} catch {
|
|
2697
|
+
}
|
|
2690
2698
|
await client.executeMultiple(`
|
|
2691
2699
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2692
2700
|
id TEXT PRIMARY KEY,
|
|
@@ -3432,6 +3440,196 @@ var init_state_bus = __esm({
|
|
|
3432
3440
|
}
|
|
3433
3441
|
});
|
|
3434
3442
|
|
|
3443
|
+
// src/lib/memory-write-governor.ts
|
|
3444
|
+
import { createHash } from "crypto";
|
|
3445
|
+
function normalizeMemoryText(text) {
|
|
3446
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3447
|
+
}
|
|
3448
|
+
function classifyMemoryType(input2) {
|
|
3449
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3450
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3451
|
+
const text = input2.raw_text.toLowerCase();
|
|
3452
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3453
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3454
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3455
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3456
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3457
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3458
|
+
return "raw";
|
|
3459
|
+
}
|
|
3460
|
+
function shouldDropMemory(text) {
|
|
3461
|
+
const normalized = normalizeMemoryText(text);
|
|
3462
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3463
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3464
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3465
|
+
}
|
|
3466
|
+
return { drop: false };
|
|
3467
|
+
}
|
|
3468
|
+
function shouldSkipEmbedding(input2) {
|
|
3469
|
+
const type = classifyMemoryType(input2);
|
|
3470
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3471
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3472
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3473
|
+
return false;
|
|
3474
|
+
}
|
|
3475
|
+
function hashMemoryContent(text) {
|
|
3476
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3477
|
+
}
|
|
3478
|
+
function scopedDedupArgs(input2) {
|
|
3479
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3480
|
+
}
|
|
3481
|
+
function governMemoryRecord(record) {
|
|
3482
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3483
|
+
const memoryType = classifyMemoryType({
|
|
3484
|
+
raw_text: normalized,
|
|
3485
|
+
agent_id: record.agent_id,
|
|
3486
|
+
project_name: record.project_name,
|
|
3487
|
+
tool_name: record.tool_name,
|
|
3488
|
+
memory_type: record.memory_type
|
|
3489
|
+
});
|
|
3490
|
+
const drop = shouldDropMemory(normalized);
|
|
3491
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3492
|
+
raw_text: normalized,
|
|
3493
|
+
agent_id: record.agent_id,
|
|
3494
|
+
project_name: record.project_name,
|
|
3495
|
+
tool_name: record.tool_name,
|
|
3496
|
+
memory_type: memoryType
|
|
3497
|
+
});
|
|
3498
|
+
return {
|
|
3499
|
+
record: {
|
|
3500
|
+
...record,
|
|
3501
|
+
raw_text: normalized,
|
|
3502
|
+
memory_type: memoryType,
|
|
3503
|
+
vector: skipEmbedding ? null : record.vector
|
|
3504
|
+
},
|
|
3505
|
+
contentHash: hashMemoryContent(normalized),
|
|
3506
|
+
shouldDrop: drop.drop,
|
|
3507
|
+
dropReason: drop.reason,
|
|
3508
|
+
skipEmbedding,
|
|
3509
|
+
hygiene: {
|
|
3510
|
+
dedup: true,
|
|
3511
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3512
|
+
}
|
|
3513
|
+
};
|
|
3514
|
+
}
|
|
3515
|
+
async function findScopedDuplicate(input2) {
|
|
3516
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3517
|
+
const client = getClient2();
|
|
3518
|
+
const args = scopedDedupArgs(input2);
|
|
3519
|
+
let sql = `SELECT id FROM memories
|
|
3520
|
+
WHERE content_hash = ?
|
|
3521
|
+
AND agent_id = ?
|
|
3522
|
+
AND project_name = ?
|
|
3523
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3524
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3525
|
+
if (input2.excludeId) {
|
|
3526
|
+
sql += " AND id != ?";
|
|
3527
|
+
args.push(input2.excludeId);
|
|
3528
|
+
}
|
|
3529
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3530
|
+
const result = await client.execute({ sql, args });
|
|
3531
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3532
|
+
}
|
|
3533
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3534
|
+
try {
|
|
3535
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3536
|
+
const client = getClient2();
|
|
3537
|
+
const current = await client.execute({
|
|
3538
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3539
|
+
importance, timestamp
|
|
3540
|
+
FROM memories
|
|
3541
|
+
WHERE id = ?
|
|
3542
|
+
LIMIT 1`,
|
|
3543
|
+
args: [memoryId]
|
|
3544
|
+
});
|
|
3545
|
+
const row = current.rows[0];
|
|
3546
|
+
if (!row) return;
|
|
3547
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3548
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3549
|
+
const agentId = String(row.agent_id);
|
|
3550
|
+
const projectName = String(row.project_name);
|
|
3551
|
+
if (contentHash) {
|
|
3552
|
+
await client.execute({
|
|
3553
|
+
sql: `UPDATE memories
|
|
3554
|
+
SET status = 'deleted',
|
|
3555
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3556
|
+
WHERE id != ?
|
|
3557
|
+
AND content_hash = ?
|
|
3558
|
+
AND agent_id = ?
|
|
3559
|
+
AND project_name = ?
|
|
3560
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3561
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3562
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3563
|
+
});
|
|
3564
|
+
}
|
|
3565
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3566
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3567
|
+
const old = await client.execute({
|
|
3568
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3569
|
+
args: [supersedesId]
|
|
3570
|
+
});
|
|
3571
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3572
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3573
|
+
await client.batch([
|
|
3574
|
+
{
|
|
3575
|
+
sql: `UPDATE memories
|
|
3576
|
+
SET status = 'archived',
|
|
3577
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3578
|
+
WHERE id = ?`,
|
|
3579
|
+
args: [supersedesId]
|
|
3580
|
+
},
|
|
3581
|
+
{
|
|
3582
|
+
sql: `UPDATE memories
|
|
3583
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3584
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3585
|
+
WHERE id = ?`,
|
|
3586
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3587
|
+
}
|
|
3588
|
+
], "write");
|
|
3589
|
+
}
|
|
3590
|
+
} catch (err) {
|
|
3591
|
+
process.stderr.write(
|
|
3592
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3593
|
+
`
|
|
3594
|
+
);
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3598
|
+
if (memoryIds.length === 0) return;
|
|
3599
|
+
const run = () => {
|
|
3600
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3601
|
+
};
|
|
3602
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3603
|
+
else setTimeout(run, 0);
|
|
3604
|
+
}
|
|
3605
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3606
|
+
var init_memory_write_governor = __esm({
|
|
3607
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3608
|
+
"use strict";
|
|
3609
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3610
|
+
"decision",
|
|
3611
|
+
"adr",
|
|
3612
|
+
"behavior",
|
|
3613
|
+
"procedure"
|
|
3614
|
+
]);
|
|
3615
|
+
NOISE_DROP_PATTERNS = [
|
|
3616
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3617
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3618
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3619
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3620
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3621
|
+
];
|
|
3622
|
+
SKIP_EMBED_PATTERNS = [
|
|
3623
|
+
/tmux capture-pane\b/i,
|
|
3624
|
+
/docker ps\b/i,
|
|
3625
|
+
/docker images\b/i,
|
|
3626
|
+
/git status\b/i,
|
|
3627
|
+
/grep .*node_modules/i,
|
|
3628
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3629
|
+
];
|
|
3630
|
+
}
|
|
3631
|
+
});
|
|
3632
|
+
|
|
3435
3633
|
// src/lib/shard-manager.ts
|
|
3436
3634
|
var shard_manager_exports = {};
|
|
3437
3635
|
__export(shard_manager_exports, {
|
|
@@ -3596,7 +3794,8 @@ async function ensureShardSchema(client) {
|
|
|
3596
3794
|
}
|
|
3597
3795
|
for (const idx of [
|
|
3598
3796
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3599
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3797
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3798
|
+
"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"
|
|
3600
3799
|
]) {
|
|
3601
3800
|
try {
|
|
3602
3801
|
await client.execute(idx);
|
|
@@ -4021,7 +4220,6 @@ __export(store_exports, {
|
|
|
4021
4220
|
vectorToBlob: () => vectorToBlob,
|
|
4022
4221
|
writeMemory: () => writeMemory
|
|
4023
4222
|
});
|
|
4024
|
-
import { createHash } from "crypto";
|
|
4025
4223
|
function isBusyError2(err) {
|
|
4026
4224
|
if (err instanceof Error) {
|
|
4027
4225
|
const msg = err.message.toLowerCase();
|
|
@@ -4135,17 +4333,24 @@ async function writeMemory(record) {
|
|
|
4135
4333
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4136
4334
|
);
|
|
4137
4335
|
}
|
|
4138
|
-
const
|
|
4139
|
-
if (
|
|
4336
|
+
const governed = governMemoryRecord(record);
|
|
4337
|
+
if (governed.shouldDrop) return;
|
|
4338
|
+
record = governed.record;
|
|
4339
|
+
const contentHash = governed.contentHash;
|
|
4340
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4341
|
+
if (_pendingRecords.some(
|
|
4342
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4343
|
+
)) {
|
|
4140
4344
|
return;
|
|
4141
4345
|
}
|
|
4142
4346
|
try {
|
|
4143
|
-
const
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4347
|
+
const existing = await findScopedDuplicate({
|
|
4348
|
+
contentHash,
|
|
4349
|
+
agentId: record.agent_id,
|
|
4350
|
+
projectName: record.project_name,
|
|
4351
|
+
memoryType
|
|
4147
4352
|
});
|
|
4148
|
-
if (existing
|
|
4353
|
+
if (existing) return;
|
|
4149
4354
|
} catch {
|
|
4150
4355
|
}
|
|
4151
4356
|
const dbRow = {
|
|
@@ -4176,7 +4381,7 @@ async function writeMemory(record) {
|
|
|
4176
4381
|
tier: record.tier ?? classifyTier(record),
|
|
4177
4382
|
supersedes_id: record.supersedes_id ?? null,
|
|
4178
4383
|
draft: record.draft ? 1 : 0,
|
|
4179
|
-
memory_type:
|
|
4384
|
+
memory_type: memoryType,
|
|
4180
4385
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4181
4386
|
content_hash: contentHash,
|
|
4182
4387
|
intent: record.intent ?? null,
|
|
@@ -4335,6 +4540,7 @@ async function flushBatch() {
|
|
|
4335
4540
|
const globalClient = getClient();
|
|
4336
4541
|
const globalStmts = batch.map(buildStmt);
|
|
4337
4542
|
await globalClient.batch(globalStmts, "write");
|
|
4543
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4338
4544
|
_pendingRecords.splice(0, batch.length);
|
|
4339
4545
|
try {
|
|
4340
4546
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4593,6 +4799,7 @@ var init_store = __esm({
|
|
|
4593
4799
|
init_keychain();
|
|
4594
4800
|
init_config();
|
|
4595
4801
|
init_state_bus();
|
|
4802
|
+
init_memory_write_governor();
|
|
4596
4803
|
INIT_MAX_RETRIES = 3;
|
|
4597
4804
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4598
4805
|
_pendingRecords = [];
|