@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
|
@@ -2871,6 +2871,14 @@ async function ensureSchema() {
|
|
|
2871
2871
|
);
|
|
2872
2872
|
} catch {
|
|
2873
2873
|
}
|
|
2874
|
+
try {
|
|
2875
|
+
await client.execute(
|
|
2876
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2877
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2878
|
+
WHERE content_hash IS NOT NULL`
|
|
2879
|
+
);
|
|
2880
|
+
} catch {
|
|
2881
|
+
}
|
|
2874
2882
|
await client.executeMultiple(`
|
|
2875
2883
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2876
2884
|
id TEXT PRIMARY KEY,
|
|
@@ -3922,6 +3930,196 @@ var init_state_bus = __esm({
|
|
|
3922
3930
|
}
|
|
3923
3931
|
});
|
|
3924
3932
|
|
|
3933
|
+
// src/lib/memory-write-governor.ts
|
|
3934
|
+
import { createHash } from "crypto";
|
|
3935
|
+
function normalizeMemoryText(text) {
|
|
3936
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3937
|
+
}
|
|
3938
|
+
function classifyMemoryType(input2) {
|
|
3939
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3940
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3941
|
+
const text = input2.raw_text.toLowerCase();
|
|
3942
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3943
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3944
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3945
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3946
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3947
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3948
|
+
return "raw";
|
|
3949
|
+
}
|
|
3950
|
+
function shouldDropMemory(text) {
|
|
3951
|
+
const normalized = normalizeMemoryText(text);
|
|
3952
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3953
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3954
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3955
|
+
}
|
|
3956
|
+
return { drop: false };
|
|
3957
|
+
}
|
|
3958
|
+
function shouldSkipEmbedding(input2) {
|
|
3959
|
+
const type = classifyMemoryType(input2);
|
|
3960
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3961
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3962
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3963
|
+
return false;
|
|
3964
|
+
}
|
|
3965
|
+
function hashMemoryContent(text) {
|
|
3966
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3967
|
+
}
|
|
3968
|
+
function scopedDedupArgs(input2) {
|
|
3969
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3970
|
+
}
|
|
3971
|
+
function governMemoryRecord(record) {
|
|
3972
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3973
|
+
const memoryType = classifyMemoryType({
|
|
3974
|
+
raw_text: normalized,
|
|
3975
|
+
agent_id: record.agent_id,
|
|
3976
|
+
project_name: record.project_name,
|
|
3977
|
+
tool_name: record.tool_name,
|
|
3978
|
+
memory_type: record.memory_type
|
|
3979
|
+
});
|
|
3980
|
+
const drop = shouldDropMemory(normalized);
|
|
3981
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3982
|
+
raw_text: normalized,
|
|
3983
|
+
agent_id: record.agent_id,
|
|
3984
|
+
project_name: record.project_name,
|
|
3985
|
+
tool_name: record.tool_name,
|
|
3986
|
+
memory_type: memoryType
|
|
3987
|
+
});
|
|
3988
|
+
return {
|
|
3989
|
+
record: {
|
|
3990
|
+
...record,
|
|
3991
|
+
raw_text: normalized,
|
|
3992
|
+
memory_type: memoryType,
|
|
3993
|
+
vector: skipEmbedding ? null : record.vector
|
|
3994
|
+
},
|
|
3995
|
+
contentHash: hashMemoryContent(normalized),
|
|
3996
|
+
shouldDrop: drop.drop,
|
|
3997
|
+
dropReason: drop.reason,
|
|
3998
|
+
skipEmbedding,
|
|
3999
|
+
hygiene: {
|
|
4000
|
+
dedup: true,
|
|
4001
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
4002
|
+
}
|
|
4003
|
+
};
|
|
4004
|
+
}
|
|
4005
|
+
async function findScopedDuplicate(input2) {
|
|
4006
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4007
|
+
const client = getClient2();
|
|
4008
|
+
const args = scopedDedupArgs(input2);
|
|
4009
|
+
let sql = `SELECT id FROM memories
|
|
4010
|
+
WHERE content_hash = ?
|
|
4011
|
+
AND agent_id = ?
|
|
4012
|
+
AND project_name = ?
|
|
4013
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
4014
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
4015
|
+
if (input2.excludeId) {
|
|
4016
|
+
sql += " AND id != ?";
|
|
4017
|
+
args.push(input2.excludeId);
|
|
4018
|
+
}
|
|
4019
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
4020
|
+
const result = await client.execute({ sql, args });
|
|
4021
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
4022
|
+
}
|
|
4023
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
4024
|
+
try {
|
|
4025
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4026
|
+
const client = getClient2();
|
|
4027
|
+
const current = await client.execute({
|
|
4028
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
4029
|
+
importance, timestamp
|
|
4030
|
+
FROM memories
|
|
4031
|
+
WHERE id = ?
|
|
4032
|
+
LIMIT 1`,
|
|
4033
|
+
args: [memoryId]
|
|
4034
|
+
});
|
|
4035
|
+
const row = current.rows[0];
|
|
4036
|
+
if (!row) return;
|
|
4037
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
4038
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
4039
|
+
const agentId = String(row.agent_id);
|
|
4040
|
+
const projectName = String(row.project_name);
|
|
4041
|
+
if (contentHash) {
|
|
4042
|
+
await client.execute({
|
|
4043
|
+
sql: `UPDATE memories
|
|
4044
|
+
SET status = 'deleted',
|
|
4045
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4046
|
+
WHERE id != ?
|
|
4047
|
+
AND content_hash = ?
|
|
4048
|
+
AND agent_id = ?
|
|
4049
|
+
AND project_name = ?
|
|
4050
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
4051
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
4052
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
4053
|
+
});
|
|
4054
|
+
}
|
|
4055
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
4056
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
4057
|
+
const old = await client.execute({
|
|
4058
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
4059
|
+
args: [supersedesId]
|
|
4060
|
+
});
|
|
4061
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
4062
|
+
const newImportance = Number(row.importance ?? 0);
|
|
4063
|
+
await client.batch([
|
|
4064
|
+
{
|
|
4065
|
+
sql: `UPDATE memories
|
|
4066
|
+
SET status = 'archived',
|
|
4067
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
4068
|
+
WHERE id = ?`,
|
|
4069
|
+
args: [supersedesId]
|
|
4070
|
+
},
|
|
4071
|
+
{
|
|
4072
|
+
sql: `UPDATE memories
|
|
4073
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
4074
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
4075
|
+
WHERE id = ?`,
|
|
4076
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
4077
|
+
}
|
|
4078
|
+
], "write");
|
|
4079
|
+
}
|
|
4080
|
+
} catch (err) {
|
|
4081
|
+
process.stderr.write(
|
|
4082
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
4083
|
+
`
|
|
4084
|
+
);
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
4088
|
+
if (memoryIds.length === 0) return;
|
|
4089
|
+
const run = () => {
|
|
4090
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
4091
|
+
};
|
|
4092
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
4093
|
+
else setTimeout(run, 0);
|
|
4094
|
+
}
|
|
4095
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
4096
|
+
var init_memory_write_governor = __esm({
|
|
4097
|
+
"src/lib/memory-write-governor.ts"() {
|
|
4098
|
+
"use strict";
|
|
4099
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
4100
|
+
"decision",
|
|
4101
|
+
"adr",
|
|
4102
|
+
"behavior",
|
|
4103
|
+
"procedure"
|
|
4104
|
+
]);
|
|
4105
|
+
NOISE_DROP_PATTERNS = [
|
|
4106
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
4107
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
4108
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
4109
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
4110
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
4111
|
+
];
|
|
4112
|
+
SKIP_EMBED_PATTERNS = [
|
|
4113
|
+
/tmux capture-pane\b/i,
|
|
4114
|
+
/docker ps\b/i,
|
|
4115
|
+
/docker images\b/i,
|
|
4116
|
+
/git status\b/i,
|
|
4117
|
+
/grep .*node_modules/i,
|
|
4118
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
4119
|
+
];
|
|
4120
|
+
}
|
|
4121
|
+
});
|
|
4122
|
+
|
|
3925
4123
|
// src/lib/shard-manager.ts
|
|
3926
4124
|
var shard_manager_exports = {};
|
|
3927
4125
|
__export(shard_manager_exports, {
|
|
@@ -4086,7 +4284,8 @@ async function ensureShardSchema(client) {
|
|
|
4086
4284
|
}
|
|
4087
4285
|
for (const idx of [
|
|
4088
4286
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
4089
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
4287
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
4288
|
+
"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"
|
|
4090
4289
|
]) {
|
|
4091
4290
|
try {
|
|
4092
4291
|
await client.execute(idx);
|
|
@@ -4511,7 +4710,6 @@ __export(store_exports, {
|
|
|
4511
4710
|
vectorToBlob: () => vectorToBlob,
|
|
4512
4711
|
writeMemory: () => writeMemory
|
|
4513
4712
|
});
|
|
4514
|
-
import { createHash } from "crypto";
|
|
4515
4713
|
function isBusyError2(err) {
|
|
4516
4714
|
if (err instanceof Error) {
|
|
4517
4715
|
const msg = err.message.toLowerCase();
|
|
@@ -4625,17 +4823,24 @@ async function writeMemory(record) {
|
|
|
4625
4823
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4626
4824
|
);
|
|
4627
4825
|
}
|
|
4628
|
-
const
|
|
4629
|
-
if (
|
|
4826
|
+
const governed = governMemoryRecord(record);
|
|
4827
|
+
if (governed.shouldDrop) return;
|
|
4828
|
+
record = governed.record;
|
|
4829
|
+
const contentHash = governed.contentHash;
|
|
4830
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4831
|
+
if (_pendingRecords.some(
|
|
4832
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4833
|
+
)) {
|
|
4630
4834
|
return;
|
|
4631
4835
|
}
|
|
4632
4836
|
try {
|
|
4633
|
-
const
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4837
|
+
const existing = await findScopedDuplicate({
|
|
4838
|
+
contentHash,
|
|
4839
|
+
agentId: record.agent_id,
|
|
4840
|
+
projectName: record.project_name,
|
|
4841
|
+
memoryType
|
|
4637
4842
|
});
|
|
4638
|
-
if (existing
|
|
4843
|
+
if (existing) return;
|
|
4639
4844
|
} catch {
|
|
4640
4845
|
}
|
|
4641
4846
|
const dbRow = {
|
|
@@ -4666,7 +4871,7 @@ async function writeMemory(record) {
|
|
|
4666
4871
|
tier: record.tier ?? classifyTier(record),
|
|
4667
4872
|
supersedes_id: record.supersedes_id ?? null,
|
|
4668
4873
|
draft: record.draft ? 1 : 0,
|
|
4669
|
-
memory_type:
|
|
4874
|
+
memory_type: memoryType,
|
|
4670
4875
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4671
4876
|
content_hash: contentHash,
|
|
4672
4877
|
intent: record.intent ?? null,
|
|
@@ -4825,6 +5030,7 @@ async function flushBatch() {
|
|
|
4825
5030
|
const globalClient = getClient();
|
|
4826
5031
|
const globalStmts = batch.map(buildStmt);
|
|
4827
5032
|
await globalClient.batch(globalStmts, "write");
|
|
5033
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4828
5034
|
_pendingRecords.splice(0, batch.length);
|
|
4829
5035
|
try {
|
|
4830
5036
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -5083,6 +5289,7 @@ var init_store = __esm({
|
|
|
5083
5289
|
init_keychain();
|
|
5084
5290
|
init_config();
|
|
5085
5291
|
init_state_bus();
|
|
5292
|
+
init_memory_write_governor();
|
|
5086
5293
|
INIT_MAX_RETRIES = 3;
|
|
5087
5294
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
5088
5295
|
_pendingRecords = [];
|
|
@@ -2621,6 +2621,14 @@ async function ensureSchema() {
|
|
|
2621
2621
|
);
|
|
2622
2622
|
} catch {
|
|
2623
2623
|
}
|
|
2624
|
+
try {
|
|
2625
|
+
await client.execute(
|
|
2626
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
|
|
2627
|
+
ON memories(content_hash, agent_id, project_name, memory_type)
|
|
2628
|
+
WHERE content_hash IS NOT NULL`
|
|
2629
|
+
);
|
|
2630
|
+
} catch {
|
|
2631
|
+
}
|
|
2624
2632
|
await client.executeMultiple(`
|
|
2625
2633
|
CREATE TABLE IF NOT EXISTS entities (
|
|
2626
2634
|
id TEXT PRIMARY KEY,
|
|
@@ -3366,6 +3374,196 @@ var init_state_bus = __esm({
|
|
|
3366
3374
|
}
|
|
3367
3375
|
});
|
|
3368
3376
|
|
|
3377
|
+
// src/lib/memory-write-governor.ts
|
|
3378
|
+
import { createHash } from "crypto";
|
|
3379
|
+
function normalizeMemoryText(text) {
|
|
3380
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
|
|
3381
|
+
}
|
|
3382
|
+
function classifyMemoryType(input2) {
|
|
3383
|
+
if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
|
|
3384
|
+
const tool = input2.tool_name.toLowerCase();
|
|
3385
|
+
const text = input2.raw_text.toLowerCase();
|
|
3386
|
+
if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
|
|
3387
|
+
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
3388
|
+
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
3389
|
+
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
3390
|
+
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
3391
|
+
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
3392
|
+
return "raw";
|
|
3393
|
+
}
|
|
3394
|
+
function shouldDropMemory(text) {
|
|
3395
|
+
const normalized = normalizeMemoryText(text);
|
|
3396
|
+
if (normalized.length < 10) return { drop: true, reason: "too_short" };
|
|
3397
|
+
if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
3398
|
+
return { drop: true, reason: "known_boilerplate_noise" };
|
|
3399
|
+
}
|
|
3400
|
+
return { drop: false };
|
|
3401
|
+
}
|
|
3402
|
+
function shouldSkipEmbedding(input2) {
|
|
3403
|
+
const type = classifyMemoryType(input2);
|
|
3404
|
+
if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
|
|
3405
|
+
if (type === "raw" && input2.raw_text.length > 2e4) return true;
|
|
3406
|
+
if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
|
|
3407
|
+
return false;
|
|
3408
|
+
}
|
|
3409
|
+
function hashMemoryContent(text) {
|
|
3410
|
+
return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
|
|
3411
|
+
}
|
|
3412
|
+
function scopedDedupArgs(input2) {
|
|
3413
|
+
return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
|
|
3414
|
+
}
|
|
3415
|
+
function governMemoryRecord(record) {
|
|
3416
|
+
const normalized = normalizeMemoryText(record.raw_text);
|
|
3417
|
+
const memoryType = classifyMemoryType({
|
|
3418
|
+
raw_text: normalized,
|
|
3419
|
+
agent_id: record.agent_id,
|
|
3420
|
+
project_name: record.project_name,
|
|
3421
|
+
tool_name: record.tool_name,
|
|
3422
|
+
memory_type: record.memory_type
|
|
3423
|
+
});
|
|
3424
|
+
const drop = shouldDropMemory(normalized);
|
|
3425
|
+
const skipEmbedding = shouldSkipEmbedding({
|
|
3426
|
+
raw_text: normalized,
|
|
3427
|
+
agent_id: record.agent_id,
|
|
3428
|
+
project_name: record.project_name,
|
|
3429
|
+
tool_name: record.tool_name,
|
|
3430
|
+
memory_type: memoryType
|
|
3431
|
+
});
|
|
3432
|
+
return {
|
|
3433
|
+
record: {
|
|
3434
|
+
...record,
|
|
3435
|
+
raw_text: normalized,
|
|
3436
|
+
memory_type: memoryType,
|
|
3437
|
+
vector: skipEmbedding ? null : record.vector
|
|
3438
|
+
},
|
|
3439
|
+
contentHash: hashMemoryContent(normalized),
|
|
3440
|
+
shouldDrop: drop.drop,
|
|
3441
|
+
dropReason: drop.reason,
|
|
3442
|
+
skipEmbedding,
|
|
3443
|
+
hygiene: {
|
|
3444
|
+
dedup: true,
|
|
3445
|
+
supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
|
|
3446
|
+
}
|
|
3447
|
+
};
|
|
3448
|
+
}
|
|
3449
|
+
async function findScopedDuplicate(input2) {
|
|
3450
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3451
|
+
const client = getClient2();
|
|
3452
|
+
const args = scopedDedupArgs(input2);
|
|
3453
|
+
let sql = `SELECT id FROM memories
|
|
3454
|
+
WHERE content_hash = ?
|
|
3455
|
+
AND agent_id = ?
|
|
3456
|
+
AND project_name = ?
|
|
3457
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3458
|
+
AND COALESCE(status, 'active') != 'deleted'`;
|
|
3459
|
+
if (input2.excludeId) {
|
|
3460
|
+
sql += " AND id != ?";
|
|
3461
|
+
args.push(input2.excludeId);
|
|
3462
|
+
}
|
|
3463
|
+
sql += " ORDER BY timestamp DESC LIMIT 1";
|
|
3464
|
+
const result = await client.execute({ sql, args });
|
|
3465
|
+
return result.rows[0]?.id ? String(result.rows[0].id) : null;
|
|
3466
|
+
}
|
|
3467
|
+
async function runPostWriteMemoryHygiene(memoryId) {
|
|
3468
|
+
try {
|
|
3469
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3470
|
+
const client = getClient2();
|
|
3471
|
+
const current = await client.execute({
|
|
3472
|
+
sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
|
|
3473
|
+
importance, timestamp
|
|
3474
|
+
FROM memories
|
|
3475
|
+
WHERE id = ?
|
|
3476
|
+
LIMIT 1`,
|
|
3477
|
+
args: [memoryId]
|
|
3478
|
+
});
|
|
3479
|
+
const row = current.rows[0];
|
|
3480
|
+
if (!row) return;
|
|
3481
|
+
const memoryType = String(row.memory_type ?? "raw");
|
|
3482
|
+
const contentHash = row.content_hash ? String(row.content_hash) : null;
|
|
3483
|
+
const agentId = String(row.agent_id);
|
|
3484
|
+
const projectName = String(row.project_name);
|
|
3485
|
+
if (contentHash) {
|
|
3486
|
+
await client.execute({
|
|
3487
|
+
sql: `UPDATE memories
|
|
3488
|
+
SET status = 'deleted',
|
|
3489
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3490
|
+
WHERE id != ?
|
|
3491
|
+
AND content_hash = ?
|
|
3492
|
+
AND agent_id = ?
|
|
3493
|
+
AND project_name = ?
|
|
3494
|
+
AND COALESCE(memory_type, 'raw') = ?
|
|
3495
|
+
AND COALESCE(status, 'active') = 'active'`,
|
|
3496
|
+
args: [memoryId, contentHash, agentId, projectName, memoryType]
|
|
3497
|
+
});
|
|
3498
|
+
}
|
|
3499
|
+
const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
|
|
3500
|
+
if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
|
|
3501
|
+
const old = await client.execute({
|
|
3502
|
+
sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
|
|
3503
|
+
args: [supersedesId]
|
|
3504
|
+
});
|
|
3505
|
+
const oldImportance = Number(old.rows[0]?.importance ?? 0);
|
|
3506
|
+
const newImportance = Number(row.importance ?? 0);
|
|
3507
|
+
await client.batch([
|
|
3508
|
+
{
|
|
3509
|
+
sql: `UPDATE memories
|
|
3510
|
+
SET status = 'archived',
|
|
3511
|
+
outcome = COALESCE(outcome, 'superseded')
|
|
3512
|
+
WHERE id = ?`,
|
|
3513
|
+
args: [supersedesId]
|
|
3514
|
+
},
|
|
3515
|
+
{
|
|
3516
|
+
sql: `UPDATE memories
|
|
3517
|
+
SET importance = MAX(COALESCE(importance, 5), ?),
|
|
3518
|
+
parent_memory_id = COALESCE(parent_memory_id, ?)
|
|
3519
|
+
WHERE id = ?`,
|
|
3520
|
+
args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
|
|
3521
|
+
}
|
|
3522
|
+
], "write");
|
|
3523
|
+
}
|
|
3524
|
+
} catch (err) {
|
|
3525
|
+
process.stderr.write(
|
|
3526
|
+
`[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
|
|
3527
|
+
`
|
|
3528
|
+
);
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
function schedulePostWriteMemoryHygiene(memoryIds) {
|
|
3532
|
+
if (memoryIds.length === 0) return;
|
|
3533
|
+
const run = () => {
|
|
3534
|
+
void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
|
|
3535
|
+
};
|
|
3536
|
+
if (typeof setImmediate === "function") setImmediate(run);
|
|
3537
|
+
else setTimeout(run, 0);
|
|
3538
|
+
}
|
|
3539
|
+
var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
|
|
3540
|
+
var init_memory_write_governor = __esm({
|
|
3541
|
+
"src/lib/memory-write-governor.ts"() {
|
|
3542
|
+
"use strict";
|
|
3543
|
+
HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
|
|
3544
|
+
"decision",
|
|
3545
|
+
"adr",
|
|
3546
|
+
"behavior",
|
|
3547
|
+
"procedure"
|
|
3548
|
+
]);
|
|
3549
|
+
NOISE_DROP_PATTERNS = [
|
|
3550
|
+
/^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
|
|
3551
|
+
/^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
|
|
3552
|
+
/^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
|
|
3553
|
+
/^\s*Intercom is a speedup, not delivery/im,
|
|
3554
|
+
/^\s*Context bar reads as USAGE not remaining/im
|
|
3555
|
+
];
|
|
3556
|
+
SKIP_EMBED_PATTERNS = [
|
|
3557
|
+
/tmux capture-pane\b/i,
|
|
3558
|
+
/docker ps\b/i,
|
|
3559
|
+
/docker images\b/i,
|
|
3560
|
+
/git status\b/i,
|
|
3561
|
+
/grep .*node_modules/i,
|
|
3562
|
+
/npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
|
|
3563
|
+
];
|
|
3564
|
+
}
|
|
3565
|
+
});
|
|
3566
|
+
|
|
3369
3567
|
// src/lib/shard-manager.ts
|
|
3370
3568
|
var shard_manager_exports = {};
|
|
3371
3569
|
__export(shard_manager_exports, {
|
|
@@ -3530,7 +3728,8 @@ async function ensureShardSchema(client) {
|
|
|
3530
3728
|
}
|
|
3531
3729
|
for (const idx of [
|
|
3532
3730
|
"CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
|
|
3533
|
-
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
|
|
3731
|
+
"CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
|
|
3732
|
+
"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"
|
|
3534
3733
|
]) {
|
|
3535
3734
|
try {
|
|
3536
3735
|
await client.execute(idx);
|
|
@@ -3955,7 +4154,6 @@ __export(store_exports, {
|
|
|
3955
4154
|
vectorToBlob: () => vectorToBlob,
|
|
3956
4155
|
writeMemory: () => writeMemory
|
|
3957
4156
|
});
|
|
3958
|
-
import { createHash } from "crypto";
|
|
3959
4157
|
function isBusyError2(err) {
|
|
3960
4158
|
if (err instanceof Error) {
|
|
3961
4159
|
const msg = err.message.toLowerCase();
|
|
@@ -4069,17 +4267,24 @@ async function writeMemory(record) {
|
|
|
4069
4267
|
`Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
|
|
4070
4268
|
);
|
|
4071
4269
|
}
|
|
4072
|
-
const
|
|
4073
|
-
if (
|
|
4270
|
+
const governed = governMemoryRecord(record);
|
|
4271
|
+
if (governed.shouldDrop) return;
|
|
4272
|
+
record = governed.record;
|
|
4273
|
+
const contentHash = governed.contentHash;
|
|
4274
|
+
const memoryType = record.memory_type ?? "raw";
|
|
4275
|
+
if (_pendingRecords.some(
|
|
4276
|
+
(r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
|
|
4277
|
+
)) {
|
|
4074
4278
|
return;
|
|
4075
4279
|
}
|
|
4076
4280
|
try {
|
|
4077
|
-
const
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4281
|
+
const existing = await findScopedDuplicate({
|
|
4282
|
+
contentHash,
|
|
4283
|
+
agentId: record.agent_id,
|
|
4284
|
+
projectName: record.project_name,
|
|
4285
|
+
memoryType
|
|
4081
4286
|
});
|
|
4082
|
-
if (existing
|
|
4287
|
+
if (existing) return;
|
|
4083
4288
|
} catch {
|
|
4084
4289
|
}
|
|
4085
4290
|
const dbRow = {
|
|
@@ -4110,7 +4315,7 @@ async function writeMemory(record) {
|
|
|
4110
4315
|
tier: record.tier ?? classifyTier(record),
|
|
4111
4316
|
supersedes_id: record.supersedes_id ?? null,
|
|
4112
4317
|
draft: record.draft ? 1 : 0,
|
|
4113
|
-
memory_type:
|
|
4318
|
+
memory_type: memoryType,
|
|
4114
4319
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
4115
4320
|
content_hash: contentHash,
|
|
4116
4321
|
intent: record.intent ?? null,
|
|
@@ -4269,6 +4474,7 @@ async function flushBatch() {
|
|
|
4269
4474
|
const globalClient = getClient();
|
|
4270
4475
|
const globalStmts = batch.map(buildStmt);
|
|
4271
4476
|
await globalClient.batch(globalStmts, "write");
|
|
4477
|
+
schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
|
|
4272
4478
|
_pendingRecords.splice(0, batch.length);
|
|
4273
4479
|
try {
|
|
4274
4480
|
const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -4527,6 +4733,7 @@ var init_store = __esm({
|
|
|
4527
4733
|
init_keychain();
|
|
4528
4734
|
init_config();
|
|
4529
4735
|
init_state_bus();
|
|
4736
|
+
init_memory_write_governor();
|
|
4530
4737
|
INIT_MAX_RETRIES = 3;
|
|
4531
4738
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
4532
4739
|
_pendingRecords = [];
|
|
@@ -9001,7 +9208,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
9001
9208
|
}
|
|
9002
9209
|
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
9003
9210
|
if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
|
|
9004
|
-
|
|
9211
|
+
let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
9212
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
9213
|
+
ccModel += "[1m]";
|
|
9214
|
+
}
|
|
9215
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
|
|
9005
9216
|
}
|
|
9006
9217
|
}
|
|
9007
9218
|
let spawnCommand;
|