@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
|
@@ -2866,6 +2866,14 @@ async function ensureSchema() {
|
|
|
2866
2866
|
);
|
|
2867
2867
|
} catch {
|
|
2868
2868
|
}
|
|
2869
|
+
try {
|
|
2870
|
+
await client.execute(
|
|
2871
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2872
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2873
|
+
WHERE content_hash IS NOT NULL`
|
|
2874
|
+
);
|
|
2875
|
+
} catch {
|
|
2876
|
+
}
|
|
2869
2877
|
await client.executeMultiple(`
|
|
2870
2878
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2871
2879
|
id TEXT PRIMARY KEY,
|
|
@@ -6381,7 +6389,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6381
6389
|
}
|
|
6382
6390
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6383
6391
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
6384
|
-
|
|
6392
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
6393
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
6394
|
+
ccModel += "[1m]";
|
|
6395
|
+
}
|
|
6396
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
6385
6397
|
}
|
|
6386
6398
|
}
|
|
6387
6399
|
let spawnCommand;
|
|
@@ -6728,6 +6740,196 @@ var init_keychain = __esm({
|
|
|
6728
6740
|
}
|
|
6729
6741
|
});
|
|
6730
6742
|
|
|
6743
|
+
// src/lib/memory-write-governor.ts
|
|
6744
|
+
import { createHash } from "crypto";
|
|
6745
|
+
function normalizeMemoryText(text) {
|
|
6746
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
6747
|
+
}
|
|
6748
|
+
function classifyMemoryType(input) {
|
|
6749
|
+
if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
|
|
6750
|
+
const tool = input.tool_name.toLowerCase();
|
|
6751
|
+
const text = input.raw_text.toLowerCase();
|
|
6752
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
6753
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
6754
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
6755
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
6756
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
6757
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
6758
|
+
return "raw";
|
|
6759
|
+
}
|
|
6760
|
+
function shouldDropMemory(text) {
|
|
6761
|
+
const normalized = normalizeMemoryText(text);
|
|
6762
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
6763
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
6764
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
6765
|
+
}
|
|
6766
|
+
return { drop: false };
|
|
6767
|
+
}
|
|
6768
|
+
function shouldSkipEmbedding(input) {
|
|
6769
|
+
const type = classifyMemoryType(input);
|
|
6770
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
6771
|
+
if (type === "raw" && input.raw_text.length > 2e4) return true;
|
|
6772
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
|
|
6773
|
+
return false;
|
|
6774
|
+
}
|
|
6775
|
+
function hashMemoryContent(text) {
|
|
6776
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
6777
|
+
}
|
|
6778
|
+
function scopedDedupArgs(input) {
|
|
6779
|
+
return [input.contentHash, input.agentId, input.projectName, input.memoryType];
|
|
6780
|
+
}
|
|
6781
|
+
function governMemoryRecord(record) {
|
|
6782
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
6783
|
+
const memoryType = classifyMemoryType({
|
|
6784
|
+
raw_text: normalized,
|
|
6785
|
+
agent_id: record.agent_id,
|
|
6786
|
+
project_name: record.project_name,
|
|
6787
|
+
tool_name: record.tool_name,
|
|
6788
|
+
memory_type: record.memory_type
|
|
6789
|
+
});
|
|
6790
|
+
const drop = shouldDropMemory(normalized);
|
|
6791
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
6792
|
+
raw_text: normalized,
|
|
6793
|
+
agent_id: record.agent_id,
|
|
6794
|
+
project_name: record.project_name,
|
|
6795
|
+
tool_name: record.tool_name,
|
|
6796
|
+
memory_type: memoryType
|
|
6797
|
+
});
|
|
6798
|
+
return {
|
|
6799
|
+
record: {
|
|
6800
|
+
...record,
|
|
6801
|
+
raw_text: normalized,
|
|
6802
|
+
memory_type: memoryType,
|
|
6803
|
+
vector: skipEmbedding ? null : record.vector
|
|
6804
|
+
},
|
|
6805
|
+
contentHash: hashMemoryContent(normalized),
|
|
6806
|
+
shouldDrop: drop.drop,
|
|
6807
|
+
dropReason: drop.reason,
|
|
6808
|
+
skipEmbedding,
|
|
6809
|
+
hygiene: {
|
|
6810
|
+
dedup: true,
|
|
6811
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
6812
|
+
}
|
|
6813
|
+
};
|
|
6814
|
+
}
|
|
6815
|
+
async function findScopedDuplicate(input) {
|
|
6816
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6817
|
+
const client = getClient2();
|
|
6818
|
+
const args = scopedDedupArgs(input);
|
|
6819
|
+
let sql = `SELECT id FROM memories
|
|
6820
|
+
WHERE content_hash = ?
|
|
6821
|
+
AND agent_id = ?
|
|
6822
|
+
AND project_name = ?
|
|
6823
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6824
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
6825
|
+
if (input.excludeId) {
|
|
6826
|
+
sql += " AND id != ?";
|
|
6827
|
+
args.push(input.excludeId);
|
|
6828
|
+
}
|
|
6829
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
6830
|
+
const result = await client.execute({ sql, args });
|
|
6831
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
6832
|
+
}
|
|
6833
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
6834
|
+
try {
|
|
6835
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6836
|
+
const client = getClient2();
|
|
6837
|
+
const current = await client.execute({
|
|
6838
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
6839
|
+
importance, timestamp
|
|
6840
|
+
FROM memories
|
|
6841
|
+
WHERE id = ?
|
|
6842
|
+
LIMIT 1`,
|
|
6843
|
+
args: [memoryId]
|
|
6844
|
+
});
|
|
6845
|
+
const row = current.rows[0];
|
|
6846
|
+
if (!row) return;
|
|
6847
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
6848
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
6849
|
+
const agentId = String(row.agent_id);
|
|
6850
|
+
const projectName = String(row.project_name);
|
|
6851
|
+
if (contentHash) {
|
|
6852
|
+
await client.execute({
|
|
6853
|
+
sql: `UPDATE memories
|
|
6854
|
+
SET status = 'deleted',
|
|
6855
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6856
|
+
WHERE id != ?
|
|
6857
|
+
AND content_hash = ?
|
|
6858
|
+
AND agent_id = ?
|
|
6859
|
+
AND project_name = ?
|
|
6860
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
6861
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
6862
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
6863
|
+
});
|
|
6864
|
+
}
|
|
6865
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
6866
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
6867
|
+
const old = await client.execute({
|
|
6868
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
6869
|
+
args: [supersedesId]
|
|
6870
|
+
});
|
|
6871
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
6872
|
+
const newImportance = Number(row.importance ?? 0);
|
|
6873
|
+
await client.batch([
|
|
6874
|
+
{
|
|
6875
|
+
sql: `UPDATE memories
|
|
6876
|
+
SET status = 'archived',
|
|
6877
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
6878
|
+
WHERE id = ?`,
|
|
6879
|
+
args: [supersedesId]
|
|
6880
|
+
},
|
|
6881
|
+
{
|
|
6882
|
+
sql: `UPDATE memories
|
|
6883
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
6884
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
6885
|
+
WHERE id = ?`,
|
|
6886
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
6887
|
+
}
|
|
6888
|
+
], "write");
|
|
6889
|
+
}
|
|
6890
|
+
} catch (err) {
|
|
6891
|
+
process.stderr.write(
|
|
6892
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
6893
|
+
`
|
|
6894
|
+
);
|
|
6895
|
+
}
|
|
6896
|
+
}
|
|
6897
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
6898
|
+
if (memoryIds.length === 0) return;
|
|
6899
|
+
const run = () => {
|
|
6900
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
6901
|
+
};
|
|
6902
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
6903
|
+
else setTimeout(run, 0);
|
|
6904
|
+
}
|
|
6905
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
6906
|
+
var init_memory_write_governor = __esm({
|
|
6907
|
+
"src/lib/memory-write-governor.ts"() {
|
|
6908
|
+
"use strict";
|
|
6909
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
6910
|
+
"decision",
|
|
6911
|
+
"adr",
|
|
6912
|
+
"behavior",
|
|
6913
|
+
"procedure"
|
|
6914
|
+
]);
|
|
6915
|
+
NOISE_DROP_PATTERNS = [
|
|
6916
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
6917
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
6918
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
6919
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
6920
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
6921
|
+
];
|
|
6922
|
+
SKIP_EMBED_PATTERNS = [
|
|
6923
|
+
/tmux capture-pane\b/i,
|
|
6924
|
+
/docker ps\b/i,
|
|
6925
|
+
/docker images\b/i,
|
|
6926
|
+
/git status\b/i,
|
|
6927
|
+
/grep .*node_modules/i,
|
|
6928
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
6929
|
+
];
|
|
6930
|
+
}
|
|
6931
|
+
});
|
|
6932
|
+
|
|
6731
6933
|
// src/lib/shard-manager.ts
|
|
6732
6934
|
var shard_manager_exports = {};
|
|
6733
6935
|
__export(shard_manager_exports, {
|
|
@@ -6892,7 +7094,8 @@ async function ensureShardSchema(client) {
|
|
|
6892
7094
|
}
|
|
6893
7095
|
for (const idx of [
|
|
6894
7096
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
6895
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
7097
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
7098
|
+
"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"
|
|
6896
7099
|
]) {
|
|
6897
7100
|
try {
|
|
6898
7101
|
await client.execute(idx);
|
|
@@ -7317,7 +7520,6 @@ __export(store_exports, {
|
|
|
7317
7520
|
vectorToBlob: () => vectorToBlob,
|
|
7318
7521
|
writeMemory: () => writeMemory
|
|
7319
7522
|
});
|
|
7320
|
-
import { createHash } from "crypto";
|
|
7321
7523
|
function isBusyError2(err) {
|
|
7322
7524
|
if (err instanceof Error) {
|
|
7323
7525
|
const msg = err.message.toLowerCase();
|
|
@@ -7431,17 +7633,24 @@ async function writeMemory(record) {
|
|
|
7431
7633
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
7432
7634
|
);
|
|
7433
7635
|
}
|
|
7434
|
-
const
|
|
7435
|
-
if (
|
|
7636
|
+
const governed = governMemoryRecord(record);
|
|
7637
|
+
if (governed.shouldDrop) return;
|
|
7638
|
+
record = governed.record;
|
|
7639
|
+
const contentHash = governed.contentHash;
|
|
7640
|
+
const memoryType = record.memory_type ?? "raw";
|
|
7641
|
+
if (_pendingRecords.some(
|
|
7642
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
7643
|
+
)) {
|
|
7436
7644
|
return;
|
|
7437
7645
|
}
|
|
7438
7646
|
try {
|
|
7439
|
-
const
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7647
|
+
const existing = await findScopedDuplicate({
|
|
7648
|
+
contentHash,
|
|
7649
|
+
agentId: record.agent_id,
|
|
7650
|
+
projectName: record.project_name,
|
|
7651
|
+
memoryType
|
|
7443
7652
|
});
|
|
7444
|
-
if (existing
|
|
7653
|
+
if (existing) return;
|
|
7445
7654
|
} catch {
|
|
7446
7655
|
}
|
|
7447
7656
|
const dbRow = {
|
|
@@ -7472,7 +7681,7 @@ async function writeMemory(record) {
|
|
|
7472
7681
|
tier: record.tier ?? classifyTier(record),
|
|
7473
7682
|
supersedes_id: record.supersedes_id ?? null,
|
|
7474
7683
|
draft: record.draft ? 1 : 0,
|
|
7475
|
-
memory_type:
|
|
7684
|
+
memory_type: memoryType,
|
|
7476
7685
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
7477
7686
|
content_hash: contentHash,
|
|
7478
7687
|
intent: record.intent ?? null,
|
|
@@ -7631,6 +7840,7 @@ async function flushBatch() {
|
|
|
7631
7840
|
const globalClient = getClient();
|
|
7632
7841
|
const globalStmts = batch.map(buildStmt);
|
|
7633
7842
|
await globalClient.batch(globalStmts, "write");
|
|
7843
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
7634
7844
|
_pendingRecords.splice(0, batch.length);
|
|
7635
7845
|
try {
|
|
7636
7846
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -7889,6 +8099,7 @@ var init_store = __esm({
|
|
|
7889
8099
|
init_keychain();
|
|
7890
8100
|
init_config();
|
|
7891
8101
|
init_state_bus();
|
|
8102
|
+
init_memory_write_governor();
|
|
7892
8103
|
INIT_MAX_RETRIES = 3;
|
|
7893
8104
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
7894
8105
|
_pendingRecords = [];
|
|
@@ -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,196 @@ 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 (memoryIds.length === 0) return;
|
|
3191
|
+
const run = () => {
|
|
3192
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3193
|
+
};
|
|
3194
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3195
|
+
else setTimeout(run, 0);
|
|
3196
|
+
}
|
|
3197
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3198
|
+
var init_memory_write_governor = __esm({
|
|
3199
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3200
|
+
"use strict";
|
|
3201
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3202
|
+
"decision",
|
|
3203
|
+
"adr",
|
|
3204
|
+
"behavior",
|
|
3205
|
+
"procedure"
|
|
3206
|
+
]);
|
|
3207
|
+
NOISE_DROP_PATTERNS = [
|
|
3208
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3209
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3210
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3211
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3212
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3213
|
+
];
|
|
3214
|
+
SKIP_EMBED_PATTERNS = [
|
|
3215
|
+
/tmux capture-pane\b/i,
|
|
3216
|
+
/docker ps\b/i,
|
|
3217
|
+
/docker images\b/i,
|
|
3218
|
+
/git status\b/i,
|
|
3219
|
+
/grep .*node_modules/i,
|
|
3220
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3221
|
+
];
|
|
3222
|
+
}
|
|
3223
|
+
});
|
|
3224
|
+
|
|
3027
3225
|
// src/lib/shard-manager.ts
|
|
3028
3226
|
var shard_manager_exports = {};
|
|
3029
3227
|
__export(shard_manager_exports, {
|
|
@@ -3188,7 +3386,8 @@ async function ensureShardSchema(client) {
|
|
|
3188
3386
|
}
|
|
3189
3387
|
for (const idx of [
|
|
3190
3388
|
"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"
|
|
3389
|
+
"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_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
|
|
3192
3391
|
]) {
|
|
3193
3392
|
try {
|
|
3194
3393
|
await client.execute(idx);
|
|
@@ -3613,7 +3812,6 @@ __export(store_exports, {
|
|
|
3613
3812
|
vectorToBlob: () => vectorToBlob,
|
|
3614
3813
|
writeMemory: () => writeMemory
|
|
3615
3814
|
});
|
|
3616
|
-
import { createHash } from "crypto";
|
|
3617
3815
|
function isBusyError2(err) {
|
|
3618
3816
|
if (err instanceof Error) {
|
|
3619
3817
|
const msg = err.message.toLowerCase();
|
|
@@ -3727,17 +3925,24 @@ async function writeMemory(record) {
|
|
|
3727
3925
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
3728
3926
|
);
|
|
3729
3927
|
}
|
|
3730
|
-
const
|
|
3731
|
-
if (
|
|
3928
|
+
const governed = governMemoryRecord(record);
|
|
3929
|
+
if (governed.shouldDrop) return;
|
|
3930
|
+
record = governed.record;
|
|
3931
|
+
const contentHash = governed.contentHash;
|
|
3932
|
+
const memoryType = record.memory_type ?? "raw";
|
|
3933
|
+
if (_pendingRecords.some(
|
|
3934
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
3935
|
+
)) {
|
|
3732
3936
|
return;
|
|
3733
3937
|
}
|
|
3734
3938
|
try {
|
|
3735
|
-
const
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3939
|
+
const existing = await findScopedDuplicate({
|
|
3940
|
+
contentHash,
|
|
3941
|
+
agentId: record.agent_id,
|
|
3942
|
+
projectName: record.project_name,
|
|
3943
|
+
memoryType
|
|
3739
3944
|
});
|
|
3740
|
-
if (existing
|
|
3945
|
+
if (existing) return;
|
|
3741
3946
|
} catch {
|
|
3742
3947
|
}
|
|
3743
3948
|
const dbRow = {
|
|
@@ -3768,7 +3973,7 @@ async function writeMemory(record) {
|
|
|
3768
3973
|
tier: record.tier ?? classifyTier(record),
|
|
3769
3974
|
supersedes_id: record.supersedes_id ?? null,
|
|
3770
3975
|
draft: record.draft ? 1 : 0,
|
|
3771
|
-
memory_type:
|
|
3976
|
+
memory_type: memoryType,
|
|
3772
3977
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
3773
3978
|
content_hash: contentHash,
|
|
3774
3979
|
intent: record.intent ?? null,
|
|
@@ -3927,6 +4132,7 @@ async function flushBatch() {
|
|
|
3927
4132
|
const globalClient = getClient();
|
|
3928
4133
|
const globalStmts = batch.map(buildStmt);
|
|
3929
4134
|
await globalClient.batch(globalStmts, "write");
|
|
4135
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
3930
4136
|
_pendingRecords.splice(0, batch.length);
|
|
3931
4137
|
try {
|
|
3932
4138
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4185,6 +4391,7 @@ var init_store = __esm({
|
|
|
4185
4391
|
init_keychain();
|
|
4186
4392
|
init_config();
|
|
4187
4393
|
init_state_bus();
|
|
4394
|
+
init_memory_write_governor();
|
|
4188
4395
|
INIT_MAX_RETRIES = 3;
|
|
4189
4396
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4190
4397
|
_pendingRecords = [];
|